Files
arcrun-collector/direct_extract_test.go
T

111 lines
4.1 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.
// direct_extract_test.go — task 6extractor 模式端到端(本地萃卡→POST rag_ingest_card)。
package main
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
)
// 完整鏈(claude stub 版):丟原稿 → 萃卡落地本地 → 只有「卡片」被 POST 到 rag_ingest_card
// → 原文從未離開本機 → manifest 標 ingested(下一輪不重送)。
func TestDirectExtractorModeE2E(t *testing.T) {
root := t.TempDir()
if err := os.WriteFile(filepath.Join(root, "報銷規則.md"), []byte("# 原稿機密內容 XYZZY"), 0o644); err != nil {
t.Fatal(err)
}
// 假 cypher:收 rag_ingest_card、驗 payload、記帳
var posted []map[string]any
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.HasSuffix(r.URL.Path, "/webhooks/named/demo/rag_ingest_card/trigger") {
t.Errorf("打錯端點:%s", r.URL.Path)
}
body, _ := io.ReadAll(r.Body)
var m map[string]any
_ = json.Unmarshal(body, &m)
posted = append(posted, m)
_ = json.NewEncoder(w).Encode(map[string]any{"success": true})
}))
defer srv.Close()
// stub claude:把原稿萃成卡(模擬 /wiki-capture 行為)
stubDir := t.TempDir()
stub := filepath.Join(stubDir, "claude")
script := "#!/bin/sh\nmkdir -p system-dev/wiki/cards\nprintf '# 報銷規則\\n## 一句話定義\\n測試卡\\n## 關聯\\n- 報銷規則 >> 屬於 >> 財務\\n' > 'system-dev/wiki/cards/報銷規則.md'\n"
if err := os.WriteFile(stub, []byte(script), 0o755); err != nil {
t.Fatal(err)
}
cfg := &DirectConfig{
WatchFolders: []string{root},
Manifest: filepath.Join(t.TempDir(), "m.json"),
CypherURL: srv.URL, Namespace: "demo", APIKey: "demo",
Library: "kb", Extractor: "claude", ClaudeBin: stub,
CardIngestWF: "rag_ingest_card", MaxRemoved: DefaultMaxRemovedRatio,
}
results, exit, _ := RunDirectOnce(cfg, false)
if exit != 0 {
t.Fatalf("exit=%d results=%+v", exit, results)
}
if len(results) != 1 || results[0].Status != "ingested" {
t.Fatalf("results=%+v", results)
}
// 卡片落地本地(用戶看得到自己的 wiki)
if _, err := os.Stat(filepath.Join(root, "system-dev", "wiki", "cards", "報銷規則.md")); err != nil {
t.Fatalf("卡片未落地:%v", err)
}
// 上雲的是卡片、不是原文
if len(posted) != 1 {
t.Fatalf("應恰好 POST 一張卡,got %d", len(posted))
}
cc, _ := posted[0]["card_content"].(string)
if !strings.Contains(cc, "## 一句話定義") {
t.Fatalf("card_content 不是卡片:%.80s", cc)
}
if strings.Contains(cc, "XYZZY") {
t.Fatal("原文內容洩上雲=違反四步定稿邊界")
}
if p, _ := posted[0]["path"].(string); p != "system-dev/wiki/cards/報銷規則.md" {
t.Fatalf("path=%q", p)
}
// 第二輪:原稿沒變 → 不重萃不重送
results2, exit2, _ := RunDirectOnce(cfg, false)
if exit2 != 0 || len(results2) != 0 || len(posted) != 1 {
t.Fatalf("第二輪應零事件:results=%+v posted=%d", results2, len(posted))
}
}
// 萃取失敗=該檔標 failed、exit=1、manifest 不標(下輪重試),其他檔不受影響。
func TestDirectExtractorFailKeepsRetry(t *testing.T) {
root := t.TempDir()
if err := os.WriteFile(filepath.Join(root, "a.md"), []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
stubDir := t.TempDir()
stub := filepath.Join(stubDir, "claude")
if err := os.WriteFile(stub, []byte("#!/bin/sh\nexit 3\n"), 0o755); err != nil {
t.Fatal(err)
}
cfg := &DirectConfig{
WatchFolders: []string{root},
Manifest: filepath.Join(t.TempDir(), "m.json"),
CypherURL: "https://x.example", Namespace: "demo", APIKey: "demo",
Extractor: "claude", ClaudeBin: stub, MaxRemoved: DefaultMaxRemovedRatio,
}
results, exit, _ := RunDirectOnce(cfg, false)
if exit != 1 || len(results) != 1 || results[0].Status != "failed" {
t.Fatalf("exit=%d results=%+v", exit, results)
}
// 再跑一輪:仍是同一個事件(manifest 沒標 ingested=會重試)
results2, _, _ := RunDirectOnce(cfg, false)
if len(results2) != 1 {
t.Fatalf("失敗檔應重試:%+v", results2)
}
}