package integration import ( "context" "fmt" "net" "sync" "testing" internalplugin "github.com/noahlann/nnet/internal/plugin" "github.com/noahlann/nnet/pkg/config" "github.com/noahlann/nnet/pkg/nnet" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // testPlugin 测试插件实现 type testPlugin struct { name string version string initCalled bool startCalled bool stopCalled bool mu sync.RWMutex } func (p *testPlugin) Name() string { return p.name } func (p *testPlugin) Version() string { return p.version } func (p *testPlugin) Init(cfg *config.Config) error { p.mu.Lock() defer p.mu.Unlock() p.initCalled = true return nil } func (p *testPlugin) Start(ctx context.Context) error { p.mu.Lock() defer p.mu.Unlock() p.startCalled = true return nil } func (p *testPlugin) Stop(ctx context.Context) error { p.mu.Lock() defer p.mu.Unlock() p.stopCalled = true return nil } func (p *testPlugin) IsInitCalled() bool { p.mu.RLock() defer p.mu.RUnlock() return p.initCalled } func (p *testPlugin) IsStartCalled() bool { p.mu.RLock() defer p.mu.RUnlock() return p.startCalled } func (p *testPlugin) IsStopCalled() bool { p.mu.RLock() defer p.mu.RUnlock() return p.stopCalled } // testServerPlugin 测试服务器插件 type testServerPlugin struct { *testPlugin onServerStartCalled bool onServerStopCalled bool mu sync.RWMutex } func (p *testServerPlugin) OnServerStart(ctx context.Context) error { p.mu.Lock() defer p.mu.Unlock() p.onServerStartCalled = true return nil } func (p *testServerPlugin) OnServerStop(ctx context.Context) error { p.mu.Lock() defer p.mu.Unlock() p.onServerStopCalled = true return nil } func (p *testServerPlugin) IsOnServerStartCalled() bool { p.mu.RLock() defer p.mu.RUnlock() return p.onServerStartCalled } func (p *testServerPlugin) IsOnServerStopCalled() bool { p.mu.RLock() defer p.mu.RUnlock() return p.onServerStopCalled } // TestPluginManager 测试插件管理器 func TestPluginManager(t *testing.T) { // 创建插件管理器 manager := internalplugin.NewManager() // 创建测试插件 plugin1 := &testPlugin{ name: "test-plugin-1", version: "1.0.0", } plugin2 := &testPlugin{ name: "test-plugin-2", version: "2.0.0", } // 注册插件 err := manager.Register(plugin1) require.NoError(t, err, "Should register plugin1") err = manager.Register(plugin2) require.NoError(t, err, "Should register plugin2") // 尝试注册同名插件(应该失败) err = manager.Register(plugin1) assert.Error(t, err, "Should fail to register duplicate plugin") // 获取插件 retrievedPlugin, err := manager.Get("test-plugin-1") require.NoError(t, err, "Should get plugin1") require.NotNil(t, retrievedPlugin, "Plugin should not be nil") assert.Equal(t, "test-plugin-1", retrievedPlugin.Name(), "Plugin name should match") // 列出所有插件 plugins := manager.List() assert.Len(t, plugins, 2, "Should have 2 plugins") // 初始化所有插件 cfg := config.DefaultConfig() err = manager.InitAll(cfg) require.NoError(t, err, "Should init all plugins") assert.True(t, plugin1.IsInitCalled(), "Plugin1 should be initialized") assert.True(t, plugin2.IsInitCalled(), "Plugin2 should be initialized") // 启动所有插件 ctx := context.Background() err = manager.StartAll(ctx) require.NoError(t, err, "Should start all plugins") assert.True(t, plugin1.IsStartCalled(), "Plugin1 should be started") assert.True(t, plugin2.IsStartCalled(), "Plugin2 should be started") // 停止所有插件 err = manager.StopAll(ctx) require.NoError(t, err, "Should stop all plugins") assert.True(t, plugin1.IsStopCalled(), "Plugin1 should be stopped") assert.True(t, plugin2.IsStopCalled(), "Plugin2 should be stopped") // 取消注册插件 err = manager.Unregister("test-plugin-1") require.NoError(t, err, "Should unregister plugin1") // 验证插件已取消注册 _, err = manager.Get("test-plugin-1") assert.Error(t, err, "Should fail to get unregistered plugin") } // TestServerPlugin 测试服务器插件 func TestServerPlugin(t *testing.T) { // 创建插件管理器 manager := internalplugin.NewManager() // 创建服务器插件 serverPlugin := &testServerPlugin{ testPlugin: &testPlugin{ name: "server-plugin", version: "1.0.0", }, } // 注册插件 err := manager.Register(serverPlugin) require.NoError(t, err, "Should register server plugin") // 初始化插件 cfg := config.DefaultConfig() err = manager.InitAll(cfg) require.NoError(t, err, "Should init plugin") // 启动插件 ctx := context.Background() err = manager.StartAll(ctx) require.NoError(t, err, "Should start plugin") // 调用OnServerStart err = serverPlugin.OnServerStart(ctx) require.NoError(t, err, "Should call OnServerStart") assert.True(t, serverPlugin.IsOnServerStartCalled(), "OnServerStart should be called") // 调用OnServerStop err = serverPlugin.OnServerStop(ctx) require.NoError(t, err, "Should call OnServerStop") assert.True(t, serverPlugin.IsOnServerStopCalled(), "OnServerStop should be called") // 停止插件 err = manager.StopAll(ctx) require.NoError(t, err, "Should stop plugin") } // TestPluginWithServer 测试插件与服务器集成 func TestPluginWithServer(t *testing.T) { listener, err := net.Listen("tcp", "127.0.0.1:0") require.NoError(t, err) port := listener.Addr().(*net.TCPAddr).Port listener.Close() // 创建插件管理器 manager := internalplugin.NewManager() // 创建消息计数插件 var messageCount int var mu sync.Mutex messagePlugin := &testMessagePlugin{ testPlugin: &testPlugin{ name: "message-counter", version: "1.0.0", }, onMessage: func(data []byte) ([]byte, error) { mu.Lock() messageCount++ mu.Unlock() return data, nil }, } // 注册插件 err = manager.Register(messagePlugin) require.NoError(t, err, "Should register message plugin") cfg := &nnet.Config{ Addr: fmt.Sprintf("tcp://127.0.0.1:%d", port), Codec: &nnet.CodecConfig{ DefaultCodec: "json", }, } ts := StartTestServerWithRoutes(t, cfg, func(srv nnet.Server) { srv.Router().RegisterString("test", func(ctx nnet.Context) error { return ctx.Response().Write(map[string]any{ "status": "ok", }) }) }) defer CleanupTestServer(t, ts) // 初始化并启动插件 initCfg := config.DefaultConfig() err = manager.InitAll(initCfg) require.NoError(t, err, "Should init plugins") ctx := context.Background() err = manager.StartAll(ctx) require.NoError(t, err, "Should start plugins") // 注意:这里只是测试插件管理器的基本功能 // 实际的插件集成需要通过服务器内部实现 _ = messageCount // 清理 manager.StopAll(ctx) } // testMessagePlugin 测试消息插件 type testMessagePlugin struct { *testPlugin onMessage func([]byte) ([]byte, error) } func (p *testMessagePlugin) OnMessage(ctx context.Context, data []byte) ([]byte, error) { if p.onMessage != nil { return p.onMessage(data) } return data, nil }