返回 DeepSeek-Reasonix
named_paths_test.go
根目录 / internal / evidence / named_paths_test.go
1 package evidence
2
3 import (
4 "path/filepath"
5 "slices"
6 "testing"
7 )
8
9 func TestNamedPathsReadsLocationsOutOfDelegationProse(t *testing.T) {
10 text := "Look at `src/parser.go:181-207` and @internal/agent, " +
11 "then compare against src/parser.go. See https://example.com/docs.go, i.e. the CRLF path."
12 got := NamedPaths(text)
13 want := []string{
14 filepath.FromSlash("internal/agent"),
15 filepath.FromSlash("src/parser.go"),
16 }
17 if !slices.Equal(got, want) {
18 t.Fatalf("NamedPaths = %q, want %q", got, want)
19 }
20 }
21
22 // Prose that merely discusses the problem is not a location handed over: if
23 // it were, every blind delegation would report a non-zero hint count.
24 func TestNamedPathsIgnoresOrdinaryProse(t *testing.T) {
25 for _, text := range []string{
26 "Determine the root cause of the failing parser test.",
27 "The offset is wrong, i.e. it double-counts.",
28 "Check the scanner, e.g. its newline handling.",
29 "Run the suite and report what fails.",
30 } {
31 if got := NamedPaths(text); len(got) != 0 {
32 t.Errorf("NamedPaths(%q) = %q, want none", text, got)
33 }
34 }
35 }
36
37 // A delegation writes workspace-relative prose while a receipt records the
38 // absolute path the tool received, so origin cannot be decided by equality.
39 func TestUnderNamedPathMatchesAbsoluteReceiptAgainstRelativeProse(t *testing.T) {
40 named := NamedPaths("start from src/parser.go")
41 abs := filepath.Join(t.TempDir(), "src", "parser.go")
42 if !UnderNamedPath(named, abs) {
43 t.Fatalf("UnderNamedPath(%q, %q) = false, want true", named, abs)
44 }
45 }
46
47 func TestUnderNamedPathCoversFilesBeneathADirectoryHint(t *testing.T) {
48 named := NamedPaths("the bug is somewhere in internal/agent")
49 root := t.TempDir()
50 inside := filepath.Join(root, "internal", "agent", "task.go")
51 outside := filepath.Join(root, "internal", "evidence", "task.go")
52 if !UnderNamedPath(named, inside) {
53 t.Errorf("UnderNamedPath(%q, %q) = false, want true", named, inside)
54 }
55 if UnderNamedPath(named, outside) {
56 t.Errorf("UnderNamedPath(%q, %q) = true, want false", named, outside)
57 }
58 }
59
60 // Substring matching would credit the parent for a file it never named.
61 func TestUnderNamedPathMatchesWholeSegmentsOnly(t *testing.T) {
62 named := NamedPaths("see parser.go")
63 if UnderNamedPath(named, filepath.FromSlash("src/myparser.go")) {
64 t.Fatal("UnderNamedPath matched myparser.go against parser.go")
65 }
66 }
67
68 // Narrowing the search is what delegating costs; naming the file is handing
69 // over the answer. Reported as one number, a parent that said "look in pkg/"
70 // would be indistinguishable from one that said "the bug is in pkg/romeo.go".
71 func TestSplitNamedPathsSeparatesScopeFromNamedFiles(t *testing.T) {
72 scope, files := SplitNamedPaths(NamedPaths("search pkg/ and internal/agent; the bug is in pkg/romeo.py"))
73 wantScope := []string{filepath.FromSlash("internal/agent"), "pkg"}
74 wantFiles := []string{filepath.FromSlash("pkg/romeo.py")}
75 if !slices.Equal(scope, wantScope) {
76 t.Errorf("scope = %q, want %q", scope, wantScope)
77 }
78 if !slices.Equal(files, wantFiles) {
79 t.Errorf("files = %q, want %q", files, wantFiles)
80 }
81 }
82
83 // A child sent to a directory still had to work out which file in it mattered,
84 // so a scope hint must not zero out the credit for finding one.
85 func TestClassifyEvidenceOriginCreditsWorkInsideAScopeHint(t *testing.T) {
86 root := t.TempDir()
87 looked := []string{
88 filepath.Join(root, "pkg", "alpha.py"),
89 filepath.Join(root, "pkg", "romeo.py"),
90 }
91
92 var scoped DelegationAudit
93 scoped.ClassifyEvidenceOrigin("Search pkg/ for the module that gets it wrong.", looked)
94 if scoped.ParentScopeHints != 1 || scoped.ParentNamedFiles != 0 {
95 t.Fatalf("scoped = %+v, want 1 scope hint and no named file", scoped)
96 }
97 if scoped.DiscoveredPaths != 2 {
98 t.Errorf("a scope hint erased the child's credit: %d/2", scoped.DiscoveredPaths)
99 }
100
101 var told DelegationAudit
102 told.ClassifyEvidenceOrigin("The bug is in pkg/romeo.py.", looked)
103 if told.ParentNamedFiles != 1 || told.DiscoveredPaths != 1 {
104 t.Errorf("told = %+v, want 1 named file and 1 of 2 discovered", told)
105 }
106 }
107
108 func TestClassifyEvidenceOriginSplitsBlindFromSeededDelegation(t *testing.T) {
109 root := t.TempDir()
110 looked := []string{
111 filepath.Join(root, "src", "parser.go"),
112 filepath.Join(root, "src", "scanner.go"),
113 filepath.Join(root, "tests", "test_parser.py"),
114 }
115
116 var blind DelegationAudit
117 blind.ClassifyEvidenceOrigin("Diagnose the failing parser test.", looked)
118 if blind.ParentNamedFiles != 0 || blind.DiscoveredPaths != 3 || blind.EvidencePaths != 3 {
119 t.Fatalf("blind = %+v, want 0 named and 3/3 discovered", blind)
120 }
121
122 var seeded DelegationAudit
123 seeded.ClassifyEvidenceOrigin("I suspect src/parser.go double-normalizes CRLF.", looked)
124 if seeded.ParentNamedFiles != 1 {
125 t.Fatalf("seeded.ParentNamedFiles = %d, want 1", seeded.ParentNamedFiles)
126 }
127 if seeded.DiscoveredPaths != 2 || seeded.EvidencePaths != 3 {
128 t.Fatalf("seeded = %+v, want 2 of 3 discovered", seeded)
129 }
130 }
131
132 func TestEvidencePathsCoversReadsAndSkipsFailedReceipts(t *testing.T) {
133 summary := ChildEvidenceSummary{Receipts: []Receipt{
134 {ToolName: "read", Success: true, Read: true, Paths: []string{"src/parser.go"}},
135 {ToolName: "read", Success: true, Read: true, Paths: []string{"src/parser.go"}},
136 {ToolName: "edit", Success: true, Write: true, Mutation: true, Paths: []string{"src/scanner.go"}},
137 {ToolName: "read", Success: false, Read: true, Paths: []string{"src/never.go"}},
138 }}
139 want := []string{"src/parser.go", "src/scanner.go"}
140 if got := summary.EvidencePaths(); !slices.Equal(got, want) {
141 t.Fatalf("EvidencePaths = %q, want %q", got, want)
142 }
143 }
144
144 lines GO