0deef3dbd9
leo 07-28 實測坐實:youlin 時代的資料夾被原封同步進 geek6688 新實例
(「假如我把個人的同步到官方去就很危險,個資外泄」)。
修:instanceChanged(old,new) 比 host;連線精靈成功後若換了實例→清空
WatchFolders/WatchFolder,成功對話框改為說明「因為換了知識庫,為避免把舊資料夾
誤傳到新知識庫,請重新用『+新增知識資料夾…』選擇」;同實例改密碼行為照舊。
測試(main_test.go):host 異同判定含大小寫/尾斜線/scheme 差異。tray go test 綠。
(實作=子 CC;審查+commit=總管。⚠️ manifest 沿用=t86b 另案未含)
186 lines
5.5 KiB
Go
186 lines
5.5 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"strings"
|
||
"testing"
|
||
)
|
||
|
||
// t26:暱稱 > email > 未設定(leo 07-24 拍板:CF 全程隱形,這條邏輯不該提到 cypher/CF)。
|
||
func TestConnectionStatusLabel(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
instanceName string
|
||
email string
|
||
want string
|
||
}{
|
||
{"暱稱優先於email", "我的書房", "leo21c@gmail.com", "連線中:我的書房"},
|
||
{"無暱稱退回email", "", "leo21c@gmail.com", "連線中:leo21c@gmail.com"},
|
||
{"兩者皆空顯示未設定", "", "", "連線中:未設定"},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
got := connectionStatusLabel(c.instanceName, c.email)
|
||
if got != c.want {
|
||
t.Errorf("connectionStatusLabel(%q, %q) = %q, want %q", c.instanceName, c.email, got, c.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
func TestShortCypherHost(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
in string
|
||
want string
|
||
}{
|
||
{"完整URL取host", "https://arcrun-cypher-executor.someacct.workers.dev/path", "arcrun-cypher-executor.someacct.workers.dev"},
|
||
{"空字串回空字串", "", ""},
|
||
{"無法解析出host則原樣回傳", "not-a-url", "not-a-url"},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
got := shortCypherHost(c.in)
|
||
if got != c.want {
|
||
t.Errorf("shortCypherHost(%q) = %q, want %q", c.in, got, c.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// config 序列化含新欄(email/instance_name)——t26 施工面一驗收項。
|
||
func TestDirectConfigJSONRoundTrip(t *testing.T) {
|
||
c := &directConfig{
|
||
CypherURL: "https://example.workers.dev",
|
||
Namespace: "demo",
|
||
Email: "leo21c@gmail.com",
|
||
InstanceName: "我的書房",
|
||
}
|
||
data, err := json.Marshal(c)
|
||
if err != nil {
|
||
t.Fatalf("marshal 失敗:%v", err)
|
||
}
|
||
s := string(data)
|
||
if !strings.Contains(s, `"email":"leo21c@gmail.com"`) {
|
||
t.Errorf("序列化結果缺 email 欄:%s", s)
|
||
}
|
||
if !strings.Contains(s, `"instance_name":"我的書房"`) {
|
||
t.Errorf("序列化結果缺 instance_name 欄:%s", s)
|
||
}
|
||
|
||
var back directConfig
|
||
if err := json.Unmarshal(data, &back); err != nil {
|
||
t.Fatalf("unmarshal 失敗:%v", err)
|
||
}
|
||
if back.Email != c.Email || back.InstanceName != c.InstanceName {
|
||
t.Errorf("round-trip 不一致:got email=%q instance_name=%q", back.Email, back.InstanceName)
|
||
}
|
||
}
|
||
|
||
// 暱稱空時 config JSON 不應寫出 instance_name 欄(omitempty;對齊面二 installer 端「空就不寫欄位」的約定)。
|
||
func TestDirectConfigJSONOmitsEmptyInstanceName(t *testing.T) {
|
||
c := &directConfig{
|
||
CypherURL: "https://example.workers.dev",
|
||
Namespace: "demo",
|
||
Email: "leo21c@gmail.com",
|
||
}
|
||
data, err := json.Marshal(c)
|
||
if err != nil {
|
||
t.Fatalf("marshal 失敗:%v", err)
|
||
}
|
||
if strings.Contains(string(data), "instance_name") {
|
||
t.Errorf("暱稱空時不該序列化出 instance_name 欄:%s", data)
|
||
}
|
||
}
|
||
|
||
// ── t86:instanceChanged ─────────────────────────────────────────────────────
|
||
|
||
func TestInstanceChanged(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
oldURL string
|
||
newURL string
|
||
want bool
|
||
}{
|
||
{
|
||
"同 host 同實例改密碼不觸發清空",
|
||
"https://arcrun-cypher-executor.youlin.workers.dev",
|
||
"https://arcrun-cypher-executor.youlin.workers.dev",
|
||
false,
|
||
},
|
||
{
|
||
"不同 host 換實例觸發清空",
|
||
"https://arcrun-cypher-executor.youlin.workers.dev",
|
||
"https://arcrun-cypher-executor.geek6688.workers.dev",
|
||
true,
|
||
},
|
||
{
|
||
"舊 URL 空(首次設定)不觸發清空",
|
||
"",
|
||
"https://arcrun-cypher-executor.geek6688.workers.dev",
|
||
false,
|
||
},
|
||
{
|
||
"新 URL 空(異常值)不觸發清空",
|
||
"https://arcrun-cypher-executor.youlin.workers.dev",
|
||
"",
|
||
false,
|
||
},
|
||
}
|
||
for _, c := range cases {
|
||
t.Run(c.name, func(t *testing.T) {
|
||
got := instanceChanged(c.oldURL, c.newURL)
|
||
if got != c.want {
|
||
t.Errorf("instanceChanged(%q, %q) = %v, want %v", c.oldURL, c.newURL, got, c.want)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// t86:換實例後 applyRemoteConfig 清空資料夾清單
|
||
func TestApplyRemoteConfigClearsFoldersOnInstanceSwitch(t *testing.T) {
|
||
cfg := &directConfig{
|
||
CypherURL: "https://arcrun-cypher-executor.youlin.workers.dev",
|
||
Namespace: "youlin",
|
||
WatchFolder: "/Users/youlin/KnowledgeBase",
|
||
WatchFolders: []string{"/Users/youlin/KnowledgeBase", "/Users/youlin/Finance"},
|
||
}
|
||
r := &daemonConfigResp{}
|
||
r.Config.CypherURL = "https://arcrun-cypher-executor.geek6688.workers.dev"
|
||
r.Config.Namespace = "geek6688"
|
||
|
||
switched := applyRemoteConfig(cfg, r)
|
||
|
||
if !switched {
|
||
t.Error("換了不同 host 應回傳 switched=true")
|
||
}
|
||
if cfg.WatchFolder != "" {
|
||
t.Errorf("換實例後 WatchFolder 應清空,got %q", cfg.WatchFolder)
|
||
}
|
||
if len(cfg.WatchFolders) != 0 {
|
||
t.Errorf("換實例後 WatchFolders 應清空,got %v", cfg.WatchFolders)
|
||
}
|
||
}
|
||
|
||
// t86:同實例改密碼不清空資料夾清單
|
||
func TestApplyRemoteConfigKeepsFoldersOnSameInstance(t *testing.T) {
|
||
cfg := &directConfig{
|
||
CypherURL: "https://arcrun-cypher-executor.youlin.workers.dev",
|
||
Namespace: "youlin",
|
||
WatchFolder: "/Users/youlin/KnowledgeBase",
|
||
WatchFolders: []string{"/Users/youlin/KnowledgeBase"},
|
||
}
|
||
r := &daemonConfigResp{}
|
||
r.Config.CypherURL = "https://arcrun-cypher-executor.youlin.workers.dev"
|
||
r.Config.Namespace = "youlin"
|
||
|
||
switched := applyRemoteConfig(cfg, r)
|
||
|
||
if switched {
|
||
t.Error("同 host 不應回傳 switched=true")
|
||
}
|
||
if cfg.WatchFolder == "" || len(cfg.WatchFolders) == 0 {
|
||
t.Error("同實例不應清空資料夾清單")
|
||
}
|
||
}
|