package modules import ( "testing" ) func TestNewRegistry_DefaultState(t *testing.T) { r := NewRegistry() // Required modules are always enabled. for _, id := range []string{Core, Schemas, Storage} { if !r.IsEnabled(id) { t.Errorf("required module %q should be enabled by default", id) } } // Optional modules with DefaultEnabled=true. for _, id := range []string{Auth, Projects, Audit, FreeCAD} { if !r.IsEnabled(id) { t.Errorf("module %q should be enabled by default", id) } } // Optional modules with DefaultEnabled=false. for _, id := range []string{Odoo, Jobs, DAG} { if r.IsEnabled(id) { t.Errorf("module %q should be disabled by default", id) } } } func TestSetEnabled_BasicToggle(t *testing.T) { r := NewRegistry() // Disable an optional module with no dependents. if err := r.SetEnabled(Projects, false); err != nil { t.Fatalf("disabling projects: %v", err) } if r.IsEnabled(Projects) { t.Error("projects should be disabled after SetEnabled(false)") } // Re-enable it. if err := r.SetEnabled(Projects, true); err != nil { t.Fatalf("enabling projects: %v", err) } if !r.IsEnabled(Projects) { t.Error("projects should be enabled after SetEnabled(true)") } } func TestCannotDisableRequired(t *testing.T) { r := NewRegistry() for _, id := range []string{Core, Schemas, Storage} { if err := r.SetEnabled(id, false); err == nil { t.Errorf("disabling required module %q should return error", id) } } } func TestDependencyChain_EnableWithoutDep(t *testing.T) { r := NewRegistry() // Jobs depends on Auth. Auth is enabled by default, so enabling jobs works. if err := r.SetEnabled(Jobs, true); err != nil { t.Fatalf("enabling jobs (auth enabled): %v", err) } // DAG depends on Jobs. Jobs is now enabled, so enabling dag works. if err := r.SetEnabled(DAG, true); err != nil { t.Fatalf("enabling dag (jobs enabled): %v", err) } // Now try with deps disabled. Start fresh. r2 := NewRegistry() // DAG depends on Jobs, which is disabled by default. if err := r2.SetEnabled(DAG, true); err == nil { t.Error("enabling dag without jobs should fail") } } func TestDisableDependedOn(t *testing.T) { r := NewRegistry() // Enable the full chain: auth (already on) → jobs → dag. if err := r.SetEnabled(Jobs, true); err != nil { t.Fatal(err) } if err := r.SetEnabled(DAG, true); err != nil { t.Fatal(err) } // Cannot disable jobs while dag depends on it. if err := r.SetEnabled(Jobs, false); err == nil { t.Error("disabling jobs while dag is enabled should fail") } // Disable dag first, then jobs should work. if err := r.SetEnabled(DAG, false); err != nil { t.Fatal(err) } if err := r.SetEnabled(Jobs, false); err != nil { t.Fatalf("disabling jobs after dag disabled: %v", err) } } func TestCannotDisableAuthWhileJobsEnabled(t *testing.T) { r := NewRegistry() if err := r.SetEnabled(Jobs, true); err != nil { t.Fatal(err) } // Auth is depended on by jobs. if err := r.SetEnabled(Auth, false); err == nil { t.Error("disabling auth while jobs is enabled should fail") } } func TestUnknownModule(t *testing.T) { r := NewRegistry() if r.IsEnabled("nonexistent") { t.Error("unknown module should not be enabled") } if err := r.SetEnabled("nonexistent", true); err == nil { t.Error("setting unknown module should return error") } if r.Get("nonexistent") != nil { t.Error("getting unknown module should return nil") } } func TestAll_ReturnsAllModules(t *testing.T) { r := NewRegistry() all := r.All() if len(all) != 12 { t.Errorf("expected 12 modules, got %d", len(all)) } // Should be sorted by ID. for i := 1; i < len(all); i++ { if all[i].ID < all[i-1].ID { t.Errorf("modules not sorted: %s before %s", all[i-1].ID, all[i].ID) } } } func TestValidateDependencies(t *testing.T) { r := NewRegistry() // Default state should be valid. if err := r.ValidateDependencies(); err != nil { t.Fatalf("default state should be valid: %v", err) } // Force an invalid state by directly mutating (bypassing SetEnabled). r.mu.Lock() r.modules[Jobs].enabled = true r.modules[Auth].enabled = false r.mu.Unlock() if err := r.ValidateDependencies(); err == nil { t.Error("should detect jobs enabled without auth") } }