Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

87 lines
3.1 KiB
Go

package db
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// resolvedFor builds the resolved sort terms for a spec string, for renderer
// tests that need the []ResolvedSort the stores pass to the query builder.
func resolvedFor(t *testing.T, spec string) []ResolvedSort {
t.Helper()
keys, err := ParseSortSpec(spec)
require.NoError(t, err)
return ResolveSort(SessionFilter{Sort: keys})
}
// TestOrderByClause_MultiKey locks the multi-column ORDER BY rendering,
// including the implicit id tie-breaker in the last term's direction.
func TestOrderByClause_MultiKey(t *testing.T) {
rs := resolvedFor(t, "messages:asc,started:desc")
b := NewQueryBuilder(SQLiteQueryDialect(), 0)
assert.Equal(t,
"ORDER BY message_count ASC, "+
"COALESCE(NULLIF(started_at, ''), created_at) DESC, id DESC",
b.OrderByClause(rs, SessionFilter{}))
bpg := NewQueryBuilder(PostgresQueryDialect(), 0)
assert.Equal(t,
"ORDER BY message_count ASC, "+
"COALESCE(started_at, created_at) DESC, id DESC",
bpg.OrderByClause(rs, SessionFilter{}))
}
// TestOrderByClause_IDOnly keeps the single id-sort form free of a duplicate id
// tie-breaker.
func TestOrderByClause_IDOnly(t *testing.T) {
rs := resolvedFor(t, "id:desc")
b := NewQueryBuilder(SQLiteQueryDialect(), 0)
assert.Equal(t, "ORDER BY id DESC", b.OrderByClause(rs, SessionFilter{}))
}
// TestCursorPredicate_MultiKey locks the lexicographic OR-expansion that backs
// keyset pagination under mixed per-key directions, plus the per-kind casts.
func TestCursorPredicate_MultiKey(t *testing.T) {
rs := resolvedFor(t, "messages:asc,started:desc")
values := []any{int64(5), "2024-01-01T00:00:00Z"}
b := NewQueryBuilder(SQLiteQueryDialect(), 0)
gotSQLite := b.CursorPredicate(rs, SessionFilter{}, values, "sid")
assert.Equal(t,
"((message_count > ?) OR "+
"(message_count = ? AND "+
"COALESCE(NULLIF(started_at, ''), created_at) < ?) OR "+
"(message_count = ? AND "+
"COALESCE(NULLIF(started_at, ''), created_at) = ? AND id < ?))",
gotSQLite)
// Six bound params: one comparison at level 0, two at level 1, three at
// level 2 (the id tie-break being the last).
assert.Len(t, b.Args(), 6)
bpg := NewQueryBuilder(PostgresQueryDialect(), 0)
gotPG := bpg.CursorPredicate(rs, SessionFilter{}, values, "sid")
assert.Equal(t,
"((message_count > $1::bigint) OR "+
"(message_count = $2::bigint AND "+
"COALESCE(started_at, created_at) < $3::timestamptz) OR "+
"(message_count = $4::bigint AND "+
"COALESCE(started_at, created_at) = $5::timestamptz AND id < $6))",
gotPG)
}
// TestCursorPredicate_SingleKeyEquivalentToRowValue documents that the
// single-key OR-expansion is the logical equivalent of the previous row-value
// comparison: value-compare OR (equal AND id-compare).
func TestCursorPredicate_SingleKeyRecent(t *testing.T) {
rs := resolvedFor(t, "recent:desc")
b := NewQueryBuilder(SQLiteQueryDialect(), 0)
got := b.CursorPredicate(rs, SessionFilter{}, []any{"2024-05-01T00:00:00Z"}, "sid")
activity := "COALESCE(NULLIF(ended_at, ''), NULLIF(started_at, ''), created_at)"
assert.Equal(t,
"(("+activity+" < ?) OR ("+activity+" = ? AND id < ?))",
got)
}