package cursor import ( "errors" "testing" "time" "gitea.auvem.com/go-toolkit/dbx" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestBuildEdgesHasNextPageTupleOrdering(t *testing.T) { assert := assert.New(t) require := require.New(t) registerMeetingCursorColumns(t) when := time.Date(2026, 6, 23, 5, 0, 0, 0, time.UTC) base := NewCursor( NewStringValue("", Meeting.ID), Meeting.StartTime, OrderDescending, ) type meetingRow struct { id string start time.Time } items := []*meetingRow{ {id: "3FeXKOY42znOmyxxq3IEk5LJg98", start: time.Date(2026, 6, 26, 2, 0, 0, 0, time.UTC)}, {id: "3FWPfH9K7SOAikWv67BgKniF9E4", start: when}, {id: "3FWPabTpRGdmTydnULNbKpWrWhG", start: when}, } cursorFunc := func(row *meetingRow) (GenericCursor, error) { return base.CopyWithVals( NewStringValue(row.id, Meeting.ID), NewTimestampValue(row.start, Meeting.StartTime), ), nil } var endCursor GenericCursor countFn := func(_ dbx.Queryable, _, end GenericCursor) (QueryCountResult, error) { endCursor = end return QueryCountResult{Total: 3, After: 0}, nil } conn, err := BuildEdges(nil, countFn, items, cursorFunc) require.NoError(err) require.NotNil(conn) assert.False(conn.PageInfo.HasNextPage) require.NotNil(endCursor) assert.Equal("3FWPabTpRGdmTydnULNbKpWrWhG", endCursor.GenericIndex().(*StringValue).Val) } func TestBuildEdgesEmptyList(t *testing.T) { countFn := func(_ dbx.Queryable, _, _ GenericCursor) (QueryCountResult, error) { t.Fatal("countFn should not be called for empty list") return QueryCountResult{}, nil } conn, err := BuildEdges(nil, countFn, []*int{}, func(*int) (GenericCursor, error) { return nil, errors.New("unreachable") }) require.NoError(t, err) require.NotNil(t, conn) assert.Empty(t, conn.Edges) assert.False(t, conn.PageInfo.HasNextPage) assert.False(t, conn.PageInfo.HasPreviousPage) assert.Equal(t, 0, conn.TotalCount) } func TestBuildEdgesPaginationFlags(t *testing.T) { RegisterColumn(User.ID) cursorFunc := func(id *int) (GenericCursor, error) { return NewCursor(NewInt64Value(int64(*id), User.ID), User.ID, OrderDescending), nil } id1, id2 := 1, 2 conn, err := BuildEdges(nil, func(_ dbx.Queryable, _, _ GenericCursor) (QueryCountResult, error) { return QueryCountResult{Total: 10, After: 5, Before: 3}, nil }, []*int{&id1, &id2}, cursorFunc) require.NoError(t, err) assert.True(t, conn.PageInfo.HasNextPage) assert.True(t, conn.PageInfo.HasPreviousPage) assert.Equal(t, 10, conn.TotalCount) require.NotNil(t, conn.PageInfo.StartCursor) require.NotNil(t, conn.PageInfo.EndCursor) } func TestBuildEdgesErrors(t *testing.T) { RegisterColumn(User.ID) cursorFunc := func(id *int) (GenericCursor, error) { return NewCursor(NewInt64Value(int64(*id), User.ID), User.ID, OrderDescending), nil } countFn := func(_ dbx.Queryable, _, _ GenericCursor) (QueryCountResult, error) { return QueryCountResult{}, nil } t.Run("nil item", func(t *testing.T) { id := 1 _, err := BuildEdges(nil, countFn, []*int{&id, nil}, cursorFunc) require.Error(t, err) assert.Contains(t, err.Error(), "item at index 1 is nil") }) t.Run("cursor func error", func(t *testing.T) { cursorErr := errors.New("cursor failed") id := 1 _, err := BuildEdges(nil, countFn, []*int{&id}, func(*int) (GenericCursor, error) { return nil, cursorErr }) require.Error(t, err) assert.ErrorIs(t, err, cursorErr) }) t.Run("count fn error", func(t *testing.T) { countErr := errors.New("count failed") id := 1 _, err := BuildEdges(nil, func(_ dbx.Queryable, _, _ GenericCursor) (QueryCountResult, error) { return QueryCountResult{}, countErr }, []*int{&id}, cursorFunc) require.Error(t, err) assert.ErrorIs(t, err, countErr) }) } func TestExtractNodes(t *testing.T) { a, b := 1, 2 conn := &Connection[int]{ Edges: []*Edge[int]{ {Node: &a, Cursor: "c1"}, {Node: &b, Cursor: "c2"}, }, } nodes := ExtractNodes(conn) require.Len(t, nodes, 2) assert.Equal(t, 1, *nodes[0]) assert.Equal(t, 2, *nodes[1]) }