gcli2api / internal /syncer /cred_test.go
a3216's picture
chore: 同步到上游 1.12.0-panel + 凭证同步/独立启动器/保活
6d60378 verified
Raw History Blame Contribute Delete
4.07 kB
package syncer
import (
"testing"
"time"
)
// TestParseCredentialNested 插件 OAuth 的嵌套形态(真实桶里就是这个形状)。
func TestParseCredentialNested(t *testing.T) {
raw := []byte(`{
"account": { "enterpriseId": "", "nickname": "luckfun233", "uid": "0f000ecd-dfcc-4ccc-bd0c-7d3fd4e2b950" },
"auth": {
"accessToken": "at",
"domain": "www.workbuddy.ai",
"expiresAt": 1822477774,
"realm": "global",
"refreshToken": "rt"
}
}`)
c, ok := parseCredential(raw)
if !ok {
t.Fatal("嵌套形态应解析成功")
}
if c.UID != "0f000ecd-dfcc-4ccc-bd0c-7d3fd4e2b950" {
t.Errorf("UID=%q", c.UID)
}
if c.Nickname != "luckfun233" {
t.Errorf("Nickname=%q", c.Nickname)
}
if c.ExpiresAt != 1822477774 {
t.Errorf("ExpiresAt=%d", c.ExpiresAt)
}
if !c.HasRefresh {
t.Error("HasRefresh 应为 true")
}
if c.Realm != "global" {
t.Errorf("Realm=%q want global", c.Realm)
}
}
// TestParseCredentialFlat 手写 / 旧版的扁平形态。
func TestParseCredentialFlat(t *testing.T) {
raw := []byte(`{"accessToken":"at","refreshToken":"rt","expiresAt":1794492000,"domain":"www.codebuddy.cn","uid":"u9","nickname":"13467880565"}`)
c, ok := parseCredential(raw)
if !ok {
t.Fatal("扁平形态应解析成功")
}
if c.UID != "u9" || c.ExpiresAt != 1794492000 || !c.HasRefresh {
t.Errorf("解析结果不对: %+v", c)
}
// 无显式 realm + CN 域 → cn
if c.Realm != "cn" {
t.Errorf("Realm=%q want cn", c.Realm)
}
}
// TestParseCredentialRealmFallback realm 缺省时按 domain 后缀推断,最后回落 cn。
func TestParseCredentialRealmFallback(t *testing.T) {
cases := []struct{ explicit, domain, want string }{
{"global", "www.codebuddy.cn", "global"}, // 显式优先于 domain
{"", "www.workbuddy.ai", "global"},
{"", "workbuddy.ai", "global"},
{"", "www.codebuddy.cn", "cn"},
{"", "", "cn"}, // 老 CN 凭证零回归
}
for _, tc := range cases {
if got := resolveRealm(tc.explicit, tc.domain); got != tc.want {
t.Errorf("resolveRealm(%q,%q)=%q want %q", tc.explicit, tc.domain, got, tc.want)
}
}
}
// TestParseCredentialRejectsJunk 解析不出来必须是 (false),而不是报错或误判——
// 对账会退化成按内容哈希比较,仍然能正确同步。
func TestParseCredentialRejectsJunk(t *testing.T) {
bad := [][]byte{
nil,
[]byte(""),
[]byte("not json"),
[]byte("[]"),
[]byte(`{"account":{"uid":"u1"},"auth":{"refreshToken":"rt"}}`), // 缺 accessToken
[]byte(`{"refreshToken":"rt"}`), // 扁平形态缺 accessToken
[]byte(`{"auth":"not-an-object"}`),
}
for _, raw := range bad {
if _, ok := parseCredential(raw); ok {
t.Errorf("应判为不可用凭证: %q", raw)
}
}
}
// TestParseCredentialFlatEmptyAccessToken 空白 accessToken 同样不算凭证。
func TestParseCredentialFlatEmptyAccessToken(t *testing.T) {
if _, ok := parseCredential([]byte(`{"accessToken":" ","uid":"u1"}`)); ok {
t.Error("空白 accessToken 不应算作凭证")
}
}
// TestAnnotateAuth Integrated:annotateAuth 只在能解析时才写标注。
func TestAnnotateAuth(t *testing.T) {
good := newFileState("auths/a.json", []byte(`{"account":{"uid":"u1","nickname":"n"},"auth":{"accessToken":"at","expiresAt":123,"refreshToken":"rt"}}`), time.Time{}, catAuth)
if !good.IsAuth || good.UID != "u1" || good.ExpiresAt != 123 {
t.Errorf("标注失败: %+v", good)
}
bad := newFileState("auths/b.json", []byte(`{"hello":"world"}`), time.Time{}, catAuth)
if bad.IsAuth {
t.Error("非凭证文件不应被标为 auth")
}
if bad.UID != "" || bad.ExpiresAt != 0 {
t.Errorf("非凭证文件不该有 auth 标注: %+v", bad)
}
}
// TestPathAllowedUsesIncludeData 复检白名单与 include_data 的联动(避免回归)。
func TestPathAllowedUsesIncludeData(t *testing.T) {
if !PathAllowed("data/state.json", []string{"state.json"}) {
t.Error("state.json 在白名单里却未被允许")
}
if PathAllowed("data/state.json", nil) {
t.Error("未开启 include_data 时 data/ 不应被允许")
}
}