@@ -3,6 +3,7 @@ import fs from "fs/promises"
33import path from "path"
44import { Environment } from "@opencode/core/environment/index"
55import { AbsolutePath } from "@opencode/core/schema"
6+ import { ReadTool } from "@opencode/core/tool/plugin/read"
67import { ReadToolFileSystem } from "@opencode/core/tool/read-filesystem"
78import { CrossSpawnSpawner } from "@opencode/util/cross-spawn-spawner"
89import { LayerNodePlatform } from "@opencode/util/effect/app-node-platform"
@@ -20,6 +21,96 @@ const fixture = Effect.gen(function* () {
2021} )
2122const absolute = ( value : string ) => AbsolutePath . make ( value )
2223
24+ describe ( "ReadTool text serialization" , ( ) => {
25+ const cases = [
26+ {
27+ name : "preserves a selected trailing blank line before continuation" ,
28+ content : "alpha\n\nomega\n" ,
29+ page : { offset : 1 , limit : 2 } ,
30+ output : { type : "text-page" , content : "alpha\n" , offset : 1 , truncated : true , next : 3 } ,
31+ model : "Read file lines.txt, lines 1-2\n1: alpha\n2: \n[Output truncated. Continue reading with offset: 3]" ,
32+ } ,
33+ {
34+ name : "preserves multiple selected trailing blank lines at a noninitial offset" ,
35+ content : "before\nalpha\n\n\nomega\n" ,
36+ page : { offset : 2 , limit : 3 } ,
37+ output : { type : "text-page" , content : "alpha\n\n" , offset : 2 , truncated : true , next : 5 } ,
38+ model : "Read file lines.txt, lines 2-4\n2: alpha\n3: \n4: \n[Output truncated. Continue reading with offset: 5]" ,
39+ } ,
40+ {
41+ name : "preserves a selected trailing blank line at EOF" ,
42+ content : "alpha\n\n" ,
43+ page : { limit : 2 } ,
44+ output : { type : "text-page" , content : "alpha\n" , offset : 1 , truncated : false } ,
45+ model : "Read file lines.txt, lines 1-2\n1: alpha\n2: " ,
46+ } ,
47+ {
48+ name : "preserves internal blank lines in a page" ,
49+ content : "alpha\n\nomega\n" ,
50+ page : { limit : 3 } ,
51+ output : { type : "text-page" , content : "alpha\n\nomega" , offset : 1 , truncated : false } ,
52+ model : "Read file lines.txt, lines 1-3\n1: alpha\n2: \n3: omega" ,
53+ } ,
54+ {
55+ name : "preserves continuation for a nonblank page" ,
56+ content : "alpha\n\nomega\n" ,
57+ page : { limit : 1 } ,
58+ output : { type : "text-page" , content : "alpha" , offset : 1 , truncated : true , next : 2 } ,
59+ model : "Read file lines.txt, lines 1-1\n1: alpha\n[Output truncated. Continue reading with offset: 2]" ,
60+ } ,
61+ {
62+ name : "strips only the terminal file newline in a whole-file read" ,
63+ content : "alpha\n\n" ,
64+ page : { } ,
65+ output : { type : "file" , content : "alpha\n\n" , encoding : "utf8" } ,
66+ model : "Read file lines.txt, lines 1-2\n1: alpha\n2: " ,
67+ } ,
68+ {
69+ name : "does not add a line for a whole-file terminal newline" ,
70+ content : "alpha\n" ,
71+ page : { } ,
72+ output : { type : "file" , content : "alpha\n" , encoding : "utf8" } ,
73+ model : "Read file lines.txt, lines 1-1\n1: alpha" ,
74+ } ,
75+ {
76+ name : "preserves a whole-file read without a terminal newline" ,
77+ content : "alpha" ,
78+ page : { } ,
79+ output : { type : "file" , content : "alpha" , encoding : "utf8" } ,
80+ model : "Read file lines.txt, lines 1-1\n1: alpha" ,
81+ } ,
82+ {
83+ name : "preserves empty whole-file output" ,
84+ content : "" ,
85+ page : { } ,
86+ output : { type : "file" , content : "" , encoding : "utf8" } ,
87+ model : "Read file lines.txt, 0 lines" ,
88+ } ,
89+ {
90+ name : "preserves empty-file page output" ,
91+ content : "" ,
92+ page : { limit : 2 } ,
93+ output : { type : "text-page" , content : "" , offset : 1 , truncated : false } ,
94+ model : "Read file lines.txt, 0 lines" ,
95+ } ,
96+ ]
97+
98+ cases . forEach ( ( input ) => {
99+ it . live ( input . name , ( ) =>
100+ Effect . gen ( function * ( ) {
101+ const current = yield * fixture
102+ const file = absolute ( path . join ( current . directory , "lines.txt" ) )
103+ yield * current . files . writeFileString ( file , input . content )
104+
105+ const result = yield * ReadToolFileSystem . read ( current . environment , file , "lines.txt" , input . page )
106+
107+ expect ( result ) . toMatchObject ( input . output )
108+ expect ( ReadTool . toModelContent ( "lines.txt" , undefined , result ) ) . toBe ( input . model )
109+ } ) ,
110+ )
111+ } )
112+ } )
113+
23114describe ( "ReadToolFileSystem" , ( ) => {
24115 it . effect ( "preserves the environment not-found error" , ( ) =>
25116 Effect . gen ( function * ( ) {
0 commit comments