Overview
Implement comprehensive end-to-end tests for the query-driven sync feature with on-demand collection loading (PR #763, RFC #676). These tests will verify that predicate push-down, deduplication, pagination, joins, and other distributed system behaviors work correctly across different collection types and syncModes.
Related Links:
These new e2e tests will branch off PR #763 and be merged into it.
Goals
- Create a shared e2e test suite that can be reused across different collection implementations
- Test critical distributed systems scenarios: concurrent loadSubset calls, race conditions, deduplication
- Verify predicate push-down works correctly with various query patterns
- Test pagination, ordering, and multi-collection joins
- Catch integration bugs like those found during early testing (see Known Bugs section)
- Keep execution time under 5 minutes for CI/CD
Architecture
Package Structure
Create a new package: @tanstack/db-collection-e2e
This package will export:
- Individual test scenario groups organized by feature
- Standard seed data schema and fixtures
- Utility functions for common assertions
- Configuration types/interfaces
Test Organization
Tests will be organized into feature-based scenario groups:
- Predicates Suite - Basic where clause functionality
- Pagination Suite - Order by, limit, offset, setWindow
- Joins Suite - Single and multi-collection joins
- Deduplication Suite - Concurrent loadSubset scenarios
- Collation Suite - String comparison configuration
- Mutations Suite - Data mutations with on-demand mode
- Live Updates Suite (optional) - Reactive updates for sync-enabled collections
- Regression Suite - Explicit tests for known bugs
Collection Integration
Each collection package (e.g., electric-db-collection, query-db-collection) will:
- Import the shared test suite
- Provide collection instances configured for both eager and on-demand syncModes
- Implement collection-specific setup/teardown hooks
- Choose which optional test suites to run
Example structure:
packages/
db-collection-e2e/ # Shared test suite package
src/
suites/
predicates.test.ts
pagination.test.ts
joins.test.ts
deduplication.test.ts
collation.test.ts
mutations.test.ts
live-updates.test.ts (optional)
regressions.test.ts
fixtures/
seed-data.ts
test-schema.ts
utils/
assertions.ts
types.ts
index.ts
electric-db-collection/
e2e/
setup.ts # Docker, Postgres, Electric setup
electric.e2e.test.ts # Imports and runs shared suites
query-db-collection/
e2e/
setup.ts # Mock backend setup
query.e2e.test.ts # Imports and runs shared suites
Standard Test Data Schema
Entities and Relationships
Design a schema that exercises edge cases across various data types:
// Users tableinterfaceUser{id: string;// UUIDname: string;// For collation testingemail: string|null;// Nullable fieldage: number;// Numeric comparisonsisActive: boolean;// Boolean predicatescreatedAt: Date;// Date comparisonsmetadata: object|null;// JSON field (if supported)deletedAt: Date|null;// Soft delete pattern}// Posts tableinterfacePost{id: string;userId: string;// Foreign key to Usertitle: string;content: string|null;viewCount: number;publishedAt: Date|null;deletedAt: Date|null;}// Comments tableinterfaceComment{id: string;postId: string;// Foreign key to PostuserId: string;// Foreign key to Usertext: string;createdAt: Date;deletedAt: Date|null;}Seed Data Volume
- Users: ~100 records
- Posts: ~100 records (distributed across users)
- Comments: ~100 records (distributed across posts)
This provides enough data to test pagination effectively while keeping tests fast.
Data Distribution
Ensure data includes:
- Mix of null and non-null values
- Various string cases for collation testing (uppercase, lowercase, special chars)
- Date ranges (past, present, future)
- Boolean distributions (true/false/null if applicable)
- Numeric ranges (negative, zero, positive, large numbers)
- Some soft-deleted records (deletedAt not null)
Test Configuration Interface
interfaceE2ETestConfig{// Collection instances configured for testingcollections: {eager: {users: Collection<User>;posts: Collection<Post>;comments: Collection<Comment>;};onDemand: {users: Collection<User>;posts: Collection<Post>;comments: Collection<Comment>;};};// Lifecycle hookssetup: ()=>Promise<void>;teardown: ()=>Promise<void>;// Per-test hooks (optional)beforeEach?: ()=>Promise<void>;afterEach?: ()=>Promise<void>;}Note: Collections handle their own mutation logic internally, so no separate mutator is needed in the config.
Test Scenario Groups
1. Predicates Suite
Test basic predicate functionality across all data types:
Test cases:
eq() with various types (string, number, boolean, date, UUID, null)ne() with various typesgt(), gte(), lt(), lte() with numbers and datesin() with arraysisNull() and isNotNull()- Complex boolean logic (AND, OR combinations)
- Nested predicates
Assertions:
- Query returns correct data matching predicates
- Only necessary data is loaded (check collection state)
- No errors thrown
2. Pagination Suite
Test ordering, limits, offsets, and window management:
Test cases:
- Basic orderBy (ascending/descending)
- Multiple orderBy fields
- Limit without offset
- Limit with offset
liveQuery.utils.setWindow() - changing windowssetWindow() while data is loading- Overlapping windows (page 2 before page 1 completes)
- Edge cases: limit=0, offset beyond dataset, negative values
Assertions:
- Correct page of data returned
- Proper ordering maintained
- Only requested data loaded (not entire dataset)
3. Joins Suite
Test multi-collection joins with various syncMode combinations:
Test cases:
- Two-collection join (Users + Posts)
- Three-collection join (Users + Posts + Comments)
- Mixed syncModes: one on-demand, one eager
- Both collections on-demand
- Predicates on joined collections (verify pushdown)
- Ordering across joined collections
- Pagination on joined results
setWindow() on joins requiring loads from multiple collections
Assertions:
- Correct joined data returned
- Each collection only loads required subset (predicate pushdown working)
- Query result matches expected join
- No extra data loaded
Example test:
// Join users and posts where userId = 123// Verify only user 123's posts loaded, not all posts
4. Deduplication Suite
Test concurrent loadSubset calls and deduplication behavior:
Test cases:
- Two queries with identical predicates calling loadSubset simultaneously
- Overlapping predicates (one is subset of another)
- Queries arriving while data is still loading
- Multiple concurrent queries with different predicates
- Deduplication with limit/offset variations
Assertions:
- Use deduplication callback to count actual vs deduplicated loads
- Verify expected number of backend requests
- All queries receive correct data
- No race conditions or data corruption
5. Collation Suite
Test string collation configuration:
Test cases:
- Default collation behavior
- Custom
defaultStringCollation at collection level - Custom collation at query level
- Collation inheritance in nested queries
- String comparisons with different collations (case-sensitive vs case-insensitive)
Assertions:
- String predicates respect collation settings
- Correct data returned based on collation
- Query-level collation overrides collection-level
6. Mutations Suite
Test mutations with on-demand syncMode:
Test cases:
- Mutate loaded data (verify sync back)
- Create new record (verify appears in matching queries)
- Update record to match/unmatch query predicates
- Delete record
- Concurrent mutations
Assertions:
- Mutations to loaded records sync correctly
- Query results update reactively after mutations
- Cannot mutate records that aren't loaded (verify error/behavior)
Note: Mutation logic is collection-specific, handled internally.
7. Live Updates Suite (Optional)
For collections that support sync, test reactive updates:
Test cases:
- Load subset via query
- Mutate data on backend (outside client)
- Verify query reactively updates
- Updates during active loadSubset
- Multiple queries watching same data
Assertions:
- Query data updates when backend changes
- Updates don't trigger unnecessary reloads
- Correct data maintained throughout
8. Regression Suite
Explicit tests for known bugs found during development:
Critical bugs to test:
Missing subset_ params bug
- Query with no
where or limit should include proper params - Server shouldn't treat as normal shape request
- Test: Create query without predicates, verify URL params
eq(deletedAt, null) SQL syntax error
- Test:
eq(deletedAt, null) should work without SQL errors - Verify correct records returned
eq(id, uuid) syntax error
- Test:
eq(id, '{uuid-value}') should work - Verify UUID fields work in predicates
Unnecessary offset in URL
- Verify offset only added when needed
- Test URL construction
JSON parse inefficiency
- Ensure snapshot responses aren't parsed multiple times
- (May need internal inspection or performance monitoring)
9. Progressive Mode (Deferred)
Not included in initial implementation, but placeholder for future:
- Test mode transition from on-demand → eager when full sync completes
- Verify subset loads work while background sync proceeds
Infrastructure Setup
Docker Orchestration
Follow Electric's e2e pattern (see generated docs):
Services needed:
- PostgreSQL (port 54321)
- Electric server (port 3000)
- tmpfs for performance
- Health checks with proper timeouts
docker-compose.yml example:
services:
postgres:
image: postgres:14-alpineenvironment:
POSTGRES_DB: electricPOSTGRES_USER: postgresPOSTGRES_PASSWORD: passwordports:
- "54321:5432"tmpfs: /var/lib/postgresql/datahealthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]interval: 2stimeout: 10sretries: 5electric:
image: electricsql/electric:latestenvironment:
DATABASE_URL: postgresql://postgres:password@postgres:5432/electricELECTRIC_WRITE_TO_PG_MODE: direct_writesPG_PROXY_PORT: "65432"ports:
- "3000:3000"depends_on:
postgres:
condition: service_healthyhealthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:3000/health || exit 1"]interval: 2stimeout: 10sretries: 5
Database Isolation
Use schema-based isolation with unique table names per test:
// Shared database: electric_test schema// Unique table names: `table_for_{taskId}_{randomSuffix}`This prevents test collisions while allowing parallel execution within Vitest's constraints.
Vitest Configuration
Critical settings:
exportdefaultdefineConfig({test: {fileParallelism: false,// Serial execution for shared DBglobalSetup: './e2e/global-setup.ts',timeout: 30000,// Extended for Docker operations}})Global Setup
Implement health check for Docker services:
// global-setup.tsexportasyncfunctionsetup(){// Wait for Postgres and Electric to be healthyawaitwaitForHealthCheck('http://localhost:3000/health')awaitwaitForPostgres('postgresql://postgres:password@localhost:54321/electric')}Test Fixtures
Use Vitest's test.extend() for composable fixtures:
consttestWithDb=test.extend({db: async({},use)=>{constdb=awaitsetupDatabase()awaituse(db)awaitcleanupDatabase(db)}})consttestWithCollections=testWithDb.extend({collections: async({ db },use)=>{constcollections=awaitseedAndCreateCollections(db)awaituse(collections)}})Test Flow Pattern
Typical test structure:
test('should load correct data with predicates',async({ collections })=>{// 1. Seed data happens in fixture setup// 2. Create query with predicatesconstquery=collections.onDemand.users.liveQuery({where: eq(users.age,25)})// 3. Await preloadawaitquery.preload()// 4. Assert correct dataconstresult=query.getResult()expect(result).toHaveLength(expectedCount)expect(result.every(u=>u.age===25)).toBe(true)// 5. Verify only necessary data loadedconstloadedIds=getLoadedUserIds(collections.onDemand.users)expect(loadedIds).toEqual(expectedUserIds)})Mock Backend (Query Collection)
For query-db-collection, mock the backend fetch:
// Mock TanStack Query backendconstmockBackend={fetchUsers: vi.fn(async({ where, orderBy, limit, offset })=>{// Return filtered/paginated seed datareturnfilterData(seedData.users,{ where, orderBy, limit, offset })})}Implementation Checklist
Phase 1: Infrastructure
Phase 2: Core Test Suites
Phase 3: Additional Suites
Phase 4: Collection Integration
Phase 5: CI/CD
Success Criteria
Future Enhancements
- Progressive mode transition testing
- Performance benchmarks
- Subscription lifecycle edge cases (unsubscribe during load)
- Network failure scenarios
- More complex join patterns (self-joins, multiple paths)
References
Notes for Implementation
- Start small: Implement infrastructure + Predicates Suite first to validate approach
- Copy Electric patterns: Leverage proven patterns from Electric's e2e setup
- Keep tests strict: No retries, fast timeouts - rely on setup hooks for consistency
- Test through public API: Don't expose internal state unless necessary
- Document as you go: Add examples for future collections to reference
Overview
Implement comprehensive end-to-end tests for the query-driven sync feature with on-demand collection loading (PR #763, RFC #676). These tests will verify that predicate push-down, deduplication, pagination, joins, and other distributed system behaviors work correctly across different collection types and syncModes.
Related Links:
~/programs/electric/packages/typescript-client(see generated docs in this repo)These new e2e tests will branch off PR #763 and be merged into it.
Goals
Architecture
Package Structure
Create a new package:
@tanstack/db-collection-e2eThis package will export:
Test Organization
Tests will be organized into feature-based scenario groups:
Collection Integration
Each collection package (e.g.,
electric-db-collection,query-db-collection) will:Example structure:
Standard Test Data Schema
Entities and Relationships
Design a schema that exercises edge cases across various data types:
Seed Data Volume
This provides enough data to test pagination effectively while keeping tests fast.
Data Distribution
Ensure data includes:
Test Configuration Interface
Note: Collections handle their own mutation logic internally, so no separate mutator is needed in the config.
Test Scenario Groups
1. Predicates Suite
Test basic predicate functionality across all data types:
Test cases:
eq()with various types (string, number, boolean, date, UUID, null)ne()with various typesgt(),gte(),lt(),lte()with numbers and datesin()with arraysisNull()andisNotNull()Assertions:
2. Pagination Suite
Test ordering, limits, offsets, and window management:
Test cases:
liveQuery.utils.setWindow()- changing windowssetWindow()while data is loadingAssertions:
3. Joins Suite
Test multi-collection joins with various syncMode combinations:
Test cases:
setWindow()on joins requiring loads from multiple collectionsAssertions:
Example test:
4. Deduplication Suite
Test concurrent loadSubset calls and deduplication behavior:
Test cases:
Assertions:
5. Collation Suite
Test string collation configuration:
Test cases:
defaultStringCollationat collection levelAssertions:
6. Mutations Suite
Test mutations with on-demand syncMode:
Test cases:
Assertions:
Note: Mutation logic is collection-specific, handled internally.
7. Live Updates Suite (Optional)
For collections that support sync, test reactive updates:
Test cases:
Assertions:
8. Regression Suite
Explicit tests for known bugs found during development:
Critical bugs to test:
Missing subset_ params bug
whereorlimitshould include proper paramseq(deletedAt, null) SQL syntax error
eq(deletedAt, null)should work without SQL errorseq(id, uuid) syntax error
eq(id, '{uuid-value}')should workUnnecessary offset in URL
JSON parse inefficiency
9. Progressive Mode (Deferred)
Not included in initial implementation, but placeholder for future:
Infrastructure Setup
Docker Orchestration
Follow Electric's e2e pattern (see generated docs):
Services needed:
docker-compose.yml example:
Database Isolation
Use schema-based isolation with unique table names per test:
This prevents test collisions while allowing parallel execution within Vitest's constraints.
Vitest Configuration
Critical settings:
Global Setup
Implement health check for Docker services:
Test Fixtures
Use Vitest's
test.extend()for composable fixtures:Test Flow Pattern
Typical test structure:
Mock Backend (Query Collection)
For
query-db-collection, mock the backend fetch:Implementation Checklist
Phase 1: Infrastructure
@tanstack/db-collection-e2epackagePhase 2: Core Test Suites
Phase 3: Additional Suites
Phase 4: Collection Integration
electric-db-collectionquery-db-collectionPhase 5: CI/CD
Success Criteria
electric-db-collectionandquery-db-collectionFuture Enhancements
References
README_E2E_TESTS.md,ELECTRIC_E2E_PATTERNS.md, etc. in this repo~/programs/electric/packages/typescript-clientNotes for Implementation