Files
cursor/query_count_test.go
T

178 lines
5.1 KiB
Go

package cursor
import (
"context"
"database/sql"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/go-jet/jet/v2/mysql"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestValueMethods(t *testing.T) {
RegisterColumn(Scratch.ID)
intVal, err := NewInt64Value(7, Scratch.ID).Value()
require.NoError(t, err)
assert.Equal(t, int64(7), intVal)
uintVal, err := NewUint64Value(9, Spare.ID).Value()
require.NoError(t, err)
assert.Equal(t, uint64(9), uintVal)
}
func TestCursorString(t *testing.T) {
RegisterColumn(User.ID)
c := NewCursor(NewInt64Value(3, User.ID), User.ID, OrderAscending)
assert.JSONEq(t, `{"index":{"key":{"table":"user","column":"id"},"val":3},"order_col":{"table":"user","column":"id"},"order_dir":"ASC"}`, c.String())
}
func TestUsesTupleOrderingEdgeCases(t *testing.T) {
assert.False(t, (*Cursor[mysql.IntegerExpression, mysql.ColumnInteger])(nil).UsesTupleOrdering())
RegisterColumn(User.ID)
sameCol := NewCursor(NewInt64Value(0, User.ID), User.ID, OrderDescending)
assert.False(t, sameCol.UsesTupleOrdering())
}
func TestPaginateWrapperFuncs(t *testing.T) {
RegisterColumn(User.ID, Meeting.StartTime)
intCursor := NewCursor(NewInt64Value(1, User.ID), User.ID, OrderDescending)
sql := User.SELECT(User.AllColumns).WHERE(PaginateIntConds(intCursor)).DebugSql()
assert.Contains(t, sql, "user.id < 1")
strCursor := NewCursor(NewStringValue("x", MeetingRoom.ID), MeetingRoom.ID, OrderDescending)
RegisterColumn(MeetingRoom.ID)
sql = MeetingRoom.SELECT(MeetingRoom.AllColumns).WHERE(PaginateUUIDConds(strCursor)).DebugSql()
assert.Contains(t, sql, "meeting_room.id < 'x'")
tsCursor := NewCursor(
NewTimestampValue(mustTime("2026-01-01T00:00:00Z"), Meeting.StartTime),
Meeting.StartTime,
OrderDescending,
)
sql = Meeting.SELECT(Meeting.AllColumns).WHERE(PaginateTimestampConds(tsCursor)).DebugSql()
assert.Contains(t, sql, "meeting.start_time <")
}
func TestNewCursorFromJSONError(t *testing.T) {
RegisterColumn(User.ID)
_, err := NewCursorFromJSON(NewInt64Value(0, User.ID), []byte(`{invalid`))
require.Error(t, err)
}
func TestColWrongTypePanics(t *testing.T) {
RegisterColumn(User.ID)
// Key points at user.id (integer) but we construct StringValue for it.
sv := &StringValue{Key: NewColumnKey(User.ID), Val: "x"}
assert.Panics(t, func() { sv.Col() })
}
func TestQueryCount(t *testing.T) {
registerMeetingCursorColumns(t)
db, mock, err := sqlmock.New()
require.NoError(t, err)
t.Cleanup(func() { db.Close() })
rows := sqlmock.NewRows([]string{
"QueryCountResult.Total",
"QueryCountResult.Before",
"QueryCountResult.After",
}).AddRow(10, 3, 5)
mock.ExpectQuery("SELECT").WillReturnRows(rows)
when := mustTime("2026-06-01T09:00:00Z")
start := NewCursor(
NewStringValue("", Meeting.ID),
Meeting.StartTime,
OrderDescending,
).CopyWithVals(
NewStringValue("meeting-uuid-1", Meeting.ID),
NewTimestampValue(when, Meeting.StartTime),
)
end := start.CopyWithVals(
NewStringValue("meeting-uuid-2", Meeting.ID),
NewTimestampValue(when, Meeting.StartTime),
)
sqlo := &queryableDB{db: db}
res, err := QueryCount(sqlo, Meeting.ID, Meeting, mysql.Bool(true), start, end)
require.NoError(t, err)
assert.Equal(t, QueryCountResult{Total: 10, Before: 3, After: 5}, res)
require.NoError(t, mock.ExpectationsWereMet())
}
func TestQueryCountError(t *testing.T) {
registerMeetingCursorColumns(t)
db, mock, err := sqlmock.New()
require.NoError(t, err)
t.Cleanup(func() { db.Close() })
mock.ExpectQuery("SELECT").WillReturnError(assert.AnError)
when := mustTime("2026-06-01T09:00:00Z")
cursor := NewCursor(
NewStringValue("", Meeting.ID),
Meeting.StartTime,
OrderDescending,
).CopyWithVals(
NewStringValue("meeting-uuid-1", Meeting.ID),
NewTimestampValue(when, Meeting.StartTime),
)
sqlo := &queryableDB{db: db}
_, err = QueryCount(sqlo, Meeting.ID, Meeting, mysql.Bool(true), cursor, cursor)
require.Error(t, err)
assert.Contains(t, err.Error(), "failed to query count")
}
func TestBuildQueryCountFnInvoke(t *testing.T) {
registerMeetingCursorColumns(t)
db, mock, err := sqlmock.New()
require.NoError(t, err)
t.Cleanup(func() { db.Close() })
rows := sqlmock.NewRows([]string{
"QueryCountResult.Total",
"QueryCountResult.Before",
"QueryCountResult.After",
}).AddRow(1, 0, 0)
mock.ExpectQuery("SELECT").WillReturnRows(rows)
when := mustTime("2026-06-01T09:00:00Z")
cursor := NewCursor(
NewStringValue("", Meeting.ID),
Meeting.StartTime,
OrderDescending,
).CopyWithVals(
NewStringValue("meeting-uuid-1", Meeting.ID),
NewTimestampValue(when, Meeting.StartTime),
)
fn := BuildQueryCountFn(Meeting.ID, Meeting, mysql.Bool(true))
res, err := fn(&queryableDB{db: db}, cursor, cursor)
require.NoError(t, err)
assert.Equal(t, 1, res.Total)
}
// queryableDB implements dbx.Queryable for tests.
type queryableDB struct {
db *sql.DB
}
func (q *queryableDB) QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error) {
return q.db.QueryContext(ctx, query, args...)
}
func (q *queryableDB) Query(query string, args ...any) (*sql.Rows, error) {
return q.db.Query(query, args...)
}