@@ -2,10 +2,13 @@ package commands
22
33import (
44 "bufio"
5+ "errors"
6+ "io"
57 "strings"
68 "testing"
79
8- "github.com/spf13/cobra"
10+ "github.com/stretchr/testify/assert"
11+ "github.com/stretchr/testify/require"
912)
1013
1114func TestReadMultilineInput (t * testing.T ) {
@@ -15,40 +18,34 @@ func TestReadMultilineInput(t *testing.T) {
1518 expected string
1619 wantErr bool
1720 }{
18- {
19- name : "single line input" ,
20- input : "hello world" ,
21- expected : "hello world" ,
22- wantErr : false ,
23- },
2421 {
2522 name : "single line with triple quotes" ,
2623 input : `"""hello world"""` ,
27- expected : `""" hello world""" ` ,
24+ expected : `hello world` ,
2825 wantErr : false ,
2926 },
3027 {
3128 name : "multiline input with double quotes" ,
3229 input : `"""tell
33- me
34- a
35- joke"""` ,
36- expected : `""" tell
37- me
38- a
39- joke""" ` ,
30+ me
31+ a
32+ joke"""` ,
33+ expected : `tell
34+ me
35+ a
36+ joke` ,
4037 wantErr : false ,
4138 },
4239 {
4340 name : "multiline input with single quotes" ,
4441 input : `'''tell
45- me
46- a
47- joke'''` ,
48- expected : `''' tell
49- me
50- a
51- joke''' ` ,
42+ me
43+ a
44+ joke'''` ,
45+ expected : `tell
46+ me
47+ a
48+ joke` ,
5249 wantErr : false ,
5350 },
5451 {
@@ -61,27 +58,124 @@ joke'''`,
6158 name : "multiline with empty lines" ,
6259 input : `"""first line
6360
64- third line"""` ,
65- expected : `""" first line
61+ third line"""` ,
62+ expected : `first line
6663
67- third line"""` ,
64+ third line` ,
65+ wantErr : false ,
66+ },
67+ {
68+ name : "multiline with spaces and closing quotes on new line" ,
69+ input : `"""first line
70+ second line
71+ third line
72+ """` ,
73+ expected : `first line
74+ second line
75+ third line` , // this will intentionally trim the last newline
76+ wantErr : false ,
77+ },
78+ {
79+ name : "multiline with closing quotes and trailing spaces" ,
80+ input : `"""first line
81+ second line
82+ third line """` ,
83+ expected : `first line
84+ second line
85+ third line ` ,
86+ wantErr : false ,
87+ },
88+ {
89+ name : "single quotes with spaces" ,
90+ input : `'''foo bar'''` ,
91+ expected : `foo bar` ,
92+ wantErr : false ,
93+ },
94+ {
95+ name : "triple quotes only" ,
96+ input : `""""""` ,
97+ expected : "" ,
98+ wantErr : false ,
99+ },
100+ {
101+ name : "single quotes only" ,
102+ input : `''''''` ,
103+ expected : "" ,
104+ wantErr : false ,
105+ },
106+ {
107+ name : "closing quotes in middle of line" ,
108+ input : `"""foo"""bar"""` ,
109+ expected : `foo"""bar` ,
110+ wantErr : false ,
111+ },
112+ {
113+ name : "no closing quotes" ,
114+ input : `"""foo
115+ bar
116+ baz` ,
117+ expected : "" ,
118+ wantErr : true ,
119+ },
120+ {
121+ name : "invalid prefix" ,
122+ input : `"foo bar"` ,
123+ expected : "" ,
124+ wantErr : true ,
125+ },
126+ {
127+ name : "prefix but no content" ,
128+ input : `"""` ,
129+ expected : "" ,
130+ wantErr : true ,
131+ },
132+ {
133+ name : "prefix and newline only" ,
134+ input : `"""
135+ """` ,
136+ expected : "" ,
137+ wantErr : false ,
138+ },
139+ {
140+ name : "multiline with only whitespace" ,
141+ input : `"""
142+
143+ """` ,
144+ expected : `
145+
146+ ` ,
68147 wantErr : false ,
69148 },
70149 }
71150
151+ // Strings can be read either as the first line followed by the rest of the text,
152+ // or as a single block of text. Make sure we test both scenarios.
153+
72154 for _ , tt := range tests {
73155 t .Run (tt .name , func (t * testing.T ) {
74- // Create a mock command for testing
75- cmd := & cobra. Command {}
156+ r := bufio . NewReader ( strings . NewReader ( tt . input ))
157+ result , err := readMultilineString ( t . Context (), r , "" )
76158
77- // Create a scanner from the test input
78- scanner := bufio .NewScanner (strings .NewReader (tt .input ))
159+ if (err != nil ) != tt .wantErr {
160+ t .Errorf ("readMultilineInput() error = %v, wantErr %v" , err , tt .wantErr )
161+ return
162+ }
79163
80- // Capture output to avoid printing during tests
81- var output strings.Builder
82- cmd .SetOut (& output )
164+ if result != tt .expected {
165+ t .Errorf ("readMultilineInput() = %q, want %q" , result , tt .expected )
166+ }
167+ })
83168
84- result , err := readMultilineInput (cmd , scanner )
169+ t .Run (tt .name + "_chunked" , func (t * testing.T ) {
170+ r := bufio .NewReader (strings .NewReader (tt .input ))
171+ firstLine , err := r .ReadString ('\n' ) // Simulate reading the first line
172+ if errors .Is (err , io .EOF ) {
173+ // Some test cases are single line, EOF is ok here
174+ firstLine = tt .input
175+ } else {
176+ require .NoError (t , err )
177+ }
178+ result , err := readMultilineString (t .Context (), r , firstLine )
85179
86180 if (err != nil ) != tt .wantErr {
87181 t .Errorf ("readMultilineInput() error = %v, wantErr %v" , err , tt .wantErr )
@@ -98,18 +192,12 @@ third line"""`,
98192func TestReadMultilineInputUnclosed (t * testing.T ) {
99193 // Test unclosed multiline input (should return error)
100194 input := `"""unclosed multiline`
101- cmd := & cobra.Command {}
102- var output strings.Builder
103- cmd .SetOut (& output )
104-
105- scanner := bufio .NewScanner (strings .NewReader (input ))
106-
107- _ , err := readMultilineInput (cmd , scanner )
195+ _ , err := readMultilineString (t .Context (), strings .NewReader (input ), "" )
108196 if err == nil {
109- t .Error ("readMultilineInput() should return error for unclosed multiline input" )
197+ t .Fatal ("readMultilineInput() should return an error for unclosed multiline input" )
110198 }
111199
112- if ! strings .Contains (err .Error (), "unclosed multiline input" ) {
113- t . Errorf ( "readMultilineInput() error should mention unclosed multiline input, got: %v" , err )
114- }
200+ assert .Contains (t , err .Error (), "unclosed multiline input" , "error should mention unclosed multiline input" )
201+ // Error should also be io.EOF
202+ assert . True ( t , errors . Is ( err , io . EOF ), "error should be io.EOF" )
115203}
0 commit comments