Files
arcrun-collector/folderindex_test.go
T

165 lines
5.7 KiB
Go
Raw Permalink 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
import (
"strings"
"testing"
)
// ent 造一份最小 manifest entries(只有 path 有意義)。
func ent(paths ...string) map[string]*ManifestEntry {
m := map[string]*ManifestEntry{}
for i, p := range paths {
m[p] = &ManifestEntry{ContentHash: "sha256:x", Mtime: int64(1787000000 + i)}
}
return m
}
// 這份就是 leo 08-19 要 demo 的形狀:4 層巢狀。
var nested = ent(
"README.md",
"01-公司制度/請假規則.md",
"01-公司制度/資訊安全/密碼原則.md",
"02-專案/教育部標案/RFP摘要.md",
"02-專案/教育部標案/會議紀錄/20260801-啟動會議.md",
"02-專案/教育部標案/會議紀錄/20260812-期中檢討.md",
"03-產品/價目表.csv",
)
func TestDirsIncludeIntermediateLevels(t *testing.T) {
got := dirsFromEntries(nested)
want := []string{
"01-公司制度", "01-公司制度/資訊安全",
"02-專案", "02-專案/教育部標案", "02-專案/教育部標案/會議紀錄",
"03-產品",
}
if len(got) != len(want) {
t.Fatalf("目錄數 %d,期望 %d%v", len(got), len(want), got)
}
for i := range want {
if got[i] != want[i] {
t.Fatalf("第 %d 個是 %q,期望 %q", i, got[i], want[i])
}
}
}
// 🔴 本檔的核心宣稱:每個巢狀資料夾各一張卡,而且父子接得起來。
func TestFractalOneCardPerFolderAndChainReachesRoot(t *testing.T) {
cards := BuildFolderCards("/x/demo-test1", nested, "test1")
if len(cards) != 6 {
t.Fatalf("卡數 %d,期望 6(每個子資料夾一張)", len(cards))
}
byPage := map[string]FolderCard{}
for _, c := range cards {
byPage[c.Page] = c
// 每張卡的「## 關聯」恰好一條——這是「不撞 50 subrequests」的結構保證
rel := c.Content[strings.Index(c.Content, "## 關聯"):]
if n := strings.Count(rel, "\n- "); n != 1 {
t.Fatalf("%s 的關聯有 %d 條,必須恰好 1 條", c.Page, n)
}
if !strings.Contains(rel, c.Page+" >> part_of >> "+c.Parent) {
t.Fatalf("%s 的關聯不是指向父層 %s%q", c.Page, c.Parent, rel)
}
}
// 從最深的一格一路往上走,必須走得到根(=總覽卡頁名),且步數正確。
deepest := folderCardPageName("/x/demo-test1", "02-專案/教育部標案/會議紀錄")
cur, hops := byPage[deepest], 0
if cur.Page == "" {
t.Fatalf("找不到最深的那張卡 %q", deepest)
}
for {
hops++
parent, ok := byPage[cur.Parent]
if !ok {
if cur.Parent != inventoryPageName("/x/demo-test1") {
t.Fatalf("鏈斷在 %q,它的父 %q 既不是卡也不是根", cur.Page, cur.Parent)
}
break
}
cur = parent
if hops > 10 {
t.Fatal("往上走超過 10 步,疑似有環")
}
}
if hops != 3 {
t.Fatalf("從第三層走到根走了 %d 步,期望 3", hops)
}
}
// 卡片要講得出「這一層有幾份、含子層幾份」與子資料夾清單——那是給人看的「指向子層」。
func TestCardCountsAndChildrenListing(t *testing.T) {
cards := BuildFolderCards("/x/demo-test1", nested, "test1")
var top FolderCard
for _, c := range cards {
if c.Rel == "02-專案" {
top = c
}
}
if top.Page == "" {
t.Fatal("找不到 02-專案 的卡")
}
if !strings.Contains(top.Content, "本層直接放了 0 份文件,連同子資料夾共 3 份") {
t.Fatalf("份數講錯了:\n%s", top.Content)
}
if !strings.Contains(top.Content, "- 教育部標案(3 份)") {
t.Fatalf("子資料夾清單不對:\n%s", top.Content)
}
}
// t89 同款:slug 會把 "A/B" 與 "A_B" 壓成同一個字串,只靠 slug 會讓兩個資料夾共用一張卡。
func TestPathSlugDoesNotCollapseDifferentPaths(t *testing.T) {
if pathSlug("a/b") == pathSlug("a_b") {
t.Fatal("a/b 與 a_b 的鍵相同 ⇒ 兩個資料夾會共用一張卡(靜默混層)")
}
// 純中文路徑 slug 後為空,必須退成雜湊而不是空字串
z := pathSlug("專案/會議")
if z == "" || strings.HasPrefix(z, "-") {
t.Fatalf("純中文路徑的鍵不合法:%q", z)
}
if pathSlug("專案/會議") == pathSlug("專案/紀錄") {
t.Fatal("兩個不同的純中文路徑得到同一個鍵")
}
}
// 🔴 迴歸守門:上限必須套在「要送的」上,不是套在「全部」上。
// 反過來寫的話,第 201 個之後的資料夾永遠送不上去,而畫面上一切正常。
func TestCapAppliesToPendingNotAll(t *testing.T) {
paths := make([]string, 0, maxFolderCards+20)
for i := 0; i < maxFolderCards+20; i++ {
paths = append(paths, "d"+pad(i)+"/f.md")
}
entries := ent(paths...)
cards := BuildFolderCards("/x/root", entries, "lib")
if len(cards) != maxFolderCards+20 {
t.Fatalf("卡數 %d,期望 %d", len(cards), maxFolderCards+20)
}
m := &Manifest{Entries: entries, FolderCardHashes: map[string]string{}}
// 假裝前 maxFolderCards 個都已經送成功(雜湊相同)
for _, c := range cards[:maxFolderCards] {
m.FolderCardHashes[c.Rel] = hashOf(c.Content)
}
cfg := &DirectConfig{}
res := syncFolderCards(cfg, "/x/root", m, true, true /*dryRun*/, nowFixed())
if len(res) != 20 {
t.Fatalf("這輪排到 %d 張,期望剩下的 20 張——上限若套在全部上,這裡會是 0", len(res))
}
}
func TestIdempotentWhenNothingChanged(t *testing.T) {
m := &Manifest{Entries: nested, FolderCardHashes: map[string]string{}}
cfg := &DirectConfig{}
first := syncFolderCards(cfg, "/x/demo-test1", m, true, true, nowFixed())
if len(first) != 6 {
t.Fatalf("第一輪 %d 張,期望 6", len(first))
}
// dryRun 不寫雜湊,改用真的算一次填進去模擬送成功
for _, c := range BuildFolderCards("/x/demo-test1", nested, "") {
m.FolderCardHashes[c.Rel] = hashOf(c.Content)
}
if second := syncFolderCards(cfg, "/x/demo-test1", m, true, true, nowFixed()); second != nil {
t.Fatalf("內容沒變卻還要送 %d 張", len(second))
}
}