Files
arcrun-collector/foldertree_live_test.go
T

117 lines
4.2 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.
package collector
// foldertree_live_test.go — 對**真實的監看資料夾**跑一次樹,用 leo 給的機械判準對帳
//`inkstone/Arcrun#180`leo 2026-08-28
// 「就在桌面,你為什麼沒看?最簡單的方式就像 terminal tree 一樣把被同步資料夾下的
// 所有 folder 都遍歷後印出來」)。
//
// 🔴 為什麼 fixture 不夠(同 sourcerepair_live_test.go 的理由):
// `foldertree_traversal_test.go` 用的是我們自己擺出來的形狀——它證明得了邏輯,
// 證明不了「**leo 桌面上那個資料夾**現在到底展不展得開」。
// 這張票的現象是他螢幕上的數字,對帳就要拿他螢幕上那個資料夾來對。
//
// 只讀磁碟、不碰網路、不寫 manifest,所以沒有「打錯實例」的風險;
// 仍然預設 skip,因為路徑是機器專屬的。
//
// LIVE_TREE_ROOTS="/Users/x/Desktop/youlinhsieh-test1,/Users/x/Desktop/youlinhsieh-test2" \
// go test -run TestLiveFolderTreeMatchesDisk -v ./
//
// 判準(leo 原話):**`find <root> -type d` 數出幾個目錄,雲端的樹就該有幾個節點**
//(收不收是另一回事,`skip_reason` 照標)。
// 例外只有一種且必須說得出口:**真的沒走進去**的那幾類(gitignore/工具產物/
// 建置目錄/worktree/巢狀 repo)——它們仍然佔一個節點,但底下不生子節點,
// 所以本測試把它們的子樹從地端這一側也扣掉,兩邊才是同一個問題的兩個答案。
import (
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"time"
)
// diskDirsExcludingPrunedSubtrees=地端目錄清單,但「真的沒走進去」的那幾類只算它自己。
func diskDirsExcludingPrunedSubtrees(t *testing.T, root string, plan IngestPlan) []string {
t.Helper()
var out []string
err := filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if err != nil || !d.IsDir() {
return err //nolint:nilerr // 讀不到的目錄交給 WalkDir 自己回報
}
if p == root {
out = append(out, "")
return nil
}
if strings.HasPrefix(d.Name(), ".") {
return filepath.SkipDir
}
rel, _ := filepath.Rel(root, p)
rel = filepath.ToSlash(rel)
out = append(out, rel)
if skip, _ := plan.SkipsDirWhy(rel, p); skip {
return filepath.SkipDir // 節點留著(上一行已經 append),子樹不算
}
return nil
})
if err != nil {
t.Fatal(err)
}
sort.Strings(out)
return out
}
func TestLiveFolderTreeMatchesDisk(t *testing.T) {
raw := strings.TrimSpace(os.Getenv("LIVE_TREE_ROOTS"))
if raw == "" {
t.Skip("設 LIVE_TREE_ROOTS=<資料夾,資料夾> 才跑(路徑是機器專屬的)")
}
for _, root := range strings.Split(raw, ",") {
root = strings.TrimSpace(root)
if root == "" {
continue
}
t.Run(filepath.Base(root), func(t *testing.T) {
plan := PlanIngest(root)
m := &Manifest{Entries: map[string]*ManifestEntry{}}
p, err := Scan(root, m, ScanOptions{Plan: plan})
if err != nil {
t.Fatal(err)
}
tree := BuildFolderTree(root, "live", p.DirStats, m.Entries, p.AllExcludedDirs, plan, time.Now())
onTree := map[string]FolderNode{}
for _, n := range tree.Nodes {
onTree[n.Path] = n
}
want := diskDirsExcludingPrunedSubtrees(t, root, plan)
t.Logf("收檔策略:%s — %s", plan.Mode, plan.Reason)
t.Logf("地端目錄 %d(扣掉沒走進去的子樹)/樹上節點 %dtruncated=%v",
len(want), tree.TotalNodes, tree.Truncated)
for _, n := range tree.Nodes {
t.Logf(" %-46s tot=%-4d sync=%-4d excl=%-4d skipped=%v %s",
"'"+n.Path+"'", n.TotalFiles, n.SyncedFiles, n.ExcludedFiles, n.Skipped, n.SkipReason)
}
var missing []string
for _, w := range want {
if _, ok := onTree[w]; !ok {
missing = append(missing, w)
}
}
if len(missing) > 0 {
t.Fatalf("地端有、樹上沒有的資料夾(「不收」又被實作成「不走進去」了):%v", missing)
}
if tree.TotalNodes != len(want) {
t.Fatalf("樹上節點 %d ≠ 地端資料夾 %d", tree.TotalNodes, len(want))
}
if tree.Truncated {
t.Fatalf("被截斷了——MaxFolderTreeNodes=%d 不夠這個資料夾用,不准靜默截斷",
MaxFolderTreeNodes)
}
})
}
}