@@ -273,7 +273,8 @@ func TestPaginatedSearchEnterAndRestore(t *testing.T) {
273273 assert .False (t , pm .searching )
274274 assert .True (t , searchCalled )
275275 assert .NotNil (t , cmd )
276- assert .NotNil (t , pm .savedRows )
276+ assert .True (t , pm .hasSearchState )
277+ assert .Equal (t , 1 , pm .fetchGeneration )
277278
278279 // Restore by submitting empty search
279280 pm .searching = true
@@ -283,7 +284,60 @@ func TestPaginatedSearchEnterAndRestore(t *testing.T) {
283284 pm = result .(paginatedModel )
284285
285286 assert .Equal (t , [][]string {{"original" }}, pm .rows )
287+ assert .False (t , pm .hasSearchState )
286288 assert .Nil (t , pm .savedRows )
289+ assert .Equal (t , 2 , pm .fetchGeneration )
290+ }
291+
292+ func TestPaginatedSearchRestoreEmptyOriginalTable (t * testing.T ) {
293+ cfg := & TableConfig {
294+ Columns : []ColumnDef {
295+ {Header : "Name" },
296+ },
297+ Search : & SearchConfig {
298+ Placeholder : "search..." ,
299+ NewIterator : func (_ context.Context , query string ) RowIterator {
300+ return & stringRowIterator {rows : [][]string {{"found:" + query }}}
301+ },
302+ },
303+ }
304+
305+ ctx := t .Context ()
306+ originalIter := & stringRowIterator {}
307+ m := paginatedModel {
308+ cfg : cfg ,
309+ headers : []string {"Name" },
310+ rowIter : originalIter ,
311+ makeFetchCmd : newFetchCmdFunc (ctx ),
312+ makeSearchIter : newSearchIterFunc (ctx , cfg .Search ),
313+ exhausted : true ,
314+ ready : true ,
315+ }
316+ m .viewport .Width = 80
317+ m .viewport .Height = 20
318+
319+ m .searching = true
320+ m .searchInput = "test"
321+
322+ result , cmd := m .updateSearch (tea.KeyMsg {Type : tea .KeyEnter })
323+ pm := result .(paginatedModel )
324+
325+ assert .NotNil (t , cmd )
326+ assert .True (t , pm .hasSearchState )
327+ assert .Nil (t , pm .savedRows )
328+ assert .Equal (t , 1 , pm .fetchGeneration )
329+
330+ pm .searching = true
331+ pm .searchInput = ""
332+ pm .rows = [][]string {{"found:test" }}
333+ result , _ = pm .updateSearch (tea.KeyMsg {Type : tea .KeyEnter })
334+ pm = result .(paginatedModel )
335+
336+ assert .Nil (t , pm .rows )
337+ assert .Equal (t , originalIter , pm .rowIter )
338+ assert .True (t , pm .exhausted )
339+ assert .False (t , pm .hasSearchState )
340+ assert .Equal (t , 2 , pm .fetchGeneration )
287341}
288342
289343func TestPaginatedSearchEscCancels (t * testing.T ) {
@@ -389,6 +443,28 @@ func TestMaybeFetchSetsLoadingAndPreventsDoubleFetch(t *testing.T) {
389443 assert .Nil (t , cmd2 , "should not trigger second fetch while loading" )
390444}
391445
446+ func TestPaginatedIgnoresStaleFetchMessages (t * testing.T ) {
447+ m := newTestModel (t , nil , 0 )
448+ m .ready = true
449+ m .viewport .Width = 80
450+ m .viewport .Height = 20
451+ m .rows = [][]string {{"search" , "1" }}
452+ m .widths = []int {6 , 1 }
453+ m .loading = true
454+ m .fetchGeneration = 1
455+
456+ result , _ := m .Update (rowsFetchedMsg {
457+ rows : [][]string {{"stale" , "2" }},
458+ exhausted : true ,
459+ generation : 0 ,
460+ })
461+ pm := result .(paginatedModel )
462+
463+ assert .Equal (t , [][]string {{"search" , "1" }}, pm .rows )
464+ assert .False (t , pm .exhausted )
465+ assert .True (t , pm .loading )
466+ }
467+
392468func TestFetchCmdWithIterator (t * testing.T ) {
393469 rows := make ([][]string , 60 )
394470 for i := range rows {
@@ -406,6 +482,7 @@ func TestFetchCmdWithIterator(t *testing.T) {
406482 require .True (t , ok )
407483
408484 assert .NoError (t , fetched .err )
485+ assert .Equal (t , 0 , fetched .generation )
409486 assert .Len (t , fetched .rows , fetchBatchSize )
410487 assert .False (t , fetched .exhausted , "iterator should have more rows" )
411488}
@@ -422,6 +499,7 @@ func TestFetchCmdExhaustsSmallIterator(t *testing.T) {
422499 require .True (t , ok )
423500
424501 assert .NoError (t , fetched .err )
502+ assert .Equal (t , 0 , fetched .generation )
425503 assert .Len (t , fetched .rows , 2 )
426504 assert .True (t , fetched .exhausted , "small iterator should be exhausted" )
427505}
0 commit comments