主人 2026-10-03 拍板「注释一律中文」;gateway 至此整包注释中文化完成(本会话共 3 批: 已推库 16 个文件 + 本批剩余文件)。 校验:机械校验(非注释改动行)= 空;剥注释后代码文本逐行比对全等(不一致文件数 0); 全树 go test ./... 58 包 ok / 0 FAIL;交叉编译绿。
158 lines
5.5 KiB
Go
158 lines
5.5 KiB
Go
package gateway
|
|
|
|
// 演练按平台的 KOOK 适配器面板端点(design/17 §2)。适配器
|
|
// 经由一个内嵌 adapter.PlatformAdapter 的包装器注册 —— 仿照
|
|
// cmd/cyrene 的 namedAdapter —— 因此这些测试也覆盖网关用来
|
|
// 抵达具体 *kook.Adapter 的反射拆包。
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/auth"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/registry"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
|
"git.yeij.top/AskaEth/Cyrene_v2/plugins/channels/kook"
|
|
)
|
|
|
|
func gatewayKookLogger() *slog.Logger {
|
|
return slog.New(slog.NewTextHandler(io.Discard, nil))
|
|
}
|
|
|
|
// gatewayKookWrap 按 cmd/cyrene 的 namedAdapter 的方式内嵌
|
|
// adapter.PlatformAdapter,从而演练网关基于反射的拆包(asKookAdmin)。
|
|
type gatewayKookWrap struct {
|
|
adapter.PlatformAdapter
|
|
name string
|
|
}
|
|
|
|
func (w *gatewayKookWrap) Name() string { return w.name }
|
|
|
|
// gatewayKookServer 应答面板视图会触及的 KOOK REST 表面。
|
|
func gatewayKookServer(t *testing.T) *httptest.Server {
|
|
t.Helper()
|
|
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
switch r.URL.Path {
|
|
case "/friend":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{
|
|
"friend": []any{map[string]any{"id": 1, "friend_info": map[string]any{"id": "u1", "username": "alice"}}},
|
|
}})
|
|
case "/user-chat/list":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{
|
|
"items": []any{map[string]any{"code": "c1", "target_info": map[string]any{"id": "u9", "username": "zed"}}},
|
|
}})
|
|
case "/guild/list":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{
|
|
"items": []any{map[string]any{"id": "g1", "name": "server"}},
|
|
}})
|
|
case "/channel/list":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{
|
|
"items": []any{map[string]any{"id": "c1", "guild_id": "g1", "name": "general", "type": 1}},
|
|
}})
|
|
case "/guild/user-list":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{
|
|
"items": []any{map[string]any{"id": "u1", "username": "alice", "online": true}},
|
|
}})
|
|
case "/friend/handle-request":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{}})
|
|
case "/message/create":
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"code": 0, "data": map[string]any{"msg_id": "m1"}})
|
|
default:
|
|
http.NotFound(w, r)
|
|
}
|
|
})
|
|
srv := httptest.NewServer(h)
|
|
t.Cleanup(srv.Close)
|
|
return srv
|
|
}
|
|
|
|
// newKookGateway 接线一个注册表,其 kook 渠道包装一个指向 httptest
|
|
// KOOK 服务器的真实适配器,并返回 owner JWT。
|
|
func newKookGateway(t *testing.T, name string) (string, string) {
|
|
t.Helper()
|
|
kookSrv := gatewayKookServer(t)
|
|
ka := kook.New(
|
|
kook.Config{BotToken: "testtoken"},
|
|
kook.WithBaseURL(kookSrv.URL),
|
|
kook.WithHTTPClient(kookSrv.Client()),
|
|
kook.WithLogger(gatewayKookLogger()),
|
|
)
|
|
reg := registry.New()
|
|
if err := reg.RegisterChannel(&gatewayKookWrap{PlatformAdapter: ka, name: name}); err != nil {
|
|
t.Fatalf("register channel: %v", err)
|
|
}
|
|
_, ts, jwt := buildEnv(t, cfgDefault(), Deps{Registry: reg})
|
|
tok := signAny(t, jwt, "acct", auth.RoleOwner)
|
|
return ts.URL, tok
|
|
}
|
|
|
|
func TestAdminChannelPlatformView_Kook(t *testing.T) {
|
|
t.Parallel()
|
|
base, tok := newKookGateway(t, "kook:conf0")
|
|
|
|
resp, body := doReq(t, http.MethodGet, base+"/api/v1/admin/channels/kook/view?accountId=conf0", nil, tok)
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("view status = %d, body=%s", resp.StatusCode, body)
|
|
}
|
|
var out struct {
|
|
Data struct {
|
|
Platform string `json:"platform"`
|
|
Friends *struct {
|
|
Friends []struct{ UserID string `json:"user_id"` } `json:"friends"`
|
|
} `json:"friends"`
|
|
Guilds []struct{ ID string `json:"id"` } `json:"guilds"`
|
|
} `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(body, &out); err != nil {
|
|
t.Fatalf("decode view: %v", err)
|
|
}
|
|
if out.Data.Platform != "kook" {
|
|
t.Errorf("platform = %q, want kook", out.Data.Platform)
|
|
}
|
|
if out.Data.Friends == nil || len(out.Data.Friends.Friends) != 1 || out.Data.Friends.Friends[0].UserID != "u1" {
|
|
t.Errorf("friends = %+v, want [u1]", out.Data.Friends)
|
|
}
|
|
if len(out.Data.Guilds) != 1 || out.Data.Guilds[0].ID != "g1" {
|
|
t.Errorf("guilds = %+v, want [g1]", out.Data.Guilds)
|
|
}
|
|
}
|
|
|
|
func TestAdminChannelPlatformView_NonKookNotImplemented(t *testing.T) {
|
|
t.Parallel()
|
|
_, ts, jwt := buildEnv(t, cfgDefault(), Deps{})
|
|
tok := signAny(t, jwt, "acct", auth.RoleOwner)
|
|
|
|
resp, body := doReq(t, http.MethodGet, ts.URL+"/api/v1/admin/channels/telegram/view", nil, tok)
|
|
if resp.StatusCode != http.StatusNotImplemented {
|
|
t.Fatalf("status = %d, want 501 (body=%s)", resp.StatusCode, body)
|
|
}
|
|
}
|
|
|
|
func TestAdminChannelFriendHandle_Kook(t *testing.T) {
|
|
t.Parallel()
|
|
base, tok := newKookGateway(t, "kook:conf0")
|
|
|
|
resp, body := doReq(t, http.MethodPost, base+"/api/v1/admin/channels/kook/friend/handle?accountId=conf0",
|
|
map[string]any{"id": 77, "accept": true}, tok)
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("handle status = %d, body=%s", resp.StatusCode, body)
|
|
}
|
|
var out struct {
|
|
Data struct {
|
|
Handled bool `json:"handled"`
|
|
ID int64 `json:"id"`
|
|
} `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(body, &out); err != nil {
|
|
t.Fatalf("decode handle: %v", err)
|
|
}
|
|
if !out.Data.Handled || out.Data.ID != 77 {
|
|
t.Errorf("handle = %+v, want handled id 77", out.Data)
|
|
}
|
|
}
|