ai_xiaopei
2 days ago ada154418fb223e742112e58ef169193e588eafb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package index
 
import (
    "os"
    "path/filepath"
    "testing"
)
 
func TestRetryUnresolved(t *testing.T) {
    dir := t.TempDir()
    os.MkdirAll(filepath.Join(dir, "FAQ"), 0755)
    os.WriteFile(filepath.Join(dir, "FAQ", "001-旧文档.md"),
        []byte("---\ntitle: 旧文档\n---\n提到 [[补气失败]]\n"), 0644)
 
    store, _ := Open(filepath.Join(t.TempDir(), "kb.db"))
    defer store.Close()
 
    Reconcile(store, dir)
 
    // 验证悬空链接入表
    var n int
    store.db.QueryRow(`SELECT COUNT(*) FROM unresolved_links WHERE link_text='补气失败'`).Scan(&n)
    if n != 1 {
        t.Fatalf("悬空链接未入表: %d", n)
    }
 
    // 新文档入库:标题正好是 "补气失败"
    os.WriteFile(filepath.Join(dir, "FAQ", "002-补气失败.md"),
        []byte("---\ntitle: 补气失败\n---\n补气失败排查\n"), 0644)
 
    res, err := Reconcile(store, dir)
    if err != nil {
        t.Fatal(err)
    }
    if res.Added != 1 {
        t.Fatalf("新增: %+v", res)
    }
 
    // 悬空链接应被解析:行删除 + 边建立
    store.db.QueryRow(`SELECT COUNT(*) FROM unresolved_links WHERE link_text='补气失败'`).Scan(&n)
    if n != 0 {
        t.Errorf("悬空行未清除: %d", n)
    }
    store.db.QueryRow(`SELECT COUNT(*) FROM edges WHERE relation='wikilink' AND label='补气失败'`).Scan(&n)
    if n != 1 {
        t.Errorf("wikilink 边未建立: %d", n)
    }
    var prov string
    store.db.QueryRow(`SELECT provenance FROM edges WHERE relation='wikilink' AND label='补气失败'`).Scan(&prov)
    if prov != "exact" {
        t.Errorf("补全边应为 exact: %s", prov)
    }
}
 
func TestResolveWikilinkEmptyLink(t *testing.T) {
    dir := t.TempDir()
    os.MkdirAll(filepath.Join(dir, "FAQ"), 0755)
    os.WriteFile(filepath.Join(dir, "FAQ", "001-文档.md"),
        []byte("---\ntitle: 文档\n---\n提到 [[|别名]]\n"), 0644)
 
    store, _ := Open(filepath.Join(t.TempDir(), "kb.db"))
    defer store.Close()
    Reconcile(store, dir)
 
    // 空 link 不应 fuzzy 误建边(Contains 空串恒真)
    var n int
    store.db.QueryRow(`SELECT COUNT(*) FROM edges WHERE relation='wikilink' AND label='|别名'`).Scan(&n)
    if n != 0 {
        t.Errorf("空 link 不应建 wikilink 边: %d", n)
    }
    // 悬空行应入表且只入一行
    store.db.QueryRow(`SELECT COUNT(*) FROM unresolved_links WHERE link_text='|别名'`).Scan(&n)
    if n != 1 {
        t.Errorf("悬空行应为 1: %d", n)
    }
}
 
func TestTagEntityEdgeProvenance(t *testing.T) {
    dir := t.TempDir()
    os.MkdirAll(filepath.Join(dir, "FAQ"), 0755)
    os.WriteFile(filepath.Join(dir, "FAQ", "001-文档.md"),
        []byte("---\ntitle: 文档\ntags: [t1]\nentities: [张三]\n---\n内容\n"), 0644)
 
    store, _ := Open(filepath.Join(t.TempDir(), "kb.db"))
    defer store.Close()
    Reconcile(store, dir)
 
    // tag/entity 边的 provenance 应为 relation 本身(Minor 1 修复)
    var prov string
    store.db.QueryRow(`SELECT provenance FROM edges WHERE relation='tag' AND label='t1'`).Scan(&prov)
    if prov != "tag" {
        t.Errorf("tag 边 provenance: %q", prov)
    }
    store.db.QueryRow(`SELECT provenance FROM edges WHERE relation='entity' AND label='张三'`).Scan(&prov)
    if prov != "entity" {
        t.Errorf("entity 边 provenance: %q", prov)
    }
}
 
// TestGetUnresolvedLinks 覆盖 GetUnresolvedLinks 查询(explore 命令悬空链接提示用)
func TestGetUnresolvedLinks(t *testing.T) {
    dir := t.TempDir()
    os.MkdirAll(filepath.Join(dir, "FAQ"), 0755)
    // 文档 A 有两条悬空链接;文档 B 无
    os.WriteFile(filepath.Join(dir, "FAQ", "001-文档A.md"),
        []byte("---\ntitle: 文档A\n---\n提到 [[悬空甲]] 和 [[悬空乙]]\n"), 0644)
    os.WriteFile(filepath.Join(dir, "FAQ", "002-文档B.md"),
        []byte("---\ntitle: 文档B\n---\n无链接\n"), 0644)
 
    store, _ := Open(filepath.Join(t.TempDir(), "kb.db"))
    defer store.Close()
    Reconcile(store, dir)
 
    links, err := store.GetUnresolvedLinks("FAQ/001-文档A.md")
    if err != nil {
        t.Fatalf("GetUnresolvedLinks failed: %v", err)
    }
    if len(links) != 2 {
        t.Fatalf("文档A 悬空链接 = %d, want 2: %v", len(links), links)
    }
 
    // 无悬空链接的文档返回空(非 nil 错误)
    links, err = store.GetUnresolvedLinks("FAQ/002-文档B.md")
    if err != nil {
        t.Fatalf("GetUnresolvedLinks(文档B) failed: %v", err)
    }
    if len(links) != 0 {
        t.Errorf("文档B 悬空链接 = %d, want 0: %v", len(links), links)
    }
}