Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

pageable_test.gno

9.95 Kb · 410 lines
  1package pageable
  2
  3import (
  4	"testing"
  5)
  6
  7// MockPageable implements the Pageable interface for testing
  8type MockPageable struct {
  9	items []int
 10}
 11
 12func (m *MockPageable) Size() int {
 13	return len(m.items)
 14}
 15
 16func (m *MockPageable) IterateByOffset(offset int, count int, cb func(index interface{}, value interface{}) bool) bool {
 17	if count < 0 {
 18		// Handle reversed iteration
 19		count = -count                     // Make count positive
 20		start := len(m.items) - 1 - offset // Adjust start position from the end
 21		if start >= len(m.items) {
 22			start = len(m.items) - 1
 23		}
 24		end := start - count + 1
 25		if end < 0 {
 26			end = 0
 27		}
 28		for i := start; i >= end; i-- {
 29			if cb(i, m.items[i]) {
 30				return true
 31			}
 32		}
 33		return false
 34	}
 35
 36	end := offset + count
 37	if end > len(m.items) {
 38		end = len(m.items)
 39	}
 40
 41	for i := offset; i < end; i++ {
 42		if cb(i, m.items[i]) {
 43			return true
 44		}
 45	}
 46	return false
 47}
 48
 49func TestPager(t *testing.T) {
 50	// Create test data
 51	mock := &MockPageable{
 52		items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
 53	}
 54
 55	tests := []struct {
 56		name        string
 57		pageSize    int
 58		pageNum     int
 59		wantItems   int
 60		wantTotal   int
 61		wantPages   int
 62		wantHasPrev bool
 63		wantHasNext bool
 64	}{
 65		{"first page", 3, 1, 3, 10, 4, false, true},
 66		{"middle page", 3, 2, 3, 10, 4, true, true},
 67		{"last page", 3, 4, 1, 10, 4, true, false},
 68		{"invalid page", 3, 5, 0, 10, 4, true, false},
 69		{"zero page", 3, 0, 0, 10, 4, false, true},
 70		{"all items", 10, 1, 10, 10, 1, false, false},
 71	}
 72
 73	for _, tt := range tests {
 74		t.Run(tt.name, func(t *testing.T) {
 75			pager := NewPager(mock, tt.pageSize, false)
 76			page := pager.GetPage(tt.pageNum)
 77
 78			if len(page.Items) != tt.wantItems {
 79				t.Errorf("got %d items, want %d", len(page.Items), tt.wantItems)
 80			}
 81			if page.TotalItems != tt.wantTotal {
 82				t.Errorf("got %d total items, want %d", page.TotalItems, tt.wantTotal)
 83			}
 84			if page.TotalPages != tt.wantPages {
 85				t.Errorf("got %d total pages, want %d", page.TotalPages, tt.wantPages)
 86			}
 87			if page.HasPrev != tt.wantHasPrev {
 88				t.Errorf("got HasPrev=%v, want %v", page.HasPrev, tt.wantHasPrev)
 89			}
 90			if page.HasNext != tt.wantHasNext {
 91				t.Errorf("got HasNext=%v, want %v", page.HasNext, tt.wantHasNext)
 92			}
 93		})
 94	}
 95}
 96
 97func TestPicker(t *testing.T) {
 98	mock := &MockPageable{
 99		items: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
100	}
101
102	tests := []struct {
103		name     string
104		pageSize int
105		pageNum  int
106		want     string
107	}{
108		{"first page", 3, 1, "**1** | [2](?page=2) | [3](?page=3) | [4](?page=4)"},
109		{"middle page", 3, 2, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4)"},
110		{"last page", 3, 4, "[1](?page=1) | [2](?page=2) | [3](?page=3) | **4**"},
111		{"single page", 10, 1, ""},
112	}
113
114	for _, tt := range tests {
115		t.Run(tt.name, func(t *testing.T) {
116			pager := NewPager(mock, tt.pageSize, false)
117			page := pager.GetPage(tt.pageNum)
118			got := page.Picker()
119			if got != tt.want {
120				t.Errorf("\ngot:  %s\nwant: %s", got, tt.want)
121			}
122		})
123	}
124}
125
126func TestReversedPager(t *testing.T) {
127	mock := &MockPageable{
128		items: []int{1, 2, 3, 4, 5},
129	}
130
131	pager := NewPager(mock, 2, true)
132	page := pager.GetPage(1)
133
134	if len(page.Items) != 2 {
135		t.Errorf("got %d items, want 2", len(page.Items))
136	}
137
138	// Check if items are in reverse order
139	if page.Items[0].Value.(int) != 5 || page.Items[1].Value.(int) != 4 {
140		t.Errorf("items not in reverse order: got %v", page.Items)
141	}
142}
143
144func TestParseQuery(t *testing.T) {
145	mock := &MockPageable{items: []int{1, 2, 3}}
146	pager := NewPager(mock, 10, false)
147
148	tests := []struct {
149		name      string
150		url       string
151		wantPage  int
152		wantSize  int
153		wantError bool
154	}{
155		{"valid query", "/?page=2&size=5", 2, 5, false},
156		{"missing params", "/", 1, 10, false},
157		{"invalid page", "/?page=invalid", 1, 10, false},
158		{"invalid size", "/?size=invalid", 1, 10, false},
159		{"negative values", "/?page=-1&size=-5", 1, 10, false},
160		{"invalid url", ":%invalid", 1, 10, true},
161	}
162
163	for _, tt := range tests {
164		t.Run(tt.name, func(t *testing.T) {
165			page, size, err := pager.ParseQuery(tt.url)
166			if (err != nil) != tt.wantError {
167				t.Errorf("got error=%v, want error=%v", err != nil, tt.wantError)
168			}
169			if page != tt.wantPage {
170				t.Errorf("got page=%d, want %d", page, tt.wantPage)
171			}
172			if size != tt.wantSize {
173				t.Errorf("got size=%d, want %d", size, tt.wantSize)
174			}
175		})
176	}
177}
178
179func TestPager_UI_WithManyPages(t *testing.T) {
180	mock := &MockPageable{
181		items: make([]int, 100),
182	}
183	for i := 0; i < 100; i++ {
184		mock.items[i] = i
185	}
186
187	pager := NewPager(mock, 10, false)
188
189	tests := []struct {
190		name     string
191		pageNum  int
192		pageSize int
193		want     string
194	}{
195		{"first page", 1, 10, "**1** | [2](?page=2) | [3](?page=3) | … | [10](?page=10)"},
196		{"second page", 2, 10, "[1](?page=1) | **2** | [3](?page=3) | [4](?page=4) | … | [10](?page=10)"},
197		{"middle page", 5, 10, "[1](?page=1) | … | [3](?page=3) | [4](?page=4) | **5** | [6](?page=6) | [7](?page=7) | … | [10](?page=10)"},
198		{"penultimate page", 9, 10, "[1](?page=1) | … | [7](?page=7) | [8](?page=8) | **9** | [10](?page=10)"},
199		{"last page", 10, 10, "[1](?page=1) | … | [8](?page=8) | [9](?page=9) | **10**"},
200	}
201
202	for _, tt := range tests {
203		t.Run(tt.name, func(t *testing.T) {
204			page := pager.GetPageWithSize(tt.pageNum, tt.pageSize)
205			got := page.Picker()
206			if got != tt.want {
207				t.Errorf("\ngot:  %s\nwant: %s", got, tt.want)
208			}
209		})
210	}
211}
212
213func TestGetPageWithSize(t *testing.T) {
214	mock := &MockPageable{
215		items: []int{1, 2, 3, 4, 5},
216	}
217
218	pager := NewPager(mock, 10, false)
219
220	tests := []struct {
221		name      string
222		pageNum   int
223		pageSize  int
224		wantLen   int
225		wantFirst int
226	}{
227		{"custom size smaller", 1, 2, 2, 1},
228		{"custom size exact", 1, 5, 5, 1},
229		{"custom size larger", 1, 10, 5, 1},
230		{"second page partial", 2, 3, 2, 4},
231		{"empty page", 3, 3, 0, 0},
232	}
233
234	for _, tt := range tests {
235		t.Run(tt.name, func(t *testing.T) {
236			page := pager.GetPageWithSize(tt.pageNum, tt.pageSize)
237			if len(page.Items) != tt.wantLen {
238				t.Errorf("got %d items, want %d", len(page.Items), tt.wantLen)
239			}
240			if tt.wantLen > 0 && page.Items[0].Value.(int) != tt.wantFirst {
241				t.Errorf("first item got %d, want %d", page.Items[0].Value.(int), tt.wantFirst)
242			}
243		})
244	}
245}
246
247func TestInvalidInputs(t *testing.T) {
248	mock := &MockPageable{
249		items: []int{1, 2, 3},
250	}
251
252	tests := []struct {
253		name     string
254		pageSize int
255		pageNum  int
256		wantLen  int
257	}{
258		{"zero page size", 0, 1, 0},
259		{"negative page size", -1, 1, 0},
260		{"negative page number", 10, -1, 0},
261		{"too large page number", 10, 999, 0},
262	}
263
264	for _, tt := range tests {
265		t.Run(tt.name, func(t *testing.T) {
266			pager := NewPager(mock, tt.pageSize, false)
267			page := pager.GetPage(tt.pageNum)
268			if len(page.Items) != tt.wantLen {
269				t.Errorf("got %d items, want %d", len(page.Items), tt.wantLen)
270			}
271		})
272	}
273}
274
275func TestEmptySource(t *testing.T) {
276	mock := &MockPageable{items: []int{}}
277	pager := NewPager(mock, 10, false)
278
279	page := pager.GetPage(1)
280	if page.TotalItems != 0 {
281		t.Errorf("got total items %d, want 0", page.TotalItems)
282	}
283	if page.TotalPages != 0 {
284		t.Errorf("got total pages %d, want 0", page.TotalPages)
285	}
286	if page.HasNext {
287		t.Error("got HasNext=true, want false")
288	}
289	if page.HasPrev {
290		t.Error("got HasPrev=true, want false")
291	}
292	if len(page.Items) != 0 {
293		t.Errorf("got %d items, want 0", len(page.Items))
294	}
295}
296
297func TestReversedPagerEdgeCases(t *testing.T) {
298	mock := &MockPageable{
299		items: []int{1, 2, 3, 4, 5},
300	}
301	pager := NewPager(mock, 2, true)
302
303	tests := []struct {
304		name     string
305		pageNum  int
306		pageSize int
307		want     []int
308	}{
309		{"last partial page", 3, 2, []int{1}},
310		{"oversized page", 1, 10, []int{5, 4, 3, 2, 1}},
311		{"empty result", 4, 2, nil},
312	}
313
314	for _, tt := range tests {
315		t.Run(tt.name, func(t *testing.T) {
316			page := pager.GetPageWithSize(tt.pageNum, tt.pageSize)
317			got := make([]int, len(page.Items))
318			for i, item := range page.Items {
319				got[i] = item.Value.(int)
320			}
321
322			if len(got) != len(tt.want) {
323				t.Errorf("got length %d, want length %d", len(got), len(tt.want))
324				return
325			}
326
327			for i := 0; i < len(got); i++ {
328				if got[i] != tt.want[i] {
329					t.Errorf("at index %d: got %d, want %d", i, got[i], tt.want[i])
330				}
331			}
332		})
333	}
334}
335
336func TestCustomQueryParams(t *testing.T) {
337	mock := &MockPageable{items: []int{1, 2, 3}}
338	pager := NewPager(mock, 10, false)
339	pager.PageQueryParam = "p"
340	pager.SizeQueryParam = "s"
341
342	tests := []struct {
343		name     string
344		url      string
345		wantPage int
346		wantSize int
347	}{
348		{"custom params", "/?p=2&s=1", 2, 1},
349		{"old params ignored", "/?page=3&size=2", 1, 10},
350	}
351
352	for _, tt := range tests {
353		t.Run(tt.name, func(t *testing.T) {
354			page, size, err := pager.ParseQuery(tt.url)
355			if err != nil {
356				t.Errorf("unexpected error: %v", err)
357			}
358			if page != tt.wantPage {
359				t.Errorf("got page=%d, want %d", page, tt.wantPage)
360			}
361			if size != tt.wantSize {
362				t.Errorf("got size=%d, want %d", size, tt.wantSize)
363			}
364		})
365	}
366}
367
368func TestMustGetPageByPath(t *testing.T) {
369	mock := &MockPageable{items: []int{1, 2, 3}}
370	pager := NewPager(mock, 10, false)
371
372	t.Run("panic on invalid url", func(t *testing.T) {
373		defer func() {
374			if r := recover(); r == nil {
375				t.Error("expected panic but got none")
376			}
377		}()
378		pager.MustGetPageByPath(":%invalid")
379	})
380}
381
382func TestPickerWithPath(t *testing.T) {
383	mock := &MockPageable{
384		items: []int{1, 2, 3, 4, 5, 6},
385	}
386
387	tests := []struct {
388		name     string
389		pageSize int
390		pageNum  int
391		path     string
392		want     string
393	}{
394		{"with query params", 2, 1, "/test?foo=bar&baz=qux", "**1** | [2](?page=2&baz=qux&foo=bar) | [3](?page=3&baz=qux&foo=bar)"},
395		{"existing page param", 2, 2, "/test?page=999&other=value", "[1](?page=1&other=value) | **2** | [3](?page=3&other=value)"},
396		{"no query params", 2, 3, "/test", "[1](?page=1) | [2](?page=2) | **3**"},
397		{"empty path", 2, 2, "", "[1](?page=1) | **2** | [3](?page=3)"},
398	}
399
400	for _, tt := range tests {
401		t.Run(tt.name, func(t *testing.T) {
402			pager := NewPager(mock, tt.pageSize, false)
403			page := pager.GetPageWithSize(tt.pageNum, tt.pageSize)
404			got := page.PickerWithPath(tt.path)
405			if got != tt.want {
406				t.Errorf("\ngot:  %s\nwant: %s", got, tt.want)
407			}
408		})
409	}
410}