Files
arcrun-collector/wikishape_origin_test.go

124 lines
4.8 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 (
"os"
"path/filepath"
"strings"
"testing"
"time"
)
// testOrigin=測試用的三件式(機器/庫/庫內路徑)。
// 內容不重要,重要的是**每個呼叫端都必須交出它**——`inkstone/Arcrun#167` 的教訓是
// 「出處寫的是什麼」不能靠預設值,得由知道自己在哪一台、哪個庫的那一端說出來。
func testOrigin() SourceOrigin {
return SourceOrigin{MachineLabel: "test@Machine", Library: "test-lib", LibraryPath: "測試文件.md"}
}
// wsOriginwikishape 測試用:庫名/機器固定,庫內路徑跟著受測的 relPath 走
//(子資料夾的檔要驗「資料夾沒被弄丟」,所以這一格不能寫死)。
func wsOrigin(relPath string) SourceOrigin {
return SourceOrigin{MachineLabel: "test@Machine", Library: "test-lib", LibraryPath: relPath}
}
// TestSourceOriginNeverEmitsInternalPath 是 `inkstone/Arcrun#167` 的迴歸閘。
//
// leo 2026-08-27 用 n8n 實測:問「原文 wiki 的位置在哪裏?」,AI 照著卡片內文答
// `../小果被AFTEE詐貸.pdf`——**使用者拿著它走不到任何地方**。
// 這裡把那個形狀鎖死:不管三件式缺哪一格,輸出都不准出現 `../`。
func TestSourceOriginNeverEmitsInternalPath(t *testing.T) {
cases := []struct {
name string
origin SourceOrigin
want []string
}{
{
"三件齊全",
SourceOrigin{MachineLabel: "youlinhsieh@Leo-MBA", Library: "youlinhsieh-test1", LibraryPath: "小果被AFTEE詐貸.pdf"},
[]string{"youlinhsieh@Leo-MBA youlinhsieh-test1 小果被AFTEE詐貸.pdf", "`youlinhsieh-test1/小果被AFTEE詐貸.pdf`"},
},
{
// 🔴 票上的驗收條件三:子資料夾裡的檔要定位得到。
// 舊寫法 `../<檔名>` 會把整段資料夾弄丟(實據:inkstoneco 庫的
// system-dev/wiki/trees/2026-08-17-today.md 出處只剩 `../2026-08-17-today.md`)。
"子資料夾不准被弄丟",
SourceOrigin{MachineLabel: "youlinhsieh@Leo-MBA", Library: "inkstoneco", LibraryPath: "system-dev/wiki/trees/2026-08-17-today.md"},
[]string{"inkstoneco system-dev/wiki/trees/2026-08-17-today.md", "`inkstoneco/system-dev/wiki/trees/2026-08-17-today.md`"},
},
{
"機器未知也要誠實,不得退回相對路徑",
SourceOrigin{Library: "kb", LibraryPath: "a/b.md"},
[]string{"機器(未知) kb a/b.md"},
},
{
"庫未知",
SourceOrigin{MachineLabel: "m", LibraryPath: "a/b.md"},
[]string{"m 知識庫(未知) a/b.md", "`a/b.md`"},
},
{
"路徑未知",
SourceOrigin{MachineLabel: "m", Library: "kb"},
[]string{"m kb 庫內路徑(未知)"},
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var b strings.Builder
renderSourceLine(&b, c.origin, "某卡")
got := b.String()
if strings.Contains(got, "../") {
t.Fatalf("出處帶內部相對路徑:\n%s", got)
}
for _, w := range c.want {
if !strings.Contains(got, w) {
t.Fatalf("出處少了「%s」:\n%s", w, got)
}
}
// 人話那行不准變成三元組(`## 關聯` 的解析只收含兩個分隔符的行)。
human := strings.Split(strings.TrimSpace(got), "\n")[1]
if strings.Contains(human, triSep) {
t.Fatalf("人話位置那行含三元組分隔符,會被當成一條邊:%s", human)
}
})
}
}
// TestBuildWikiDocSourceLocatesSubfolderDoc 走完整落地路徑(不是只測 renderer):
// 子資料夾裡的原稿,產出的文件卡與概念卡都要寫得出「庫內完整路徑」。
func TestBuildWikiDocSourceLocatesSubfolderDoc(t *testing.T) {
root := t.TempDir()
rel := "專案/會議/週會.md"
if err := os.MkdirAll(filepath.Join(root, "專案", "會議"), 0o755); err != nil {
t.Fatal(err)
}
src := "# 週會\n\n甲和乙討論了進度。\n"
if err := os.WriteFile(filepath.Join(root, filepath.FromSlash(rel)), []byte(src), 0o644); err != nil {
t.Fatal(err)
}
ex := &DocExtract{
Gloss: "週會記錄", Summary: "甲乙討論進度", Points: []string{"討論了進度"},
Concepts: []WikiConcept{{Name: "進度", Gloss: "專案進度", Summary: "專案進度的討論"}},
}
origin := SourceOrigin{MachineLabel: "youlinhsieh@Leo-MBA", Library: "inkstoneco", LibraryPath: rel}
cards, err := BuildWikiDoc(root, rel, src, ex, origin, time.Date(2026, 8, 27, 0, 0, 0, 0, time.UTC))
if err != nil {
t.Fatal(err)
}
if len(cards) == 0 {
t.Fatal("沒產出卡")
}
for _, c := range cards {
body, rerr := os.ReadFile(filepath.Join(root, filepath.FromSlash(c)))
if rerr != nil {
t.Fatal(rerr)
}
card := string(body)
if strings.Contains(card, "../") {
t.Fatalf("%s 仍帶 `../`\n%s", c, card)
}
if !strings.Contains(card, "inkstoneco 專案/會議/週會.md") {
t.Fatalf("%s 的出處定位不到子資料夾:\n%s", c, card)
}
}
}