Skip to content

Commit 3407a55

Browse files
committed
improve test
1 parent 053663e commit 3407a55

File tree

1 file changed

+51
-19
lines changed

1 file changed

+51
-19
lines changed

packages/db/tests/collection.test.ts

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,13 +1250,14 @@ describe(`Collection with schema validation`, () => {
12501250
})
12511251

12521252
it(`should not block user actions when keys are recently synced`, async () => {
1253-
// This test reproduces the issue where rapid user actions get blocked
1253+
// This test reproduces the ACTUAL issue where rapid user actions get blocked
12541254
// when optimistic updates back up with slow sync responses
12551255
const txResolvers: Array<() => void> = []
12561256
const emitter = mitt()
1257+
const changeEvents: Array<any> = []
12571258

12581259
const mutationFn = vi.fn().mockImplementation(async ({ transaction }) => {
1259-
// Simulate server operation that can be controlled
1260+
// Simulate SLOW server operation - this is key to reproducing the issue
12601261
return new Promise((resolve) => {
12611262
txResolvers.push(() => {
12621263
emitter.emit(`sync`, transaction.mutations)
@@ -1282,7 +1283,7 @@ describe(`Collection with schema validation`, () => {
12821283
commit()
12831284
markReady()
12841285

1285-
// Listen for sync events
1286+
// Listen for sync events - this triggers the problematic batching
12861287
// @ts-expect-error don't trust mitt's typing
12871288
emitter.on(`*`, (_, changes: Array<PendingMutation>) => {
12881289
begin()
@@ -1300,36 +1301,67 @@ describe(`Collection with schema validation`, () => {
13001301
onUpdate: mutationFn,
13011302
})
13021303

1304+
// Listen to change events to verify they're emitted (this was the actual problem)
1305+
collection.subscribeChanges((changes) => {
1306+
changeEvents.push(...changes)
1307+
})
1308+
13031309
await collection.stateWhenReady()
13041310

1305-
// Step 1: First user action - should work fine
1311+
// CRITICAL: Simulate rapid clicking WITHOUT waiting for transactions to complete
1312+
// This is what actually triggers the bug - multiple pending transactions
1313+
1314+
// Step 1: First click
13061315
const tx1 = collection.update(1, (draft) => {
13071316
draft.checked = true
13081317
})
13091318
expect(collection.state.get(1)?.checked).toBe(true)
1310-
expect(collection.optimisticUpserts.has(1)).toBe(true)
1319+
const initialEventCount = changeEvents.length
1320+
1321+
// Step 2: Second click immediately (before first completes)
1322+
const tx2 = collection.update(1, (draft) => {
1323+
draft.checked = false
1324+
})
1325+
expect(collection.state.get(1)?.checked).toBe(false)
1326+
1327+
// Step 3: Third click immediately (before others complete)
1328+
const tx3 = collection.update(1, (draft) => {
1329+
draft.checked = true
1330+
})
1331+
expect(collection.state.get(1)?.checked).toBe(true)
1332+
1333+
// CRITICAL TEST: Verify events are still being emitted for rapid user actions
1334+
// Before the fix, these would be batched and UI would freeze
1335+
expect(changeEvents.length).toBeGreaterThan(initialEventCount)
1336+
expect(mutationFn).toHaveBeenCalledTimes(3)
13111337

1312-
// Step 2: Complete the transaction to trigger sync
1338+
// Now complete the first transaction to trigger sync and batching
13131339
txResolvers[0]?.()
13141340
await tx1.isPersisted.promise
13151341

1316-
// At this point, key 1 should be in recentlySyncedKeys due to the sync event
1342+
// Step 4: More rapid clicks after sync starts (this is where the bug occurred)
1343+
const eventCountBeforeRapidClicks = changeEvents.length
13171344

1318-
// Step 3: Try another user action on the same key immediately
1319-
// This should NOT be blocked (this was the bug)
1320-
const tx2 = collection.update(1, (draft) => {
1345+
const tx4 = collection.update(1, (draft) => {
13211346
draft.checked = false
13221347
})
1348+
const tx5 = collection.update(1, (draft) => {
1349+
draft.checked = true
1350+
})
13231351

1324-
// The optimistic state should be updated
1325-
expect(collection.state.get(1)?.checked).toBe(false)
1326-
expect(collection.optimisticUpserts.has(1)).toBe(true)
1327-
1328-
// The mutation function should have been called
1329-
expect(mutationFn).toHaveBeenCalledTimes(2)
1352+
// CRITICAL: Verify that even after sync/batching starts, user actions still emit events
1353+
expect(changeEvents.length).toBeGreaterThan(eventCountBeforeRapidClicks)
1354+
expect(collection.state.get(1)?.checked).toBe(true) // Last action should win
13301355

1331-
// Clean up
1332-
txResolvers[1]?.()
1333-
await tx2.isPersisted.promise
1356+
// Clean up remaining transactions
1357+
for (let i = 1; i < txResolvers.length; i++) {
1358+
txResolvers[i]?.()
1359+
}
1360+
await Promise.all([
1361+
tx2.isPersisted.promise,
1362+
tx3.isPersisted.promise,
1363+
tx4.isPersisted.promise,
1364+
tx5.isPersisted.promise,
1365+
])
13341366
})
13351367
})

0 commit comments

Comments
 (0)