Files
arcrun-collector/cloud_version_test.go
T
Leo 4d3a6a09a6 v0.18.9:collector 併進同一支執行檔——磁碟上不再攤出第二支 exe
leo 08-06 裁決:「不要兩支,寫成一支檔案」。

## 為什麼
v0.18.7-8 的「單一 exe」其實是**一支包著另一支**:collector.exe 被 go:embed
進 Arcrun.exe,執行時攤到 ~/.arcrun-rag/bin/ 再跑。
那正是防毒軟體眼中的 dropper 特徵 —— 封測者實撞
`Trojan:Win32/Sabsik.FL.A!ml`,檔案當場被隔離、自動刪除。

⚠️ 誠實界定:`!ml` 結尾=**機器學習判定**,Sabsik 是最常見的通用誤判家族,
   主因是「未簽章+下載次數少」,**不是**特別指向 dropper 行為。
   所以本次改動**不保證**解除誤判——真正的解是上架 MS Store(微軟簽章)。
   但「執行時把第二支 PE 寫到磁碟再執行」本來就該拿掉,這是對的方向且順手變小。

## 怎麼做
- `collector/` 39 個檔 `package main` → `package collector`,`main()` → 匯出的 `Run(args) int`
- 新增 `collector/cmd/collector/`(薄殼 CLI,讓單獨跑 collector 這條路仍可用)
- App 直接 import 該套件;`main()` 第一件事就判 `--collector`,是的話走 `collector.Run` 不碰 GUI
- `supervisor` 加 `ArgPrefix`,App 把 `BinPath` 指向 `os.Executable()` 自己
- 刪掉 `bundled_collector_{windows,other}.go`(embed + 攤檔那套)
- 三支打包腳本不再編/複製第二支;版本注入同時打到兩個 package
- build-win.sh 的機械閘改成**直接問它**:`--collector --version` 回得出版本才放行
  (舊閘是比大小,只能證明「有 embed」,證明不了「分派是對的」)

## 驗(真機實跑)
· `.app/Contents/MacOS/` 只有 **一個** 執行檔(原本兩個)
· 跑起來兩個行程是**同一個 exe**:
    …/MacOS/arcrun-app
    …/MacOS/arcrun-app --collector direct --config …
· `~/.arcrun-rag/bin` **不存在**(沒有任何東西被攤出來)
· 端到端:丟檔進看守資料夾 → collector.log `"status":"ingested","http_status":200`
· `lsappinfo` 仍是 `type="UIElement"`、`Version="0.18.9"`
· collector 39 檔測試全過;app 測試過;go vet 全綠;mac + windows 交叉編譯皆過
· 單檔 26MB → 22MB(不再夾帶第二份完整程式)
2026-08-06 16:00:47 +08:00

73 lines
2.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// cloud_version_test.go — t103 雲端版本判定函式 4 案單元測試。
package collector
import (
"os"
"testing"
)
// TestMain 在所有測試執行前把 fetchCloudVersion 換成 stub(回 ("", false)),
// 避免各測試的假 httptest.Server 或離線環境因真實 /health 呼叫炸掉測試。
// TestCloudVersionStale 直接測 cloudVersionStale 邏輯,不受此 stub 影響。
func TestMain(m *testing.M) {
fetchCloudVersion = func(string) (string, bool) { return "", false }
os.Exit(m.Run())
}
// TestCloudVersionStale 驗證四種判定情境:空版本/過舊/正常/解析失敗。
func TestCloudVersionStale(t *testing.T) {
cases := []struct {
name string
version string
checkOK bool
wantStale bool
}{
{
// 老實例:/health 可達但 bundle_version 為空字串 → 需要更新
name: "空版本(老實例)", version: "", checkOK: true, wantStale: true,
},
{
// 日期早於 minCloudBuilt → 需要更新
name: "過舊日期", version: "2026-07-27+abc1234", checkOK: true, wantStale: true,
},
{
// 日期等於 minCloudBuilt → 正常(剛好達標)
name: "正常(剛好達標)", version: "2026-07-28+abc1234", checkOK: true, wantStale: false,
},
{
// /health 不可達(解析失敗/逾時)→ 靜默不判定
name: "解析失敗(/health 不可達)", version: "", checkOK: false, wantStale: false,
},
// 🔴 2026-08-02 迴歸守衛(leo 實撞:兩帳號都更新到 1.4.1,卻**都**顯示需要更新且消不掉)。
// 病根:舊實作 `head < minCloudBuilt` 是**字串比較**semver 進來後
// "1.4.1" < "2026-07-28" 恆為真 ⇒ 最新版被判過舊。
// 這幾則若變紅,代表版本比較又退回字串比較,或新格式沒被辨識。
{
name: "semver 最新版(1.4.1)不該被判過舊", version: "1.4.1", checkOK: true, wantStale: false,
},
{
name: "semver 剛好達標(1.4.0", version: "1.4.0", checkOK: true, wantStale: false,
},
{
name: "semver 過舊(1.3.9)需要更新", version: "1.3.9", checkOK: true, wantStale: true,
},
{
// 字串比較會誤判成 "1.10.0" < "1.9.0";整數逐段比較才對
name: "semver 1.10.0 比 1.9.0 新(防字串比較)", version: "1.10.0", checkOK: true, wantStale: false,
},
{
// 舊格式仍須正確辨識——兩種格式並存期間,老實例不可被誤判成最新而收不到提示
name: "舊格式過舊仍要判過舊(相容)", version: "2026-07-01+deadbee", checkOK: true, wantStale: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
got := cloudVersionStale(tc.version, tc.checkOK)
if got != tc.wantStale {
t.Errorf("cloudVersionStale(%q, %v) = %vwant %v", tc.version, tc.checkOK, got, tc.wantStale)
}
})
}
}