test(sync): align import filtering integration expectations

This commit is contained in:
Johannes Millan 2026-05-11 11:12:27 +02:00
parent b56c997874
commit bca1dba174
2 changed files with 22 additions and 23 deletions

View file

@ -583,14 +583,14 @@ describe('Service Logic Integration', () => {
* 4. Client B comes online and uploads its ops to server
* 5. Client A downloads B's ops
*
* Expected: B's ops should be KEPT because the import has no knowledge of
* client-b at all (independent timeline). The import was created in complete
* ignorance of client-b, so it can't claim to supersede client-b's ops.
* Expected: B's ops should be filtered because they lack causal knowledge of
* the import. SYNC_IMPORT is a clean-slate operation, so CONCURRENT ops without
* proof that they saw the import are discarded.
*
* This test verifies the unknown-client exception works correctly at the
* integration level (through the full sync service flow).
* This test verifies the clean-slate filtering works correctly at the
* integration level through the full sync service flow.
*/
it('should keep CONCURRENT ops from unknown client after SYNC_IMPORT', async (): Promise<void> => {
it('should filter CONCURRENT ops from unknown client after SYNC_IMPORT', async (): Promise<void> => {
// 1. Store already has a SYNC_IMPORT from a previous sync (Client A imported)
const importOp: Operation = {
id: 'import-op-1',
@ -649,12 +649,9 @@ describe('Service Logic Integration', () => {
// 4. Download and process remote ops
await syncService.downloadRemoteOps(mockProvider);
// 5. EXPECTED: Both ops should be KEPT - import has no knowledge of client-b
// (independent timeline, unknown client exception)
expect(applierSpy.applyOperations.calls.count()).toBeGreaterThan(0);
const appliedOps = applierSpy.applyOperations.calls.mostRecent().args[0];
expect(appliedOps.find((op: Operation) => op.id === 'offline-op-1')).toBeDefined();
expect(appliedOps.find((op: Operation) => op.id === 'offline-op-2')).toBeDefined();
// 5. EXPECTED: Both ops should be filtered - neither proves knowledge of
// the clean-slate import.
expect(applierSpy.applyOperations).not.toHaveBeenCalled();
});
/**
@ -721,8 +718,8 @@ describe('Service Logic Integration', () => {
* Even if client B's clock is ahead (ops have future timestamps), vector clocks
* correctly identify that B had no knowledge of the import.
*
* Note: The import clock includes client-b so the unknown-client exception
* does not apply this tests filtering of a KNOWN client's CONCURRENT ops.
* Note: The import clock includes client-b, making this an explicit known
* stale-client case.
*/
it('should filter offline ops even when client clock was ahead (clock drift)', async (): Promise<void> => {
// 1. Store has SYNC_IMPORT (import knows about client-b from prior communication)

View file

@ -376,11 +376,11 @@ describe('Vector Clock Import Reset Integration', () => {
);
/**
* Verify that pre-import ops from unknown clients (never communicated with import
* client) are KEPT. The import has no knowledge of these clients, so it can't
* claim to supersede their ops (independent timelines).
* Verify that pre-import ops from unknown clients are filtered, while
* post-import ops with minimal clocks are kept by the import-client-counter
* exception.
*/
it('should keep pre-import ops from clients unknown to the import', async () => {
it('should filter pre-import unknown-client ops and keep post-import minimal-clock ops', async () => {
const clientA = new SimulatedClient('client-aaa', storeService);
const clientB = new SimulatedClient('client-bbb', storeService);
const clientC = new SimulatedClient('client-ccc', storeService);
@ -476,11 +476,13 @@ describe('Vector Clock Import Reset Integration', () => {
postImportOpB,
]);
// Pre-import op from C should be KEPT (unknown client - import has no entry for C)
// Post-import op from B should also be kept (import-client-counter exception)
expect(result.invalidatedOps.length).toBe(0);
expect(result.validOps.length).toBe(2);
expect(result.validOps.find((op) => op.entityId === 'taskC-pre')).toBeDefined();
// Pre-import op from C is filtered because it lacks import knowledge.
// Post-import op from B is kept by the import-client-counter exception.
expect(result.invalidatedOps.length).toBe(1);
expect(result.validOps.length).toBe(1);
expect(
result.invalidatedOps.find((op) => op.entityId === 'taskC-pre'),
).toBeDefined();
expect(result.validOps.find((op) => op.entityId === 'taskB-post')).toBeDefined();
});
});