779a1b801e
leo 08-08:「在每個知識庫會看到其他需要更新的,在每個知識庫上顯示是否要更新, 如果要,加開啓 install 頁的連結」。小幫手以前只提示自己(daemon 本體)要不要 更新,使用者連著多個雲端知識庫時完全看不出哪一個落後。 - collector/cloud_latest.go:EvalCloudUpdate + FetchLatestCloudRelease(30 分鐘節流), 判準與 portal 版本卡 loadVersion() 同一套(bundle_version vs install.arcrun.dev/ api/latest 的 release,semver 逐段整數比較),不是 t103 minCloudRelease 那把相容 底線,避免同一個知識庫在兩處得到相反答案。 - direct.go/sync_status.go:每輪同步順帶算好每個帳號的 CloudUpdateKnown/ CloudUpdateStale/CloudLatest,寫進 status.json。 - app.go:GetState 把這些欄位接進 UIAccount,供前端讀。 - main.js/style.css:首頁新增「知識庫版本」卡(每庫一行,落後才出現「前往安裝頁 更新」按鈕,帶 email 預填);側邊欄庫名旁加警示點;各庫頁也顯示同一行版本狀態。 查不到版本(連不上/latest 暫時查不到)一律誠實說「查不到」,不當成「已最新」。 已用假 window.go 在瀏覽器實測四種情境(落後/已最新/連不上/查得到 mine 但查不到 latest)+零知識庫的 onboarding 頁+深色模式,畫面與按鈕行為皆符合預期。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
85 lines
3.0 KiB
Go
85 lines
3.0 KiB
Go
// cloud_latest_test.go — t215 單元測試:EvalCloudUpdate 的判準要跟 portal 版本卡一致。
|
||
package collector
|
||
|
||
import "testing"
|
||
|
||
func TestEvalCloudUpdate(t *testing.T) {
|
||
cases := []struct {
|
||
name string
|
||
mine string
|
||
mineOK bool
|
||
latest string
|
||
latestOK bool
|
||
wantKnown bool
|
||
wantUpdate bool
|
||
}{
|
||
{
|
||
name: "兩邊都拿得到、mine 落後", mine: "1.4.1", mineOK: true, latest: "1.4.2", latestOK: true,
|
||
wantKnown: true, wantUpdate: true,
|
||
},
|
||
{
|
||
name: "兩邊都拿得到、已是最新", mine: "1.4.2", mineOK: true, latest: "1.4.2", latestOK: true,
|
||
wantKnown: true, wantUpdate: false,
|
||
},
|
||
{
|
||
// 字串比較會誤判 "1.10.0" < "1.9.0";逐段整數比較才對(同 t103 迴歸守衛)。
|
||
name: "1.10.0 比 1.9.0 新,不該判落後", mine: "1.10.0", mineOK: true, latest: "1.9.0", latestOK: true,
|
||
wantKnown: true, wantUpdate: false,
|
||
},
|
||
{
|
||
// 老格式(YYYY-MM-DD+sha)——portal 版本卡註解原話:「這種情況一律當成落後」。
|
||
name: "老格式版本一律當落後", mine: "2026-07-31+8e83589", mineOK: true, latest: "1.4.2", latestOK: true,
|
||
wantKnown: true, wantUpdate: true,
|
||
},
|
||
{
|
||
name: "連不上這個知識庫(mineOK=false)→ 查不到,不能裝沒事", mine: "", mineOK: false, latest: "1.4.2", latestOK: true,
|
||
wantKnown: false, wantUpdate: false,
|
||
},
|
||
{
|
||
name: "查得到 mine 但暫時查不到最新版 → 查不到,不是已最新", mine: "1.4.2", mineOK: true, latest: "", latestOK: false,
|
||
wantKnown: false, wantUpdate: false,
|
||
},
|
||
{
|
||
name: "/health 可達但 bundle_version 空字串(老實例)→ 查不到", mine: "", mineOK: true, latest: "1.4.2", latestOK: true,
|
||
wantKnown: false, wantUpdate: false,
|
||
},
|
||
}
|
||
for _, tc := range cases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
got := EvalCloudUpdate(tc.mine, tc.mineOK, tc.latest, tc.latestOK)
|
||
if got.Known != tc.wantKnown {
|
||
t.Errorf("Known = %v,want %v", got.Known, tc.wantKnown)
|
||
}
|
||
if got.NeedsUpdate != tc.wantUpdate {
|
||
t.Errorf("NeedsUpdate = %v,want %v", got.NeedsUpdate, tc.wantUpdate)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestFetchLatestCloudReleaseThrottle 驗證節流:窗口內第二次呼叫不重打 fetchLatestCloudReleaseRaw。
|
||
func TestFetchLatestCloudReleaseThrottle(t *testing.T) {
|
||
calls := 0
|
||
orig := fetchLatestCloudReleaseRaw
|
||
defer func() {
|
||
fetchLatestCloudReleaseRaw = orig
|
||
latestMu.Lock()
|
||
latestCached, latestCachedOK, latestFetched = "", false, latestFetched.Add(-2*latestCacheTTL)
|
||
latestMu.Unlock()
|
||
}()
|
||
fetchLatestCloudReleaseRaw = func() (string, bool) { calls++; return "1.4.2", true }
|
||
// 強制第一次一定重打(避開其他測試留下的快取)。
|
||
latestMu.Lock()
|
||
latestFetched = latestFetched.Add(-2 * latestCacheTTL)
|
||
latestMu.Unlock()
|
||
|
||
r1, ok1 := FetchLatestCloudRelease()
|
||
r2, ok2 := FetchLatestCloudRelease()
|
||
if calls != 1 {
|
||
t.Errorf("節流窗口內第二次呼叫不該重打,calls = %d", calls)
|
||
}
|
||
if r1 != "1.4.2" || !ok1 || r2 != "1.4.2" || !ok2 {
|
||
t.Errorf("兩次結果應相同,got (%q,%v) (%q,%v)", r1, ok1, r2, ok2)
|
||
}
|
||
}
|