In-memory registry for 10 modules (3 required, 7 optional). SetEnabled validates dependency chains: cannot enable a module whose dependencies are disabled, cannot disable a module that others depend on. 9 unit tests covering default state, toggling, dependency validation, and error cases. Ref #96
170 lines
4.2 KiB
Go
170 lines
4.2 KiB
Go
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) != 10 {
|
|
t.Errorf("expected 10 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")
|
|
}
|
|
}
|