108 lines
3.2 KiB
Go
108 lines
3.2 KiB
Go
// cloudcheck_test.go — 每輪固定問雲端的兩件事,一分鐘只真的問一次(inkstone/arcrun-rag#121)。
|
|
package collector
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// init:測試環境預設不快取——既有測試(例如「第一輪查得到版本、第二輪 DNS 抽風」)
|
|
// 是連續兩輪背靠背跑,模擬的是「每一輪都真的問過」。
|
|
// 需要驗證節流本身的測試(下方與 route_backoff_measure_test.go)自行 save/restore 成真值。
|
|
func init() {
|
|
cloudCheckInterval = 0
|
|
}
|
|
|
|
func withCloudCheckInterval(t *testing.T, d time.Duration) {
|
|
t.Helper()
|
|
old := cloudCheckInterval
|
|
cloudCheckInterval = d
|
|
resetCloudChecks()
|
|
t.Cleanup(func() {
|
|
cloudCheckInterval = old
|
|
resetCloudChecks()
|
|
})
|
|
}
|
|
|
|
func withClock(t *testing.T, start time.Time) *atomic.Int64 {
|
|
t.Helper()
|
|
var ns atomic.Int64
|
|
ns.Store(start.UnixNano())
|
|
old := directNow
|
|
directNow = func() time.Time { return time.Unix(0, ns.Load()).UTC() }
|
|
t.Cleanup(func() { directNow = old })
|
|
return &ns
|
|
}
|
|
|
|
func TestCloudVersion_AskedOncePerMinute(t *testing.T) {
|
|
withCloudCheckInterval(t, 60*time.Second)
|
|
t0 := time.Date(2026, 9, 13, 5, 0, 0, 0, time.UTC)
|
|
clock := withClock(t, t0)
|
|
|
|
var calls int
|
|
orig := fetchCloudVersion
|
|
fetchCloudVersion = func(string) (string, bool) { calls++; return "1.4.60", true }
|
|
defer func() { fetchCloudVersion = orig }()
|
|
|
|
u := "https://a.workers.dev"
|
|
for i := 0; i < 12; i++ { // 一分鐘內每 5 秒一輪
|
|
clock.Store(t0.Add(time.Duration(i*5) * time.Second).UnixNano())
|
|
if v, ok := cloudVersionThrottled(u, false); v != "1.4.60" || !ok {
|
|
t.Fatalf("快取回的答案要與真問到的一樣:%q %v", v, ok)
|
|
}
|
|
}
|
|
if calls != 1 {
|
|
t.Fatalf("一分鐘內 12 輪應該只真的問 1 次,got %d", calls)
|
|
}
|
|
clock.Store(t0.Add(60 * time.Second).UnixNano())
|
|
cloudVersionThrottled(u, false)
|
|
if calls != 2 {
|
|
t.Fatalf("滿一分鐘要重問,got %d", calls)
|
|
}
|
|
cloudVersionThrottled(u, true)
|
|
if calls != 3 {
|
|
t.Fatalf("按「立刻同步」的那一輪要照問,got %d", calls)
|
|
}
|
|
cloudVersionThrottled("https://b.workers.dev", false)
|
|
if calls != 4 {
|
|
t.Fatalf("不同知識庫各問各的,got %d", calls)
|
|
}
|
|
}
|
|
|
|
func TestProbeWorkersAI_AskedOncePerMinute(t *testing.T) {
|
|
withCloudCheckInterval(t, 60*time.Second)
|
|
cloudRoutes.reset()
|
|
defer cloudRoutes.reset()
|
|
t0 := time.Date(2026, 9, 13, 5, 0, 0, 0, time.UTC)
|
|
clock := withClock(t, t0)
|
|
|
|
var probes atomic.Int32
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if strings.HasSuffix(r.URL.Path, "/portal/daemon/extract") {
|
|
probes.Add(1)
|
|
w.WriteHeader(http.StatusBadRequest) // 空 text 的探測:路由存在=可用
|
|
}
|
|
}))
|
|
defer srv.Close()
|
|
|
|
cfg := &DirectConfig{CypherURL: srv.URL, APIKey: "k"}
|
|
for i := 0; i < 12; i++ {
|
|
clock.Store(t0.Add(time.Duration(i*5) * time.Second).UnixNano())
|
|
if st := cfg.probeWorkersAI(); !st.Ready {
|
|
t.Fatalf("第 %d 輪:探測結果要沿用「可用」,got %+v", i, st)
|
|
}
|
|
}
|
|
if n := probes.Load(); n != 1 {
|
|
t.Fatalf("一分鐘內 12 輪應該只探測 1 次,got %d", n)
|
|
}
|
|
clock.Store(t0.Add(61 * time.Second).UnixNano())
|
|
cfg.probeWorkersAI()
|
|
if n := probes.Load(); n != 2 {
|
|
t.Fatalf("滿一分鐘要重新探測,got %d", n)
|
|
}
|
|
}
|