mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-27 07:16:19 +09:00
6aebbe9279
Major changes: 1. Core Architecture: Decoupled Bots from Users. Bots now have independent lifecycles, member management (bot_members), and dedicated configurations. 2. Channel Gateway: - Implemented a unified Channel Manager supporting Feishu, Telegram, and Local (Web/CLI) adapters. - Added message processing pipeline to normalize interactions across different platforms. - Introduced a Contact system for identity binding and guest access policies. 3. Database & Tooling: - Consolidated all migrations into 0001_init with updated schema for bots, channels, and contacts. - Optimized sqlc.yaml to automatically track the migrations directory. 4. Agent Enhancements: - Introduced ToolContext to provide Agents with platform-aware execution capabilities (e.g., messaging, contact lookups). - Added tool logging and fallback mechanisms for toolChoice execution. 5. UI & Docs: Updated frontend stores, UI components, and Swagger documentation to align with the new Bot-centric model.
111 lines
3.0 KiB
Go
111 lines
3.0 KiB
Go
package channel
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
)
|
|
|
|
// mockAdapter 专门用于 Manager 路由测试
|
|
type mockAdapter struct {
|
|
sentMessages []OutboundMessage
|
|
}
|
|
|
|
func (m *mockAdapter) Type() ChannelType { return ChannelFeishu }
|
|
func (m *mockAdapter) Start(ctx context.Context, cfg ChannelConfig, handler InboundHandler) (AdapterRunner, error) {
|
|
return AdapterRunner{}, nil
|
|
}
|
|
func (m *mockAdapter) Send(ctx context.Context, cfg ChannelConfig, msg OutboundMessage) error {
|
|
m.sentMessages = append(m.sentMessages, msg)
|
|
return nil
|
|
}
|
|
|
|
type fakeInboundProcessor struct {
|
|
resp *OutboundMessage
|
|
err error
|
|
gotCfg ChannelConfig
|
|
gotMsg InboundMessage
|
|
}
|
|
|
|
func (f *fakeInboundProcessor) HandleInbound(ctx context.Context, cfg ChannelConfig, msg InboundMessage) (*OutboundMessage, error) {
|
|
f.gotCfg = cfg
|
|
f.gotMsg = msg
|
|
return f.resp, f.err
|
|
}
|
|
|
|
func TestManager_HandleInbound_CoreLogic(t *testing.T) {
|
|
logger := slog.Default()
|
|
|
|
t.Run("返回回复_发送成功", func(t *testing.T) {
|
|
processor := &fakeInboundProcessor{
|
|
resp: &OutboundMessage{
|
|
To: "target-id",
|
|
Text: "AI回复内容",
|
|
},
|
|
}
|
|
|
|
m := NewManager(logger, &fakeConfigStore{}, processor)
|
|
adapter := &mockAdapter{}
|
|
m.RegisterAdapter(adapter)
|
|
|
|
cfg := ChannelConfig{ID: "bot-1", BotID: "bot-1", ChannelType: ChannelFeishu}
|
|
msg := InboundMessage{
|
|
Channel: ChannelFeishu,
|
|
Text: "你好",
|
|
ChatID: "chat-1",
|
|
ReplyTo: "target-id",
|
|
}
|
|
|
|
err := m.handleInbound(context.Background(), cfg, msg)
|
|
if err != nil {
|
|
t.Fatalf("不应报错: %v", err)
|
|
}
|
|
|
|
// 验证: 是否正确调用了 Adapter 发送回复
|
|
if len(adapter.sentMessages) != 1 {
|
|
t.Fatalf("应该发送 1 条回复,实际发送: %d", len(adapter.sentMessages))
|
|
}
|
|
if adapter.sentMessages[0].Text != "AI回复内容" {
|
|
t.Errorf("回复内容错误: %s", adapter.sentMessages[0].Text)
|
|
}
|
|
if adapter.sentMessages[0].To != "target-id" {
|
|
t.Errorf("回复目标错误: %s", adapter.sentMessages[0].To)
|
|
}
|
|
})
|
|
|
|
t.Run("无回复_不发送", func(t *testing.T) {
|
|
processor := &fakeInboundProcessor{resp: nil}
|
|
m := NewManager(logger, &fakeConfigStore{}, processor)
|
|
adapter := &mockAdapter{}
|
|
m.RegisterAdapter(adapter)
|
|
|
|
cfg := ChannelConfig{ID: "bot-1", BotID: "bot-1", ChannelType: ChannelFeishu}
|
|
msg := InboundMessage{
|
|
Channel: ChannelFeishu,
|
|
Text: "你好",
|
|
ReplyTo: "target-id",
|
|
}
|
|
|
|
err := m.handleInbound(context.Background(), cfg, msg)
|
|
if err != nil {
|
|
t.Fatalf("不应报错: %v", err)
|
|
}
|
|
|
|
if len(adapter.sentMessages) != 0 {
|
|
t.Errorf("不应发送回复,实际发送: %+v", adapter.sentMessages)
|
|
}
|
|
})
|
|
|
|
t.Run("处理失败_返回错误", func(t *testing.T) {
|
|
processor := &fakeInboundProcessor{err: context.Canceled}
|
|
m := NewManager(logger, &fakeConfigStore{}, processor)
|
|
cfg := ChannelConfig{ID: "bot-1"}
|
|
msg := InboundMessage{Text: " "} // 空格消息
|
|
|
|
err := m.handleInbound(context.Background(), cfg, msg)
|
|
if err == nil {
|
|
t.Errorf("应返回处理错误")
|
|
}
|
|
})
|
|
}
|