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("同實例不應清空資料夾清單") } }