first commit

This commit is contained in:
2026-08-14 23:41:57 +08:00
commit 086803a8dd
471 changed files with 91938 additions and 0 deletions
+195
View File
@@ -0,0 +1,195 @@
package dream
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
)
// canned builds an allowed-set + Plan pair for parser tests from a list of
// absolute paths.
func allowedSet(paths ...string) map[string]struct{} {
m := make(map[string]struct{}, len(paths))
for _, p := range paths {
m[filepath.Clean(p)] = struct{}{}
}
return m
}
func TestBuildConsolidatePromptListsEntriesAndHints(t *testing.T) {
root := "/mem"
a := "/mem/global/user/a.md"
b := "/mem/global/user/b.md"
idx := "/mem/global/MEMORY.md"
plan := Plan{
Files: []MemoryFile{
{Path: a, Scope: "global", Type: "user", Size: 5, Body: "alpha body"},
{Path: b, Scope: "global", Type: "user", Size: 5, Body: "beta body"},
{Path: idx, Scope: "global", Type: "", Size: 3, Body: "- [a](user/a.md)"},
},
NearDupPairs: []NearDupPair{{A: a, B: b, Similarity: 0.82}},
InvalidPathRefs: []InvalidPathRef{{File: a, Ref: "./gone.go"}},
}
eligible := eligibleFiles(plan)
if len(eligible) != 2 {
t.Fatalf("eligibleFiles = %d, want 2 (MEMORY.md excluded)", len(eligible))
}
prompt := buildConsolidatePrompt(ConsolidateInput{Plan: plan, MemoryRoot: root, ProjectDir: ""}, eligible, defaultBodyBudget)
for _, want := range []string{a, b, "alpha body", "beta body", "similarity 0.82", "./gone.go", "global only"} {
if !strings.Contains(prompt, want) {
t.Errorf("prompt missing %q\n---\n%s", want, prompt)
}
}
if strings.Contains(prompt, "MEMORY.md") {
t.Errorf("prompt must not offer the MEMORY.md index as an entry:\n%s", prompt)
}
}
func TestTruncateBody(t *testing.T) {
if got := truncateBody("short", 100); got != "short" {
t.Fatalf("no truncation expected, got %q", got)
}
long := strings.Repeat("x", 50)
got := truncateBody(long, 10)
if !strings.HasPrefix(got, strings.Repeat("x", 10)) || !strings.Contains(got, "truncated") {
t.Fatalf("truncateBody = %q", got)
}
}
func TestParseConsolidateResponseMergeAndPrune(t *testing.T) {
a := "/mem/global/user/a.md"
b := "/mem/global/user/b.md"
c := "/mem/global/user/c.md"
allowed := allowedSet(a, b, c)
raw := "here you go:\n```json\n" + `{
"merges": [{"keep": "` + a + `", "body": "merged body", "remove": ["` + b + `"]}],
"prunes": [{"path": "` + c + `", "reason": "superseded by newer note"}],
"notes": ["did the thing"]
}` + "\n```\n"
res := parseConsolidateResponse(raw, allowed)
if res.Merged != 1 {
t.Errorf("Merged = %d, want 1", res.Merged)
}
if res.Pruned != 1 {
t.Errorf("Pruned = %d, want 1", res.Pruned)
}
if got := res.MergedBodies[a]; got != "merged body" {
t.Errorf("MergedBodies[a] = %q", got)
}
wantDel := map[string]bool{b: true, c: true}
if len(res.Deletions) != 2 {
t.Fatalf("Deletions = %v, want b and c", res.Deletions)
}
for _, d := range res.Deletions {
if !wantDel[d] {
t.Errorf("unexpected deletion %q", d)
}
}
joined := strings.Join(res.Notes, "|")
if !strings.Contains(joined, "superseded by newer note") || !strings.Contains(joined, "did the thing") {
t.Errorf("notes missing content: %v", res.Notes)
}
}
func TestParseConsolidateResponseRejectsUnknownAndUnsafePaths(t *testing.T) {
a := "/mem/global/user/a.md"
allowed := allowedSet(a)
raw := `{
"merges": [{"keep": "/etc/passwd", "body": "x", "remove": ["` + a + `"]}],
"prunes": [{"path": "/mem/global/MEMORY.md", "reason": "index"}]
}`
res := parseConsolidateResponse(raw, allowed)
if res.Merged != 0 || res.Pruned != 0 || len(res.Deletions) != 0 {
t.Fatalf("unsafe/unknown paths must be ignored, got %+v", res)
}
}
func TestParseConsolidateResponseConservativeOnEmptyReasonAndBadJSON(t *testing.T) {
a := "/mem/global/user/a.md"
allowed := allowedSet(a)
// Empty prune reason → KEEP.
res := parseConsolidateResponse(`{"prunes":[{"path":"`+a+`","reason":""}]}`, allowed)
if res.Pruned != 0 || len(res.Deletions) != 0 {
t.Fatalf("empty reason must KEEP, got %+v", res)
}
// Unparseable → empty result with a note, never a deletion.
res = parseConsolidateResponse("the model rambled with no json", allowed)
if res.Merged != 0 || res.Pruned != 0 || len(res.Deletions) != 0 {
t.Fatalf("bad JSON must KEEP everything, got %+v", res)
}
if len(res.Notes) == 0 {
t.Fatal("expected an explanatory note on unparseable response")
}
}
func TestParseConsolidateResponseNoOpMergeIgnored(t *testing.T) {
a := "/mem/global/user/a.md"
allowed := allowedSet(a)
// A merge that removes nothing must not touch the file.
res := parseConsolidateResponse(`{"merges":[{"keep":"`+a+`","body":"rewritten","remove":[]}]}`, allowed)
if len(res.MergedBodies) != 0 || res.Merged != 0 {
t.Fatalf("no-op merge must be ignored, got %+v", res)
}
}
func TestLLMConsolidatorUsesCompleter(t *testing.T) {
root := "/mem"
a := "/mem/global/user/a.md"
b := "/mem/global/user/b.md"
plan := Plan{Files: []MemoryFile{
{Path: a, Scope: "global", Type: "user", Body: "one"},
{Path: b, Scope: "global", Type: "user", Body: "two"},
}}
var gotSystem, gotUser string
c := &llmConsolidator{complete: func(_ context.Context, sys, user string) (string, error) {
gotSystem, gotUser = sys, user
return `{"merges":[{"keep":"` + a + `","body":"merged","remove":["` + b + `"]}]}`, nil
}}
res, err := c.Consolidate(context.Background(), ConsolidateInput{Plan: plan, MemoryRoot: root})
if err != nil {
t.Fatalf("Consolidate: %v", err)
}
if gotSystem != dreamSystemPrompt {
t.Error("system prompt not passed through")
}
if !strings.Contains(gotUser, a) {
t.Error("user prompt missing entry path")
}
if res.Merged != 1 || res.MergedBodies[a] != "merged" {
t.Fatalf("merge not applied: %+v", res)
}
}
func TestLLMConsolidatorPropagatesHardError(t *testing.T) {
plan := Plan{Files: []MemoryFile{{Path: "/mem/global/user/a.md", Scope: "global", Type: "user", Body: "x"}}}
c := &llmConsolidator{complete: func(context.Context, string, string) (string, error) {
return "", errors.New("upstream 500")
}}
if _, err := c.Consolidate(context.Background(), ConsolidateInput{Plan: plan, MemoryRoot: "/mem"}); err == nil {
t.Fatal("expected hard completion error to propagate")
}
}
func TestLLMConsolidatorSkipsWhenNoEligibleFiles(t *testing.T) {
called := false
c := &llmConsolidator{complete: func(context.Context, string, string) (string, error) {
called = true
return "{}", nil
}}
// Only a MEMORY.md index → nothing eligible → no model call.
plan := Plan{Files: []MemoryFile{{Path: "/mem/global/MEMORY.md", Scope: "global", Body: "idx"}}}
if _, err := c.Consolidate(context.Background(), ConsolidateInput{Plan: plan, MemoryRoot: "/mem"}); err != nil {
t.Fatalf("Consolidate: %v", err)
}
if called {
t.Fatal("model should not be called when no eligible files exist")
}
}