You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

292 lines
6.9 KiB
Go

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
}