Files
Cyrene_v2/plugins/channels/kook/identity_test.go
T
Aska c1bd740f7c
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
fix(kook): KOOK 机器人鉴权用 Authorization: Bot <token> 而非 Bearer
httpclient 硬编码 Bearer,导致 KOOK bot token 被当 OAuth2 用户令牌发,
/ gateway/index 返回 401『登录环境异常』。按 KOOK reference 鉴权规范,
给共享 httpclient 增加可配置 authScheme(默认 Bearer),KOOK 用 Bot。

- httpclient: 新增 WithAuthScheme 选项 + authScheme 字段,do() 按 scheme 组装
- kook/adapter: 装配时传 WithAuthScheme("Bot")
- 同步修正 config.go 注释与 send/identity 测试断言
2026-09-07 16:59:53 +08:00

98 lines
3.7 KiB
Go

package kook
import (
"context"
"net/http"
"testing"
)
// TestFetchBotIdentity exercises the user/me lookup directly: a valid payload
// decodes into the bot account, and the adapter stores it.
func TestFetchBotIdentity(t *testing.T) {
t.Parallel()
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != userMePath {
http.NotFound(w, r)
return
}
if got := r.Header.Get("Authorization"); got != "Bot testtoken" {
t.Errorf("Authorization = %q, want Bot testtoken", got)
}
kookOK(w, map[string]any{"id": "bot-1", "username": "bot", "nickname": "小K"})
}))
bi, err := a.fetchBotIdentity(context.Background())
if err != nil {
t.Fatalf("fetchBotIdentity: %v", err)
}
if bi.ID != "bot-1" || bi.Username != "bot" || bi.Nickname != "小K" {
t.Fatalf("bot identity = %+v, want bot-1/bot/小K", bi)
}
a.setBotIdentity(bi)
if id, username, nickname := a.BotIdentity(); id != "bot-1" || username != "bot" || nickname != "小K" {
t.Errorf("BotIdentity = %q/%q/%q, want bot-1/bot/小K", id, username, nickname)
}
}
// TestFetchBotIdentity_EmptyID confirms that a user/me payload with a blank id is
// a bad response (a caller can distinguish "no account" from "not yet fetched").
func TestFetchBotIdentity_EmptyID(t *testing.T) {
t.Parallel()
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
kookOK(w, map[string]any{"username": "bot"})
}))
if _, err := a.fetchBotIdentity(context.Background()); err == nil {
t.Fatal("expected an error for a user/me payload with no id")
}
}
// TestRefreshBotIdentity_FailureTolerant verifies the best-effort behavior: a
// failed user/me lookup (bad token / network) logs and leaves the identity empty,
// but does not abort the connect.
func TestRefreshBotIdentity_FailureTolerant(t *testing.T) {
t.Parallel()
a, _ := newTestAdapter(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
kookFail(w, 401, "invalid token")
}))
// Must not panic; identity stays empty.
a.refreshBotIdentity(context.Background())
if id, username, nickname := a.BotIdentity(); id != "" || username != "" || nickname != "" {
t.Errorf("BotIdentity after failure = %q/%q/%q, want empty", id, username, nickname)
}
}
// TestBotIdentityConfigSnapshot confirms the resolved bot identity is surfaced
// through Config() (non-sensitive keys) so the admin read path can display it.
func TestBotIdentityConfigSnapshot(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "x"})
a.setBotIdentity(botIdentity{ID: "bot-5", Username: "bot5", Nickname: "Five"})
cfg := a.Config()
if cfg["bot_id"] != "bot-5" || cfg["bot_username"] != "bot5" || cfg["bot_nickname"] != "Five" {
t.Errorf("config bot identity = %v/%v/%v, want bot-5/bot5/Five",
cfg["bot_id"], cfg["bot_username"], cfg["bot_nickname"])
}
}
// TestConfigDefaults_NoRequirement asserts the "token 即用" contract: with only a
// token, base_url/empty receive_mode/webhook_path resolve to sensible defaults and
// no additional field is required to connect.
func TestConfigDefaults_NoRequirement(t *testing.T) {
t.Parallel()
a := New(Config{BotToken: "token"})
if err := a.cfg.Validate(); err != nil {
t.Fatalf("Validate with token only = %v, want nil", err)
}
got := a.Config()
if got["base_url"] != defaultBaseURL {
t.Errorf("base_url = %v, want default %s", got["base_url"], defaultBaseURL)
}
if got["receive_mode"] != modeWS {
t.Errorf("receive_mode = %v, want %q", got["receive_mode"], modeWS)
}
// Webhook mode with an empty WebhookPath uses the default route.
wh := New(Config{BotToken: "t", ReceiveMode: modeWebhook})
if p := wh.WebhookPath(); p != defaultWebhookPath {
t.Errorf("WebhookPath = %q, want %s", p, defaultWebhookPath)
}
}