package pagination import ( "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // TestCursor_Encode 测试 Cursor 编码 func TestCursor_Encode(t *testing.T) { tests := []struct { name string cursor *Cursor expected string }{ { name: "nil cursor", cursor: nil, expected: "", }, { name: "zero LastID", cursor: &Cursor{LastID: 0, LastValue: time.Now()}, expected: "", }, { name: "valid cursor", cursor: &Cursor{LastID: 123, LastValue: time.Unix(1609459200, 0)}, expected: "eyJsYXN0X2lkIjoxMjMsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := tt.cursor.Encode() if tt.expected == "" { assert.Empty(t, got) } else { assert.NotEmpty(t, got) } }) } } // TestDecode 测试 Cursor 解码 func TestDecode(t *testing.T) { tests := []struct { name string encoded string wantNil bool wantErr bool expectedID int64 expectedTime time.Time }{ { name: "empty string", encoded: "", wantNil: true, wantErr: false, }, { name: "invalid base64", encoded: "!!!invalid!!!", wantNil: true, wantErr: true, }, { name: "invalid json", encoded: "aW52YWxpZCBqc29u", // "invalid json" base64 wantNil: true, wantErr: true, }, { name: "valid cursor", encoded: "eyJsYXN0X2lkIjoxMjMsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9", wantNil: false, wantErr: false, expectedID: 123, expectedTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := Decode(tt.encoded) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) if tt.wantNil { assert.Nil(t, got) } else { require.NotNil(t, got) assert.Equal(t, tt.expectedID, got.LastID) assert.Equal(t, tt.expectedTime, got.LastValue) } }) } } // TestClampPageSize 测试分页大小限制 func TestClampPageSize(t *testing.T) { tests := []struct { name string input int expected int }{ {"zero", 0, DefaultPageSize}, {"negative", -1, DefaultPageSize}, {"negative large", -100, DefaultPageSize}, {"one", 1, 1}, {"ten", 10, 10}, {"default", 20, 20}, {"max", 100, 100}, {"over max", 101, MaxPageSize}, {"large", 1000, MaxPageSize}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ClampPageSize(tt.input) assert.Equal(t, tt.expected, got) }) } } // TestBuildNextCursor 测试构建下一页游标 func TestBuildNextCursor(t *testing.T) { tests := []struct { name string lastID int64 lastTime time.Time expected string }{ { name: "zero ID", lastID: 0, lastTime: time.Now(), expected: "", }, { name: "valid cursor", lastID: 456, lastTime: time.Unix(1609459200, 0), expected: "eyJsYXN0X2lkIjo0NTYsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := BuildNextCursor(tt.lastID, tt.lastTime) if tt.expected == "" { assert.Empty(t, got) } else { assert.NotEmpty(t, got) // 验证可以解码 decoded, err := Decode(got) require.NoError(t, err) assert.Equal(t, tt.lastID, decoded.LastID) } }) } } // TestPageResult 测试 PageResult 结构 func TestPageResult(t *testing.T) { // 测试泛型 PageResult type Item struct { ID int Name string } items := []Item{ {ID: 1, Name: "item1"}, {ID: 2, Name: "item2"}, } result := PageResult[Item]{ Items: items, Total: 100, NextCursor: "cursor123", HasMore: true, PageSize: 20, } assert.Len(t, result.Items, 2) assert.Equal(t, int64(100), result.Total) assert.Equal(t, "cursor123", result.NextCursor) assert.True(t, result.HasMore) assert.Equal(t, 20, result.PageSize) } // TestCursor_RoundTrip 测试编码解码往返 func TestCursor_RoundTrip(t *testing.T) { original := &Cursor{ LastID: 999, LastValue: time.Date(2023, 6, 15, 10, 30, 0, 0, time.UTC), } encoded := original.Encode() require.NotEmpty(t, encoded) decoded, err := Decode(encoded) require.NoError(t, err) require.NotNil(t, decoded) assert.Equal(t, original.LastID, decoded.LastID) assert.Equal(t, original.LastValue, decoded.LastValue) } // TestConstants 测试常量值 func TestConstants(t *testing.T) { assert.Equal(t, 20, DefaultPageSize) assert.Equal(t, 100, MaxPageSize) }