package rag import ( "strings" "testing" ) // TestPackGraphWindows_Groups 把多块合并成 ~target 的窗口,窗口数远少于块数。 func TestPackGraphWindows_Groups(t *testing.T) { var chunks []string for i := 0; i < 50; i++ { chunks = append(chunks, strings.Repeat("字", 200)) // 每块 200 字 } // target=4000 → 每窗约 20 块 → ~3 窗(不再是 50 次 LLM 调用)。 windows, truncated := packGraphWindows(chunks, 4000, 60) if truncated { t.Fatal("50×200=1万字不该触发封顶") } if len(windows) >= len(chunks) { t.Fatalf("窗口应远少于块数:windows=%d chunks=%d", len(windows), len(chunks)) } for i, w := range windows { if rl := runeLen(w); rl > 4000+200 { // 容一块的溢出 t.Fatalf("窗口 %d 超目标过多:%d 字", i, rl) } } } // TestPackGraphWindows_Truncate 超大文档封顶窗口数并报 truncated。 func TestPackGraphWindows_Truncate(t *testing.T) { var chunks []string for i := 0; i < 1000; i++ { chunks = append(chunks, strings.Repeat("字", 500)) } windows, truncated := packGraphWindows(chunks, 4000, 60) if !truncated { t.Fatal("50万字应触发封顶 truncated=true") } if len(windows) > 60 { t.Fatalf("窗口数应封顶 ≤60,got %d", len(windows)) } } // TestDedupeTriples 去重 + 去空项。 func TestDedupeTriples(t *testing.T) { in := []Triple{ {S: "李明华", P: "是", O: "总设计师"}, {S: "李明华", P: "是", O: "总设计师"}, // 重复 {S: "星云一号", P: "发射于", O: "2023"}, {S: "", P: "x", O: "y"}, // 空主体 → 丢 {S: "a", P: "", O: "b"}, // 空关系 → 丢 {S: "李明华", P: "领导", O: "团队"}, // 同主体不同关系 → 保留 } out := dedupeTriples(in) if len(out) != 3 { t.Fatalf("应剩 3 条(去 1 重复 + 2 空),got %d: %+v", len(out), out) } } // TestEnvInt env 正整数读取:缺省/0/非法都退 def。 func TestEnvInt(t *testing.T) { if envInt("NO_SUCH_ENV_XZ", 7) != 7 { t.Fatal("缺省应返回 def") } t.Setenv("X_TEST_INT", "12") if envInt("X_TEST_INT", 7) != 12 { t.Fatal("应读 env=12") } t.Setenv("X_TEST_INT", "0") if envInt("X_TEST_INT", 7) != 7 { t.Fatal("非正数应退 def") } t.Setenv("X_TEST_INT", "abc") if envInt("X_TEST_INT", 7) != 7 { t.Fatal("非法应退 def") } } // TestGraphKnobs_EnvConfigurable 图谱封顶/窗口可经 env 调(瓶颈①)。 func TestGraphKnobs_EnvConfigurable(t *testing.T) { t.Setenv("GRAPH_MAX_WINDOWS", "2") if graphMaxWindows() != 2 { t.Fatalf("env 应覆盖封顶为 2,got %d", graphMaxWindows()) } if graphWindowRunes() != 4000 { t.Fatalf("未设时默认窗口应 4000,got %d", graphWindowRunes()) } } // TestGraphChatClient_FallsBackToMain 未配专用图谱模型 → 回退主 chat;配了 → 用专用(瓶颈⑤)。 func TestGraphChatClient_FallsBackToMain(t *testing.T) { e := &Engine{chat: newChatClient("http://main", "k", "m")} if e.graphChatClient() != e.chat { t.Fatal("未配专用模型应回退主 chat") } e.graphChat = newChatClient("http://cheap", "k", "cheap") if e.graphChatClient() != e.graphChat { t.Fatal("配了专用模型应用专用") } } // TestPackGraphWindows_Empty 空输入不 panic。 func TestPackGraphWindows_Empty(t *testing.T) { w, trunc := packGraphWindows(nil, 4000, 60) if len(w) != 0 || trunc { t.Fatalf("空输入应得空窗口、不截断,got %d %v", len(w), trunc) } }