@@ -87,16 +87,22 @@ func cardsJSON(firstID, count int) string {
8787}
8888
8989func runCardTableList (t * testing.T , format output.Format , args ... string ) (* cardTableTransport , * bytes.Buffer , error ) {
90+ t .Helper ()
91+ transport , buf , _ , err := runCardTableListStderr (t , format , args ... )
92+ return transport , buf , err
93+ }
94+
95+ func runCardTableListStderr (t * testing.T , format output.Format , args ... string ) (* cardTableTransport , * bytes.Buffer , * bytes.Buffer , error ) {
9096 t .Helper ()
9197 transport := & cardTableTransport {}
9298 app , _ := newTestAppWithTransport (t , transport )
93- buf := & bytes.Buffer {}
94- app .Output = output .New (output.Options {Format : format , Writer : buf , ErrWriter : & bytes. Buffer {} })
99+ buf , errBuf := & bytes. Buffer {}, & bytes.Buffer {}
100+ app .Output = output .New (output.Options {Format : format , Writer : buf , ErrWriter : errBuf })
95101 if format == output .FormatCount {
96102 app .Flags .Count = true
97103 }
98104 err := executeCommand (NewCardsCmd (), app , append ([]string {"list" , "--in" , "123" , "--card-table" , "777" }, args ... )... )
99- return transport , buf , err
105+ return transport , buf , errBuf , err
100106}
101107
102108// TestCardsListAcrossColumnsCapsEachColumn pins that listing a whole card table
@@ -119,6 +125,53 @@ func TestCardsListAcrossColumnsCapsEachColumn(t *testing.T) {
119125 assert .Contains (t , resp .Notice , "--all" )
120126}
121127
128+ // TestCardsListAcrossColumnsCapIsMachineReadable pins that the JSON envelope
129+ // names every column the cap cut short, with what it showed and the total, so
130+ // a script can tell a capped listing from a complete one without parsing prose.
131+ func TestCardsListAcrossColumnsCapIsMachineReadable (t * testing.T ) {
132+ _ , buf , err := runCardTableList (t , output .FormatJSON )
133+ require .NoError (t , err )
134+
135+ var resp struct {
136+ Meta struct {
137+ TruncatedColumns []map [string ]any `json:"truncated_columns"`
138+ } `json:"meta"`
139+ }
140+ require .NoError (t , json .Unmarshal (buf .Bytes (), & resp ))
141+ require .Len (t , resp .Meta .TruncatedColumns , 1 , "only Done is cut short" )
142+ done := resp .Meta .TruncatedColumns [0 ]
143+ assert .EqualValues (t , 2 , done ["id" ])
144+ assert .Equal (t , "Done" , done ["title" ])
145+ assert .EqualValues (t , cardsPerColumnLimit , done ["shown" ])
146+ assert .EqualValues (t , doneColumnCards , done ["total" ])
147+ }
148+
149+ // TestCardsListAcrossColumnsCapIsLoudInQuietModes pins that --agent and --quiet,
150+ // which print the data alone, still say on stderr that the list was cut short,
151+ // while stdout stays the bare card array.
152+ func TestCardsListAcrossColumnsCapIsLoudInQuietModes (t * testing.T ) {
153+ _ , buf , errBuf , err := runCardTableListStderr (t , output .FormatQuiet )
154+ require .NoError (t , err )
155+
156+ assert .Contains (t , errBuf .String (), "notice: Showing the first 100 cards of Done (140 total)" )
157+
158+ var data []map [string ]any
159+ require .NoError (t , json .Unmarshal (buf .Bytes (), & data ), "stdout must stay data-only" )
160+ assert .Len (t , data , 3 + cardsPerColumnLimit )
161+ }
162+
163+ // TestCardsListAcrossColumnsUncappedIsQuiet pins that a listing nothing cut
164+ // short carries no notice and no truncated_columns, in either channel.
165+ func TestCardsListAcrossColumnsUncappedIsQuiet (t * testing.T ) {
166+ _ , _ , errBuf , err := runCardTableListStderr (t , output .FormatQuiet , "--all" )
167+ require .NoError (t , err )
168+ assert .Empty (t , errBuf .String ())
169+
170+ _ , buf , err := runCardTableList (t , output .FormatJSON , "--all" )
171+ require .NoError (t , err )
172+ assert .NotContains (t , buf .String (), "truncated_columns" )
173+ }
174+
122175// TestCardsListAcrossColumnsAllWalksEveryPage pins that --all, now accepted
123176// without --column, restores the full walk of every column.
124177func TestCardsListAcrossColumnsAllWalksEveryPage (t * testing.T ) {
0 commit comments