File size: 4,069 Bytes
6d60378
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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/ 不应被允许")
	}
}