package pagination import ( "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func init() { gin.SetMode(gin.TestMode) } func newContext(query string) *gin.Context { w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request = httptest.NewRequest(http.MethodGet, "/?"+query, nil) return c } func TestParse_Defaults(t *testing.T) { c := newContext("") p := Parse(c) assert.Equal(t, 1, p.Page) assert.Equal(t, DefaultPerPage, p.PerPage) assert.Equal(t, 0, p.Offset) } func TestParse_ValidParams(t *testing.T) { c := newContext("page=3&per_page=10") p := Parse(c) assert.Equal(t, 3, p.Page) assert.Equal(t, 10, p.PerPage) assert.Equal(t, 20, p.Offset) // (3-1)*10 } func TestParse_PageZero(t *testing.T) { c := newContext("page=0") p := Parse(c) assert.Equal(t, 1, p.Page) assert.Equal(t, DefaultPerPage, p.PerPage) assert.Equal(t, 0, p.Offset) } func TestParse_NegativePage(t *testing.T) { c := newContext("page=-5") p := Parse(c) assert.Equal(t, 1, p.Page) assert.Equal(t, 0, p.Offset) } func TestParse_PerPageZero(t *testing.T) { c := newContext("per_page=0") p := Parse(c) assert.Equal(t, 1, p.Page) assert.Equal(t, DefaultPerPage, p.PerPage) assert.Equal(t, 0, p.Offset) } func TestParse_NegativePerPage(t *testing.T) { c := newContext("per_page=-10") p := Parse(c) assert.Equal(t, DefaultPerPage, p.PerPage) } func TestParse_PerPageExceedsMax(t *testing.T) { c := newContext("per_page=500") p := Parse(c) assert.Equal(t, MaxPerPage, p.PerPage) } func TestParse_PerPageExactlyMax(t *testing.T) { c := newContext("per_page=100") p := Parse(c) assert.Equal(t, MaxPerPage, p.PerPage) } func TestParse_PerPageJustAboveMax(t *testing.T) { c := newContext("per_page=101") p := Parse(c) assert.Equal(t, MaxPerPage, p.PerPage) } func TestParse_PerPageJustBelowMax(t *testing.T) { c := newContext("per_page=99") p := Parse(c) assert.Equal(t, 99, p.PerPage) } func TestParse_NonNumericPage(t *testing.T) { // strconv.Atoi returns 0 for non-numeric, which gets clamped to 1 c := newContext("page=abc") p := Parse(c) assert.Equal(t, 1, p.Page) } func TestParse_NonNumericPerPage(t *testing.T) { c := newContext("per_page=abc") p := Parse(c) assert.Equal(t, DefaultPerPage, p.PerPage) } func TestParse_OffsetCalculation(t *testing.T) { tests := []struct { page, perPage, expectedOffset int }{ {1, 25, 0}, {2, 25, 25}, {3, 10, 20}, {10, 50, 450}, {1, 1, 0}, } for _, tt := range tests { c := newContext("") // Manually set query params c.Request.URL.RawQuery = "page=" + itoa(tt.page) + "&per_page=" + itoa(tt.perPage) p := Parse(c) assert.Equal(t, tt.expectedOffset, p.Offset, "page=%d perPage=%d", tt.page, tt.perPage) } } func TestOffsetToPage_NormalCases(t *testing.T) { tests := []struct { offset, perPage, expected int }{ {0, 25, 1}, {24, 25, 1}, {25, 25, 2}, {50, 25, 3}, {0, 10, 1}, {9, 10, 1}, {10, 10, 2}, {100, 50, 3}, } for _, tt := range tests { result := OffsetToPage(tt.offset, tt.perPage) assert.Equal(t, tt.expected, result, "offset=%d perPage=%d", tt.offset, tt.perPage) } } func TestOffsetToPage_ZeroPerPage(t *testing.T) { assert.Equal(t, 1, OffsetToPage(100, 0)) } func TestOffsetToPage_NegativePerPage(t *testing.T) { assert.Equal(t, 1, OffsetToPage(100, -5)) } func TestOffsetToPage_ZeroOffset(t *testing.T) { assert.Equal(t, 1, OffsetToPage(0, 25)) } // itoa is a small helper to avoid importing strconv in test func itoa(n int) string { if n == 0 { return "0" } neg := false if n < 0 { neg = true n = -n } var buf [20]byte i := len(buf) for n > 0 { i-- buf[i] = byte('0' + n%10) n /= 10 } if neg { i-- buf[i] = '-' } return string(buf[i:]) }