@@ -0,0 +1,87 @@
1+package tools
2+
3+import (
4+ "fmt"
5+ "os"
6+ "path/filepath"
7+ "strings"
8+ "testing"
9+
10+ "github.com/stretchr/testify/require"
11+)
12+
13+func TestReadTextFileBoundaryCases(t *testing.T) {
14+ t.Parallel()
15+
16+ tmpDir := t.TempDir()
17+ filePath := filepath.Join(tmpDir, "sample.txt")
18+
19+ var allLines []string
20+ for i := range 5 {
21+ allLines = append(allLines, fmt.Sprintf("line %d", i+1))
22+ }
23+ require.NoError(t, os.WriteFile(filePath, []byte(strings.Join(allLines, "\n")), 0o644))
24+
25+ tests := []struct {
26+ name string
27+ offset int
28+ limit int
29+ wantContent string
30+ wantHasMore bool
31+ }{
32+ {
33+ name: "exactly limit lines remaining",
34+ offset: 0,
35+ limit: 5,
36+ wantContent: "line 1\nline 2\nline 3\nline 4\nline 5",
37+ wantHasMore: false,
38+ },
39+ {
40+ name: "limit plus one line remaining",
41+ offset: 0,
42+ limit: 4,
43+ wantContent: "line 1\nline 2\nline 3\nline 4",
44+ wantHasMore: true,
45+ },
46+ {
47+ name: "offset at last line",
48+ offset: 4,
49+ limit: 3,
50+ wantContent: "line 5",
51+ wantHasMore: false,
52+ },
53+ {
54+ name: "offset beyond eof",
55+ offset: 10,
56+ limit: 3,
57+ wantContent: "",
58+ wantHasMore: false,
59+ },
60+ }
61+
62+ for _, tt := range tests {
63+ t.Run(tt.name, func(t *testing.T) {
64+ t.Parallel()
65+
66+ gotContent, gotHasMore, err := readTextFile(filePath, tt.offset, tt.limit)
67+ require.NoError(t, err)
68+ require.Equal(t, tt.wantContent, gotContent)
69+ require.Equal(t, tt.wantHasMore, gotHasMore)
70+ })
71+ }
72+}
73+
74+func TestReadTextFileTruncatesLongLines(t *testing.T) {
75+ t.Parallel()
76+
77+ tmpDir := t.TempDir()
78+ filePath := filepath.Join(tmpDir, "longline.txt")
79+
80+ longLine := strings.Repeat("a", MaxLineLength+10)
81+ require.NoError(t, os.WriteFile(filePath, []byte(longLine), 0o644))
82+
83+ content, hasMore, err := readTextFile(filePath, 0, 1)
84+ require.NoError(t, err)
85+ require.False(t, hasMore)
86+ require.Equal(t, strings.Repeat("a", MaxLineLength)+"...", content)
87+}