Files
arcrun-collector/cmd/arcrun-app/freshinstall_e2e_test.go
T
Leo a22d4e4436 端到端驗證:全新安裝真的跑得起同步引擎(自己驗,不推給 leo)
自走警察點破:我把「在 Windows 全新裝一次確認」推給 leo,但這件事我自己驗得了。

新增 `TestFreshInstallCollectorStarts`:
全新 HOME → App 的 saveCfg 存 config → **編出真正的執行檔、用 `--collector direct
--once --dry-run` 把它自己跑起來** → 斷言不是 exit 2、輸出沒有「缺必填欄位」。

為什麼要再加這支(已經有 LoadDirectConfig 那支了):
**「函式回傳 nil」與「行程活得下去」是兩件事**——事故正是死在後者。

反向驗證(拿掉修正)重現 leo 截圖上那句一字不差的訊息:
    collector direct: config 缺必填欄位:manifest
⇒ 這支不是假測試。修正放回後全套綠。

順帶修測試自身的坑:先 `go build` 再改 HOME——先改 HOME 會讓 Go 把**唯讀的**
模組快取寫進暫存目錄,測試結束清不掉(TempDir RemoveAll permission denied)。

剩下真正只有 Windows 能驗的:實機安裝後的行為(托盤/畫面),已列 CP 待辦。
2026-08-06 18:54:14 +08:00

64 lines
2.3 KiB
Go

package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
)
// TestFreshInstallCollectorStarts 端到端釘住 leo 2026-08-06 Windows 事故的完整鏈條:
//
// 全新安裝 → App 存 config → supervisor 用「同一支執行檔 + --collector」拉起同步引擎。
//
// 事故當時這條鏈在最後一步斷掉:config 少了 `manifest` ⇒ `exit status 2` ⇒ 無限重拉。
// 上一支測試只驗到「LoadDirectConfig 讀得下去」;這支**真的把執行檔跑起來**,
// 因為「函式回傳 nil」與「行程活得下去」是兩件事——事故就是死在後者。
//
// 用 --once --dry-run:只走到解析設定就收工,不 POST、不寫 manifest,不動到真資料。
func TestFreshInstallCollectorStarts(t *testing.T) {
// 🔴 先編再改 HOME:把 HOME 指到暫存目錄後才 `go build`,Go 會把**唯讀的**模組快取
// 寫進那個目錄,測試結束清不掉(實撞:TempDir RemoveAll permission denied)。
bin := filepath.Join(t.TempDir(), "arcrun-app-test")
if out, err := exec.Command("go", "build", "-o", bin, ".").CombinedOutput(); err != nil {
t.Fatalf("編不出執行檔:%v\n%s", err, out)
}
home := t.TempDir()
t.Setenv("HOME", home)
t.Setenv("USERPROFILE", home)
cfg := &directConfig{
Accounts: []accountCfg{{
CypherURL: "https://example.workers.dev",
Namespace: "abc123",
APIKey: "abc123",
}},
Extractor: "workers-ai",
}
if err := saveCfg(cfg); err != nil {
t.Fatalf("存檔失敗:%v", err)
}
// 用剛才編好的執行檔自己當 collector 跑(=supervisor 在真機上做的事)
cmd := exec.Command(bin, collectorModeFlag, "direct", "--once", "--dry-run", "--config", configPath())
cmd.Env = append(os.Environ(), "HOME="+home, "USERPROFILE="+home)
out, err := cmd.CombinedOutput()
code := 0
if ee, ok := err.(*exec.ExitError); ok {
code = ee.ExitCode()
} else if err != nil {
t.Fatalf("跑不起來:%v", err)
}
if code == 2 {
t.Fatalf("同步引擎以 exit status 2 結束=設定被拒,就是 leo 撞到的無限重試。\n輸出:%s", out)
}
if strings.Contains(string(out), "缺必填欄位") {
t.Fatalf("設定仍缺必填欄位:%s", out)
}
t.Logf("✅ 全新安裝的設定,同步引擎吃得下去(exit=%d)", code)
}