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}