77aa4c5195
leo:「無法知道到底同步了沒,可以在 daemon 上加一個立刻同步?」 tray 點擊寫 ~/.arcrun-rag/sync-now+短暫顯示「同步中…」;collector 主迴圈 1 秒輪詢 訊號檔、有就立刻跑一輪刪檔,無則照 PollSec 排程;完成後 status.json 更新自然刷新托盤。 測試:collector+supervisor+tray 三模組全綠(總管親跑)。 (實作=子 CC;驗證+commit=總管)
122 lines
3.6 KiB
Go
122 lines
3.6 KiB
Go
// direct_sync_now_test.go — t98 立刻同步訊號檔邏輯單元測試。
|
||
package main
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"runtime"
|
||
"testing"
|
||
)
|
||
|
||
// TestConsumeSyncNowSignal_NoFile:訊號檔不存在時回 false,不建立任何檔案。
|
||
func TestConsumeSyncNowSignal_NoFile(t *testing.T) {
|
||
dir := t.TempDir()
|
||
path := filepath.Join(dir, "sync-now")
|
||
|
||
got := consumeSyncNowSignal(path)
|
||
if got {
|
||
t.Error("訊號檔不存在時應回 false")
|
||
}
|
||
if _, err := os.Stat(path); err == nil {
|
||
t.Error("consumeSyncNowSignal 不應建立訊號檔")
|
||
}
|
||
}
|
||
|
||
// TestConsumeSyncNowSignal_FileExists:訊號檔存在時回 true 並刪除它。
|
||
func TestConsumeSyncNowSignal_FileExists(t *testing.T) {
|
||
dir := t.TempDir()
|
||
path := filepath.Join(dir, "sync-now")
|
||
|
||
// 建立訊號檔
|
||
if err := os.WriteFile(path, []byte{}, 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
got := consumeSyncNowSignal(path)
|
||
if !got {
|
||
t.Error("訊號檔存在時應回 true")
|
||
}
|
||
// 訊號檔應已被刪除
|
||
if _, err := os.Stat(path); err == nil {
|
||
t.Error("consumeSyncNowSignal 後訊號檔應已被刪除")
|
||
}
|
||
}
|
||
|
||
// TestConsumeSyncNowSignal_Idempotent:consume 後再次呼叫應回 false(訊號只觸發一輪)。
|
||
func TestConsumeSyncNowSignal_Idempotent(t *testing.T) {
|
||
dir := t.TempDir()
|
||
path := filepath.Join(dir, "sync-now")
|
||
|
||
if err := os.WriteFile(path, []byte{}, 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
|
||
first := consumeSyncNowSignal(path)
|
||
second := consumeSyncNowSignal(path)
|
||
|
||
if !first {
|
||
t.Error("第一次 consume 應回 true")
|
||
}
|
||
if second {
|
||
t.Error("第二次 consume(已刪)應回 false,確保每次訊號只觸發一輪")
|
||
}
|
||
}
|
||
|
||
// TestConsumeSyncNowSignal_TriggerRunCounting:訊號檔驅動「跑一輪」計數正確。
|
||
// 用 fake runner 模擬:寫 N 個訊號檔,逐一 consume,驗 runner 呼叫次數。
|
||
// (實際 runDirect 迴圈已在 direct.go 整合;此處只驗 consume 回傳值驅動的呼叫計數)
|
||
func TestConsumeSyncNowSignal_TriggerRunCounting(t *testing.T) {
|
||
if runtime.GOOS == "windows" {
|
||
// WriteFile race on Windows tempdir 偶爾有 flush 時序問題;邏輯已由前三條跨平台測試覆蓋
|
||
t.Skip("此驗計數測試僅跑 Unix")
|
||
}
|
||
|
||
dir := t.TempDir()
|
||
path := filepath.Join(dir, "sync-now")
|
||
|
||
runCount := 0
|
||
fakeRun := func() { runCount++ }
|
||
|
||
// 第 1 輪:有訊號 → 跑
|
||
if err := os.WriteFile(path, []byte{}, 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if consumeSyncNowSignal(path) {
|
||
fakeRun()
|
||
}
|
||
// 第 2 輪:無訊號 → 不跑
|
||
if consumeSyncNowSignal(path) {
|
||
fakeRun()
|
||
}
|
||
// 第 3 輪:再寫訊號 → 跑
|
||
if err := os.WriteFile(path, []byte{}, 0o644); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if consumeSyncNowSignal(path) {
|
||
fakeRun()
|
||
}
|
||
|
||
if runCount != 2 {
|
||
t.Errorf("期望 fakeRun 呼叫 2 次(有訊號輪),got %d", runCount)
|
||
}
|
||
}
|
||
|
||
// TestSyncNowSignalPathConsistency:collector 的 SyncNowSignalPath 與 tray 預期路徑一致。
|
||
// tray 寫入 appDir()/sync-now;collector 的函式從 manifest 路徑推導同目錄的 sync-now——
|
||
// 兩者必須解析到同一個絕對路徑,否則訊號永遠不會被 collector 讀到。
|
||
func TestSyncNowSignalPathConsistency(t *testing.T) {
|
||
dir := t.TempDir()
|
||
manifest := filepath.Join(dir, "manifest.json")
|
||
|
||
// collector 算出的訊號路徑
|
||
collectorPath := SyncNowSignalPath(manifest)
|
||
|
||
// tray 寫的是 appDir()/sync-now;此測試驗「同目錄邏輯」:
|
||
// 若 manifest 在 dir,訊號應在 dir/sync-now
|
||
wantPath := filepath.Join(dir, "sync-now")
|
||
if collectorPath != wantPath {
|
||
t.Errorf("SyncNowSignalPath(%q) = %q, want %q(tray 與 collector 路徑必須一致)",
|
||
manifest, collectorPath, wantPath)
|
||
}
|
||
}
|