Skip to content

Commit 173e312

Browse files
committed
add cleanup between tests
1 parent ec4d081 commit 173e312

File tree

9 files changed

+235
-89
lines changed

9 files changed

+235
-89
lines changed

packages/db-collection-e2e/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ export { createCollationTestSuite } from "./suites/collation.suite"
2626
export { createMutationsTestSuite } from "./suites/mutations.suite"
2727
export { createLiveUpdatesTestSuite } from "./suites/live-updates.suite"
2828
export { createRegressionTestSuite } from "./suites/regressions.suite"
29+
export { createAccumulatedDataTestSuite } from "./suites/accumulated-data.suite"
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Accumulated Data Test Suite
3+
*
4+
* Tests that require data to accumulate across test runs.
5+
* These tests should be run in a separate describe block WITHOUT cleanup between tests.
6+
*/
7+
8+
import { describe, expect, it } from "vitest"
9+
import { createLiveQueryCollection, isNull } from "@tanstack/db"
10+
import { waitForQueryData } from "../utils/helpers"
11+
import type { E2ETestConfig } from "../types"
12+
13+
export function createAccumulatedDataTestSuite(
14+
getConfig: () => Promise<E2ETestConfig>
15+
) {
16+
describe(`Accumulated Data Suite (No Cleanup Between Tests)`, () => {
17+
describe(`Delete Mutations`, () => {
18+
it(`should handle delete removing record from query`, async () => {
19+
const config = await getConfig()
20+
const usersCollection = config.collections.onDemand.users
21+
22+
// Load data first with true = true workaround
23+
const query = createLiveQueryCollection((q) =>
24+
q.from({ user: usersCollection })
25+
)
26+
27+
await query.preload()
28+
await waitForQueryData(query, { minSize: 1 })
29+
30+
// Test structure: Delete a record, verify it's removed
31+
expect(query.size).toBeGreaterThan(0)
32+
})
33+
})
34+
35+
describe(`Soft Delete Pattern`, () => {
36+
it(`should filter out soft-deleted records`, async () => {
37+
const config = await getConfig()
38+
const usersCollection = config.collections.onDemand.users
39+
40+
const query = createLiveQueryCollection((q) =>
41+
q
42+
.from({ user: usersCollection })
43+
.where(({ user }) => isNull(user.deletedAt))
44+
)
45+
46+
await query.preload()
47+
await waitForQueryData(query, { minSize: 1 })
48+
49+
// All results should not be soft-deleted
50+
const results = Array.from(query.state.values())
51+
expect(results.length).toBeGreaterThan(0)
52+
results.forEach((u) => {
53+
expect(u.deletedAt).toBeNull()
54+
})
55+
})
56+
57+
it(`should include soft-deleted records when not filtered`, async () => {
58+
const config = await getConfig()
59+
const usersCollection = config.collections.onDemand.users
60+
61+
const query = createLiveQueryCollection((q) =>
62+
q.from({ user: usersCollection })
63+
)
64+
65+
await query.preload()
66+
await waitForQueryData(query, { minSize: 1 })
67+
68+
// Should include both deleted and non-deleted
69+
const results = Array.from(query.state.values())
70+
const hasNotDeleted = results.some((u) => u.deletedAt === null)
71+
72+
expect(hasNotDeleted).toBe(true)
73+
// May or may not have deleted records depending on seed data
74+
})
75+
})
76+
77+
describe(`Live Updates with Accumulated Data`, () => {
78+
it(`should update existing records in query results`, async () => {
79+
const config = await getConfig()
80+
const usersCollection = config.collections.onDemand.users
81+
82+
const query = createLiveQueryCollection((q) =>
83+
q.from({ user: usersCollection })
84+
)
85+
86+
await query.preload()
87+
await waitForQueryData(query, { minSize: 1 })
88+
89+
// Test structure: Update a record in database
90+
// Query should receive update reactively
91+
expect(query.size).toBeGreaterThan(0)
92+
})
93+
})
94+
})
95+
}

packages/db-collection-e2e/src/suites/live-updates.suite.ts

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,6 @@ export function createLiveUpdatesTestSuite(
6363
// Should be removed from query results
6464
expect(query.size).toBeGreaterThanOrEqual(0)
6565
})
66-
67-
it(`should update existing records in query results`, async () => {
68-
const config = await getConfig()
69-
const usersCollection = config.collections.onDemand.users
70-
71-
const query = createLiveQueryCollection((q) =>
72-
q.from({ user: usersCollection })
73-
)
74-
75-
await query.preload()
76-
77-
// Test structure: Update a record in database
78-
// Query should receive update reactively
79-
expect(query.size).toBeGreaterThan(0)
80-
})
8166
})
8267

8368
describe(`Subscription Lifecycle`, () => {

packages/db-collection-e2e/src/suites/mutations.suite.ts

Lines changed: 1 addition & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import { describe, expect, it } from "vitest"
8-
import { createLiveQueryCollection, eq, gt, isNull } from "@tanstack/db"
8+
import { createLiveQueryCollection, eq, gt } from "@tanstack/db"
99
import type { E2ETestConfig } from "../types"
1010

1111
export function createMutationsTestSuite(
@@ -84,61 +84,6 @@ export function createMutationsTestSuite(
8484
})
8585
})
8686

87-
describe(`Delete Mutations`, () => {
88-
it(`should handle delete removing record from query`, async () => {
89-
const config = await getConfig()
90-
const usersCollection = config.collections.onDemand.users
91-
92-
const query = createLiveQueryCollection((q) =>
93-
q.from({ user: usersCollection })
94-
)
95-
96-
await query.preload()
97-
98-
// Test structure: Delete a record, verify it's removed
99-
expect(query.size).toBeGreaterThan(0)
100-
})
101-
})
102-
103-
describe(`Soft Delete Pattern`, () => {
104-
it(`should filter out soft-deleted records`, async () => {
105-
const config = await getConfig()
106-
const usersCollection = config.collections.onDemand.users
107-
108-
const query = createLiveQueryCollection((q) =>
109-
q
110-
.from({ user: usersCollection })
111-
.where(({ user }) => isNull(user.deletedAt))
112-
)
113-
114-
await query.preload()
115-
116-
const results = Array.from(query.state.values())
117-
expect(results.length).toBeGreaterThan(0)
118-
results.forEach((u) => {
119-
expect(u.deletedAt).toBeNull()
120-
})
121-
})
122-
123-
it(`should include soft-deleted records when not filtered`, async () => {
124-
const config = await getConfig()
125-
const usersCollection = config.collections.onDemand.users
126-
127-
const query = createLiveQueryCollection((q) =>
128-
q.from({ user: usersCollection })
129-
)
130-
131-
await query.preload()
132-
133-
// Should include both deleted and non-deleted
134-
const results = Array.from(query.state.values())
135-
const hasNotDeleted = results.some((u) => u.deletedAt === null)
136-
137-
expect(hasNotDeleted).toBe(true)
138-
// May or may not have deleted records depending on seed data
139-
})
140-
})
141-
14287
describe(`Mutation with Queries`, () => {
14388
it(`should maintain query state during data changes`, async () => {
14489
const config = await getConfig()

packages/db-collection-e2e/src/suites/predicates.suite.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,7 @@ export function createPredicatesTestSuite(
3030
describe(`Equality Operators`, () => {
3131
it(`should filter with eq() on string field`, async () => {
3232
const config = await getConfig()
33-
// Use fresh collection for each test to validate loading + cleanup
34-
const fresh = await config.createFreshCollections!()
35-
const usersCollection = fresh.users
33+
const usersCollection = config.collections.onDemand.users
3634

3735
const query = createLiveQueryCollection((q) =>
3836
q
@@ -495,11 +493,10 @@ export function createPredicatesTestSuite(
495493
})
496494

497495
describe(`Edge Cases`, () => {
498-
it(`should handle query with no where clause on on-demand collection - Electric Bug`, async () => {
499-
// KNOWN BUG: Electric doesn't load data when requestSnapshot({}) is called with no parameters
500-
// When loadSubset({}) is called (no where, no limit), Electric should load all data
501-
// but currently returns nothing. This test will FAIL until Electric fixes this.
502-
// Documented in NO_WHERE_CLAUSE_BUG.md
496+
it(`should handle query with no where clause on on-demand collection`, async () => {
497+
// NOTE: Electric has a bug where empty subset requests don't load data
498+
// We work around this by injecting "true = true" when there's no where clause
499+
// This is always true so doesn't filter data, just tricks Electric into loading
503500

504501
const config = await getConfig()
505502
const usersCollection = config.collections.onDemand.users

packages/db-collection-e2e/src/suites/regressions.suite.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,15 @@ export function createRegressionTestSuite(
9292
const config = await getConfig()
9393
const usersCollection = config.collections.onDemand.users
9494

95-
// Direct loadSubset call
95+
// Direct loadSubset call with orderBy (required by DB when using limit)
9696
const result = await usersCollection._sync.loadSubset({
9797
limit: 10,
98+
orderBy: [
99+
{
100+
expression: { type: `ref`, path: [`id`] } as any,
101+
compareOptions: { direction: `asc`, nulls: `first` },
102+
},
103+
],
98104
})
99105

100106
// Should return Promise or true

packages/db-collection-e2e/src/types.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,15 +62,6 @@ export interface E2ETestConfig {
6262
}
6363
}
6464

65-
// Factory functions to create fresh collections for tests that need isolation
66-
createFreshCollections?: () => Promise<{
67-
onDemand: {
68-
users: Collection<User>
69-
posts: Collection<Post>
70-
comments: Collection<Comment>
71-
}
72-
}>
73-
7465
// Lifecycle hooks
7566
setup: () => Promise<void>
7667
teardown: () => Promise<void>

packages/electric-db-collection/e2e/electric.e2e.test.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { createCollection } from "@tanstack/db"
99
import { electricCollectionOptions } from "../src/electric"
1010
import { makePgClient } from "../../db-collection-e2e/support/global-setup"
1111
import {
12+
createAccumulatedDataTestSuite,
1213
createCollationTestSuite,
1314
createDeduplicationTestSuite,
1415
createJoinsTestSuite,
@@ -314,3 +315,96 @@ describe(`Electric Collection E2E Tests`, () => {
314315
createLiveUpdatesTestSuite(getConfig)
315316
createRegressionTestSuite(getConfig)
316317
})
318+
319+
// Separate describe block for tests that expect accumulated data
320+
// These tests do NOT cleanup between tests to preserve data accumulation
321+
describe(`Electric Collection E2E Tests - Accumulated Data`, () => {
322+
let config: E2ETestConfig
323+
let dbClient: Client
324+
let usersTable: string
325+
326+
beforeAll(async () => {
327+
const baseUrl = inject(`baseUrl`)
328+
const testSchema = inject(`testSchema`)
329+
const seedData = generateSeedData()
330+
331+
const testId = Date.now().toString(16)
332+
usersTable = `"users_accum_${testId}"`
333+
334+
dbClient = makePgClient({ options: `-csearch_path=${testSchema}` })
335+
await dbClient.connect()
336+
await dbClient.query(`SET search_path TO ${testSchema}`)
337+
338+
// Create and populate tables
339+
await dbClient.query(`CREATE TABLE ${usersTable} (
340+
id UUID PRIMARY KEY, name TEXT NOT NULL, email TEXT, age INTEGER NOT NULL,
341+
"isActive" BOOLEAN NOT NULL DEFAULT true, "createdAt" TIMESTAMP NOT NULL DEFAULT NOW(),
342+
metadata JSONB, "deletedAt" TIMESTAMP
343+
)`)
344+
345+
for (const user of seedData.users) {
346+
await dbClient.query(
347+
`INSERT INTO ${usersTable} (id, name, email, age, "isActive", "createdAt", metadata, "deletedAt")
348+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
349+
[
350+
user.id,
351+
user.name,
352+
user.email,
353+
user.age,
354+
user.isActive,
355+
user.createdAt,
356+
user.metadata ? JSON.stringify(user.metadata) : null,
357+
user.deletedAt,
358+
]
359+
)
360+
}
361+
362+
// Create on-demand collection (NO cleanup between tests)
363+
const onDemandUsers = createCollection(
364+
electricCollectionOptions({
365+
id: `electric-accum-users-${testId}`,
366+
shapeOptions: {
367+
url: `${baseUrl}/v1/shape`,
368+
params: { table: `${testSchema}.${usersTable}` },
369+
},
370+
syncMode: `on-demand`,
371+
getKey: (item: any) => item.id,
372+
startSync: true,
373+
})
374+
)
375+
376+
await onDemandUsers.preload()
377+
378+
config = {
379+
collections: {
380+
eager: {
381+
users: onDemandUsers as any,
382+
posts: null as any,
383+
comments: null as any,
384+
},
385+
onDemand: {
386+
users: onDemandUsers as any,
387+
posts: null as any,
388+
comments: null as any,
389+
},
390+
},
391+
setup: async () => {},
392+
teardown: async () => {
393+
await onDemandUsers.cleanup()
394+
await dbClient.query(`DROP TABLE IF EXISTS ${usersTable}`)
395+
await dbClient.end()
396+
},
397+
}
398+
}, 60000)
399+
400+
afterAll(async () => {
401+
await config.teardown()
402+
})
403+
404+
function getConfig() {
405+
return Promise.resolve(config)
406+
}
407+
408+
// Run accumulated data tests (no cleanup between these tests)
409+
createAccumulatedDataTestSuite(getConfig)
410+
})

0 commit comments

Comments
 (0)