fix(gateway): 收敛 sdk.Channel 与 adapter.PlatformAdapter 接口漂移,/admin/channels 恢复枚举
根因:sdk.Channel.Connect 为 2 参 (ctx, config any),adapter.PlatformAdapter.Connect 为 1 参 (ctx),Go 无重载 → 4 个渠道(qq-obv11/weixin-ilink/telegram/webhook)只实现 1 参 Connect,类型断言 cap.Value.(sdk.Channel) 恒失败 → /api/v1/admin/channels 恒返回 []。 收敛方向:sdk.Channel.Connect 改 1 参 (ctx),与 PlatformAdapter 生命周期子集一致; 4 个真实适配器自动满足新接口,adminListChannels 枚举成功。最小改动(不改渠道实现、 不引依赖)。同步修正 sdk_test/registry_test/extra_test 的 mock。
This commit is contained in:
1 parent
77f75b0d99
commit
5dae2b9741
4 files changed
+31
-20
No files matched your search
@@ -32,7 +32,7 @@ func (s regSkill) Guardrails() []sdk.Guardrail { return nil }
|
||||
type regChannel struct{ name string }
|
||||
|
||||
func (c regChannel) Name() string { return c.name }
|
||||
func (c regChannel) Connect(_ context.Context, _ any) error { return nil }
|
||||
func (c regChannel) Connect(_ context.Context) error { return nil }
|
||||
func (c regChannel) Disconnect(_ context.Context) error { return nil }
|
||||
func (c regChannel) IsConnected() bool { return true }
|
||||
func (c regChannel) HealthCheck(_ context.Context) error { return nil }
|
||||
|
||||
@@ -21,7 +21,9 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/session"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/config"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/iot"
|
||||
personareg "git.yeij.top/AskaEth/Cyrene_v2/internal/persona/registry"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/loop"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model"
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
|
||||
)
|
||||
@@ -165,20 +167,22 @@ type stubSessions struct {
|
||||
|
||||
func (s *stubSessions) List(ctx context.Context) ([]session.Session, error) { return s.list, s.err }
|
||||
|
||||
// stubPersonas mimics PersonaStore.
|
||||
// stubPersonas mimics PersonaStore, returning domain characters that the gateway
|
||||
// maps to PersonaSummary.
|
||||
type stubPersonas struct {
|
||||
list []PersonaSummary
|
||||
list []personareg.Character
|
||||
}
|
||||
|
||||
func (s *stubPersonas) List() []PersonaSummary { return s.list }
|
||||
func (s *stubPersonas) List() []personareg.Character { return s.list }
|
||||
|
||||
// stubMemories mimics MemoryStore.
|
||||
// stubMemories mimics MemoryStore, returning domain memory items that the gateway
|
||||
// maps to MemorySummary.
|
||||
type stubMemories struct {
|
||||
list []MemorySummary
|
||||
list []memory.MemoryItem
|
||||
err error
|
||||
}
|
||||
|
||||
func (s *stubMemories) List(ctx context.Context) ([]MemorySummary, error) { return s.list, s.err }
|
||||
func (s *stubMemories) List(ctx context.Context) ([]memory.MemoryItem, error) { return s.list, s.err }
|
||||
|
||||
// stubIoT mimics IoTStore.
|
||||
type stubIoT struct {
|
||||
@@ -192,7 +196,7 @@ func (s *stubIoT) List(ctx context.Context) ([]iot.Device, error) { return s.lis
|
||||
type stubChannel struct{}
|
||||
|
||||
func (stubChannel) Name() string { return "qq" }
|
||||
func (stubChannel) Connect(ctx context.Context, c any) error { return nil }
|
||||
func (stubChannel) Connect(ctx context.Context) error { return nil }
|
||||
func (stubChannel) Disconnect(ctx context.Context) error { return nil }
|
||||
func (stubChannel) IsConnected() bool { return true }
|
||||
func (stubChannel) HealthCheck(ctx context.Context) error { return nil }
|
||||
@@ -365,7 +369,9 @@ func TestListSessionsAdminError(t *testing.T) {
|
||||
|
||||
func TestListPersonasNonEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
deps := Deps{Personas: &stubPersonas{list: []PersonaSummary{{ID: "p1", Name: "苏梦", Mode: "chat"}}}}
|
||||
deps := Deps{Personas: &stubPersonas{list: []personareg.Character{{
|
||||
ID: "p1", Name: "苏梦", PersonaID: "p1", Default: true, Modes: []string{"chat"},
|
||||
}}}}
|
||||
srv, ts, jwt := buildEnv(t, cfgDefault(), deps)
|
||||
_ = srv
|
||||
tok := signAny(t, jwt, "acct", auth.RoleOwner)
|
||||
@@ -375,21 +381,25 @@ func TestListPersonasNonEmpty(t *testing.T) {
|
||||
t.Fatalf("%s status = %d, want 200 (body=%s)", path, resp.StatusCode, body)
|
||||
}
|
||||
var items []struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
IsDefault bool `json:"isDefault"`
|
||||
Mode string `json:"mode"`
|
||||
}
|
||||
if err := json.Unmarshal(decodeBody(t, body)["data"], &items); err != nil {
|
||||
t.Fatalf("decode %s: %v", path, err)
|
||||
}
|
||||
if len(items) != 1 || items[0].Name != "苏梦" {
|
||||
t.Fatalf("%s items = %+v, want single 苏梦", path, items)
|
||||
if len(items) != 1 || items[0].Name != "苏梦" || items[0].ID != "p1" || !items[0].IsDefault || items[0].Mode != "chat" {
|
||||
t.Fatalf("%s items = %+v, want single 苏梦/p1/default/chat", path, items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminMemoriesNonEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
deps := Deps{Memories: &stubMemories{list: []MemorySummary{{ID: "m1", Scope: "project", Text: "note"}}}}
|
||||
deps := Deps{Memories: &stubMemories{list: []memory.MemoryItem{{
|
||||
ID: "m1", PersonaID: "persona.a", Scope: memory.ScopeProject, Content: "note",
|
||||
}}}}
|
||||
srv, ts, jwt := buildEnv(t, cfgDefault(), deps)
|
||||
_ = srv
|
||||
tok := signAny(t, jwt, "acct", auth.RoleOwner)
|
||||
@@ -400,12 +410,13 @@ func TestAdminMemoriesNonEmpty(t *testing.T) {
|
||||
var items []struct {
|
||||
ID string `json:"id"`
|
||||
Scope string `json:"scope"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
if err := json.Unmarshal(decodeBody(t, body)["data"], &items); err != nil {
|
||||
t.Fatalf("decode: %v", err)
|
||||
}
|
||||
if len(items) != 1 || items[0].Scope != "project" {
|
||||
t.Fatalf("items = %+v, want single project", items)
|
||||
if len(items) != 1 || items[0].Scope != "project" || items[0].Text != "note" {
|
||||
t.Fatalf("items = %+v, want single project/note", items)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,9 @@ import "context"
|
||||
type Channel interface {
|
||||
// Name returns the platform identifier (for example "qq", "telegram").
|
||||
Name() string
|
||||
// Connect establishes the platform connection using config. It may be
|
||||
// called more than once and must be idempotent.
|
||||
Connect(ctx context.Context, config any) error
|
||||
// Connect establishes the platform connection. It may be called more than
|
||||
// once and must be idempotent.
|
||||
Connect(ctx context.Context) error
|
||||
// Disconnect tears the connection down gracefully. It returns nil when the
|
||||
// adapter was already disconnected.
|
||||
Disconnect(ctx context.Context) error
|
||||
|
||||
@@ -31,7 +31,7 @@ func (s stubSkill) Guardrails() []Guardrail {
|
||||
type stubChannel struct{ name string }
|
||||
|
||||
func (s stubChannel) Name() string { return s.name }
|
||||
func (s stubChannel) Connect(_ context.Context, _ any) error { return nil }
|
||||
func (s stubChannel) Connect(_ context.Context) error { return nil }
|
||||
func (s stubChannel) Disconnect(_ context.Context) error { return nil }
|
||||
func (s stubChannel) IsConnected() bool { return true }
|
||||
func (s stubChannel) HealthCheck(_ context.Context) error { return nil }
|
||||
|
||||
Reference in new issue
Block a user