package plugin import ( "errors" "testing" "github.com/orca/orca/pkg/bus" ) // mockPlugin implements Plugin for testing. type mockPlugin struct { name string version string initFn func(host PluginHost) error closeFn func() error } func (m *mockPlugin) Name() string { return m.name } func (m *mockPlugin) Version() string { return m.version } func (m *mockPlugin) Init(host PluginHost) error { if m.initFn != nil { return m.initFn(host) } return nil } func (m *mockPlugin) Shutdown() error { if m.closeFn != nil { return m.closeFn() } return nil } func TestRegistryNew(t *testing.T) { r := NewRegistry() if r == nil { t.Fatal("NewRegistry() returned nil") } if n := r.Count(); n != 0 { t.Errorf("expected empty registry, got %d plugins", n) } } func TestRegistryRegister(t *testing.T) { r := NewRegistry() p := &mockPlugin{name: "test", version: "1.0.0"} err := r.Register(p) if err != nil { t.Fatalf("Register failed: %v", err) } if n := r.Count(); n != 1 { t.Errorf("expected 1 plugin, got %d", n) } } func TestRegistryRegisterDuplicate(t *testing.T) { r := NewRegistry() p1 := &mockPlugin{name: "test", version: "1.0.0"} p2 := &mockPlugin{name: "test", version: "2.0.0"} r.Register(p1) err := r.Register(p2) if err == nil { t.Error("expected error registering duplicate plugin") } } func TestRegistryGet(t *testing.T) { r := NewRegistry() p := &mockPlugin{name: "test", version: "1.0.0"} r.Register(p) got, ok := r.Get("test") if !ok { t.Fatal("Get returned not found") } if got.Name() != "test" { t.Errorf("expected name 'test', got %q", got.Name()) } } func TestRegistryGetNotFound(t *testing.T) { r := NewRegistry() _, ok := r.Get("nonexistent") if ok { t.Error("expected false for nonexistent plugin") } } func TestRegistryUnregister(t *testing.T) { r := NewRegistry() p := &mockPlugin{name: "test", version: "1.0.0"} r.Register(p) err := r.Unregister("test") if err != nil { t.Fatalf("Unregister failed: %v", err) } if n := r.Count(); n != 0 { t.Errorf("expected 0 plugins, got %d", n) } } func TestRegistryUnregisterNotFound(t *testing.T) { r := NewRegistry() err := r.Unregister("nonexistent") if err == nil { t.Error("expected error unregistering nonexistent plugin") } } func TestRegistryList(t *testing.T) { r := NewRegistry() r.Register(&mockPlugin{name: "a", version: "1.0.0"}) r.Register(&mockPlugin{name: "b", version: "1.0.0"}) r.Register(&mockPlugin{name: "c", version: "1.0.0"}) plugins := r.List() if len(plugins) != 3 { t.Errorf("expected 3 plugins, got %d", len(plugins)) } names := make(map[string]bool) for _, p := range plugins { names[p.Name()] = true } for _, n := range []string{"a", "b", "c"} { if !names[n] { t.Errorf("missing plugin %q in list", n) } } } func TestRegistryState(t *testing.T) { r := NewRegistry() p := &mockPlugin{name: "test", version: "1.0.0"} r.Register(p) if s := r.State("test"); s != StateRegistered { t.Errorf("expected StateRegistered, got %s", s) } r.SetState("test", StateRunning) if s := r.State("test"); s != StateRunning { t.Errorf("expected StateRunning, got %s", s) } } func TestRegistryStateUnknown(t *testing.T) { r := NewRegistry() if s := r.State("nonexistent"); s != StateUnknown { t.Errorf("expected StateUnknown for nonexistent, got %s", s) } } func TestRegistrySetStateNoOp(t *testing.T) { r := NewRegistry() r.SetState("nonexistent", StateRunning) if n := r.Count(); n != 0 { t.Errorf("SetState should not add plugins") } } func TestPluginStateString(t *testing.T) { tests := []struct { state PluginState want string }{ {StateUnknown, "unknown"}, {StateRegistered, "registered"}, {StateInitialized, "initialized"}, {StateRunning, "running"}, {StateStopped, "stopped"}, {StateError, "error"}, {PluginState(99), "unknown"}, } for _, tt := range tests { if got := tt.state.String(); got != tt.want { t.Errorf("PluginState(%d).String() = %q, want %q", tt.state, got, tt.want) } } } func TestRegistryConcurrent(t *testing.T) { r := NewRegistry() done := make(chan struct{}, 2) go func() { for i := 0; i < 100; i++ { r.Register(&mockPlugin{name: "a", version: "1.0.0"}) r.Get("a") r.Unregister("a") } done <- struct{}{} }() go func() { for i := 0; i < 100; i++ { r.Register(&mockPlugin{name: "b", version: "1.0.0"}) r.List() r.State("b") r.Unregister("b") } done <- struct{}{} }() <-done <-done } // mockPluginHost implements PluginHost for testing kernel-level plugin init. type mockPluginHost struct{} func (m *mockPluginHost) Bus() bus.MessageBus { return nil } func (m *mockPluginHost) GetPlugin(name string) (Plugin, bool) { return nil, false } func (m *mockPluginHost) ListPlugins() []Plugin { return nil } func TestPluginInitAndShutdown(t *testing.T) { var initCalled, shutdownCalled bool p := &mockPlugin{ name: "test", version: "1.0.0", initFn: func(host PluginHost) error { initCalled = true if host == nil { return errors.New("host is nil") } return nil }, closeFn: func() error { shutdownCalled = true return nil }, } host := &mockPluginHost{} if err := p.Init(host); err != nil { t.Fatalf("Init failed: %v", err) } if !initCalled { t.Error("Init function was not called") } if err := p.Shutdown(); err != nil { t.Fatalf("Shutdown failed: %v", err) } if !shutdownCalled { t.Error("Shutdown function was not called") } }