返回 DeepSeek-Reasonix
readfile_safety_test.go
根目录 / internal / tool / builtin / readfile_safety_test.go
1 package builtin
2
3 import (
4 "fmt"
5 "strconv"
6 "strings"
7 "testing"
8 )
9
10 func numberedReadLines(t *testing.T, output string) map[int]string {
11 t.Helper()
12 lines := make(map[int]string)
13 for line := range strings.SplitSeq(output, "\n") {
14 before, after, ok := strings.Cut(line, "→")
15 if !ok {
16 continue
17 }
18 n, err := strconv.Atoi(strings.TrimSpace(before))
19 if err != nil {
20 t.Fatalf("parse numbered line %q: %v", line, err)
21 }
22 lines[n] = after
23 }
24 return lines
25 }
26
27 func TestReadFileLocalSafetyPagesExplicitWindowWithoutGaps(t *testing.T) {
28 const totalLines = 2000
29 var source strings.Builder
30 for line := 1; line <= totalLines; line++ {
31 fmt.Fprintf(&source, "line-%04d-%s\r\n", line, strings.Repeat(string(rune('a'+line%26)), 5000))
32 }
33 first, err := (readFile{}).scan(strings.NewReader(source.String()), 0, totalLines)
34 if err != nil {
35 t.Fatal(err)
36 }
37 if len(first) > readFileMaxFormattedBytes {
38 t.Fatalf("first page bytes=%d, limit=%d", len(first), readFileMaxFormattedBytes)
39 }
40 const prefix = "[read_file local safety page; next_offset="
41 start := strings.LastIndex(first, prefix)
42 if start < 0 {
43 t.Fatal("first page did not include the local safety trailer")
44 }
45 fields := strings.Fields(strings.TrimSuffix(first[start+len(prefix):], "]\n"))
46 if len(fields) != 2 || !strings.HasPrefix(fields[1], "requested_end=") {
47 t.Fatalf("safety trailer fields=%q", fields)
48 }
49 next, err := strconv.Atoi(fields[0])
50 if err != nil || next <= 0 || next >= totalLines {
51 t.Fatalf("next_offset=%d err=%v", next, err)
52 }
53 requestedEnd, err := strconv.Atoi(strings.TrimPrefix(fields[1], "requested_end="))
54 if err != nil || requestedEnd != totalLines {
55 t.Fatalf("requested_end=%d err=%v", requestedEnd, err)
56 }
57 second, err := (readFile{}).scan(strings.NewReader(source.String()), next, requestedEnd-next)
58 if err != nil {
59 t.Fatal(err)
60 }
61 combined := numberedReadLines(t, first)
62 for line, text := range numberedReadLines(t, second) {
63 if _, duplicate := combined[line]; duplicate {
64 t.Fatalf("duplicate line %d", line)
65 }
66 combined[line] = text
67 }
68 if len(combined) != totalLines {
69 t.Fatalf("reconstructed lines=%d, want %d", len(combined), totalLines)
70 }
71 for line := 1; line <= totalLines; line++ {
72 if !strings.HasPrefix(combined[line], fmt.Sprintf("line-%04d-", line)) || strings.Contains(combined[line], "\r") {
73 t.Fatalf("line %d was missing, reordered, or retained CR: %q", line, combined[line])
74 }
75 }
76 }
77
78 func TestReadFileRejectsLineAboveLocalSafetyLimit(t *testing.T) {
79 _, err := (readFile{}).scan(strings.NewReader(strings.Repeat("x", readFileMaxLineBytes+1)+"\n"), 0, 1)
80 if err == nil || !strings.Contains(err.Error(), "1 MiB local safety limit") {
81 t.Fatalf("oversized line error=%v", err)
82 }
83 }
84
84 lines GO