Skip to content

Commit 40d2b33

Browse files
committed
Test cleanup and better documentation.
1 parent 39721df commit 40d2b33

File tree

4 files changed

+118
-88
lines changed

4 files changed

+118
-88
lines changed

packages/firestore/src/remote/watch_change.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -588,9 +588,9 @@ export class WatchChangeAggregator {
588588
if (targetState.current && targetIsDocumentTarget(targetData.target)) {
589589
// Document queries for document that don't exist can produce an empty
590590
// result set. To update our local cache, we synthesize a document
591-
// delete if we have not previously received the document. This
592-
// resolves the limbo state of the document, removing it from
593-
// limboDocumentRefs.
591+
// delete if we have not previously received the document for this
592+
// target. This resolves the limbo state of the document, removing it
593+
// from limboDocumentRefs.
594594
//
595595
// TODO(dimond): Ideally we would have an explicit lookup target
596596
// instead resulting in an explicit delete message and we could

packages/firestore/test/unit/specs/limbo_spec.test.ts

Lines changed: 87 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,7 +1173,7 @@ describeSpec('Limbo Documents:', [], () => {
11731173
);
11741174

11751175
specTest(
1176-
'Fix #8474 - Limbo resolution for documentC is removed even if documentC updates occurred earlier in the global snapshot window',
1176+
'Fix #8474 - Limbo resolution for document is removed even if document updates for the document occurred before documentDelete in the global snapshot window',
11771177
[],
11781178
() => {
11791179
// onSnapshot(fullQuery)
@@ -1199,7 +1199,7 @@ describeSpec('Limbo Documents:', [], () => {
11991199
})
12001200

12011201
// docC was deleted, this puts docC in limbo and causes a snapshot from cache (metadata-only change)
1202-
.watchSends({ removed: [fullQuery] }, docC)
1202+
.watchRemovesDoc(docC.key, fullQuery)
12031203
.watchCurrents(fullQuery, 'resume-token-1002')
12041204
.watchSnapshots(1002)
12051205
.expectLimboDocs(docC.key)
@@ -1221,19 +1221,19 @@ describeSpec('Limbo Documents:', [], () => {
12211221
.watchAcks(filterQuery)
12221222

12231223
// Watch responds to limboQueryC - docC was deleted
1224-
.watchSends({ affects: [limboQueryC] })
1224+
.watchDeletesDoc(docC.key, 1009, limboQueryC)
12251225
.watchCurrents(limboQueryC, 'resume-token-1009')
12261226
.watchSnapshots(1009, [limboQueryC, fullQuery])
12271227

12281228
// However, docC is still in limbo because there has not been a global snapshot
12291229
.expectLimboDocs(docC.key)
12301230

12311231
// Rapid events of document update and delete caused by application
1232-
.watchSends({ removed: [filterQuery] }, docA)
1232+
.watchRemovesDoc(docA.key, filterQuery)
12331233
.watchCurrents(filterQuery, 'resume-token-1004')
12341234
.watchSends({ affects: [filterQuery] }, docC)
12351235
.watchCurrents(filterQuery, 'resume-token-1005')
1236-
.watchSends({ removed: [filterQuery] }, docC)
1236+
.watchRemovesDoc(docC.key, filterQuery)
12371237
.watchSends({ affects: [filterQuery] }, docA2)
12381238
.watchCurrents(filterQuery, 'resume-token-1007')
12391239

@@ -1255,96 +1255,103 @@ describeSpec('Limbo Documents:', [], () => {
12551255
removed: [docC],
12561256
added: [docA2]
12571257
})
1258+
1259+
// No more expected events
1260+
.watchSnapshots(1100, [])
12581261
);
12591262
}
12601263
);
12611264

1262-
specTest('Fix #8474 - Watch sends deletedDoc on limbo resolution', [], () => {
1263-
// onSnapshot(fullQuery)
1264-
const fullQuery = query('collection');
1265+
specTest(
1266+
'Fix #8474 - Limbo resolution for document is removed even if document updates for the document occurred in the global snapshot window and no document delete was received for the limbo resolution query',
1267+
[],
1268+
() => {
1269+
// onSnapshot(fullQuery)
1270+
const fullQuery = query('collection');
12651271

1266-
// getDocs(filterQuery)
1267-
const filterQuery = query('collection', filter('included', '==', true));
1272+
// getDocs(filterQuery)
1273+
const filterQuery = query('collection', filter('included', '==', true));
12681274

1269-
const docA = doc('collection/a', 1000, { key: 'a', included: false });
1270-
const docA2 = doc('collection/a', 1007, { key: 'a', included: true });
1271-
const docC = doc('collection/c', 1002, { key: 'c', included: true });
1272-
const docCDeleted = deletedDoc('collection/c', 1009);
1275+
const docA = doc('collection/a', 1000, { key: 'a', included: false });
1276+
const docA2 = doc('collection/a', 1007, { key: 'a', included: true });
1277+
const docC = doc('collection/c', 1002, { key: 'c', included: true });
12731278

1274-
const limboQueryC = newQueryForPath(docC.key.path);
1279+
const limboQueryC = newQueryForPath(docC.key.path);
12751280

1276-
return (
1277-
spec()
1278-
// onSnapshot(fullQuery) - fullQuery is listening to documents in the collection for the full test
1279-
.userListens(fullQuery)
1280-
.watchAcksFull(fullQuery, 1001, docA, docC)
1281-
.expectEvents(fullQuery, {
1282-
fromCache: false,
1283-
added: [docA, docC]
1284-
})
1281+
return (
1282+
spec()
1283+
// onSnapshot(fullQuery) - fullQuery is listening to documents in the collection for the full test
1284+
.userListens(fullQuery)
1285+
.watchAcksFull(fullQuery, 1001, docA, docC)
1286+
.expectEvents(fullQuery, {
1287+
fromCache: false,
1288+
added: [docA, docC]
1289+
})
12851290

1286-
// docC was deleted, this puts docC in limbo and causes a snapshot from cache (metadata-only change)
1287-
.watchSends({ removed: [fullQuery] }, docC)
1288-
.watchCurrents(fullQuery, 'resume-token-1002')
1289-
.watchSnapshots(1002)
1290-
.expectLimboDocs(docC.key)
1291-
.expectEvents(fullQuery, {
1292-
fromCache: true
1293-
})
1291+
// docC was deleted, this puts docC in limbo and causes a snapshot from cache (metadata-only change)
1292+
.watchRemovesDoc(docC.key, fullQuery)
1293+
.watchCurrents(fullQuery, 'resume-token-1002')
1294+
.watchSnapshots(1002)
1295+
.expectLimboDocs(docC.key)
1296+
.expectEvents(fullQuery, {
1297+
fromCache: true
1298+
})
12941299

1295-
// User begins getDocs(filterQuery)
1296-
.userListensForGet(filterQuery)
1300+
// User begins getDocs(filterQuery)
1301+
.userListensForGet(filterQuery)
12971302

1298-
// getDocs(filterQuery) will not resolve on the snapshot from cache
1299-
.expectEvents(filterQuery, {
1300-
fromCache: true,
1301-
added: [docC]
1302-
})
1303+
// getDocs(filterQuery) will not resolve on the snapshot from cache
1304+
.expectEvents(filterQuery, {
1305+
fromCache: true,
1306+
added: [docC]
1307+
})
13031308

1304-
// Watch acks limbo and filter queries
1305-
.watchAcks(limboQueryC)
1306-
.watchAcks(filterQuery)
1309+
// Watch acks limbo and filter queries
1310+
.watchAcks(limboQueryC)
1311+
.watchAcks(filterQuery)
13071312

1308-
// Watch responds to limboQueryC - docC was deleted
1309-
.watchSends({ affects: [limboQueryC] }, docCDeleted)
1310-
.watchCurrents(limboQueryC, 'resume-token-1009')
1311-
.watchSnapshots(1009, [limboQueryC, fullQuery])
1313+
// Watch currents the limbo query, but did not send a document delete.
1314+
// This is and unexpected code path, but something that is called
1315+
// out as possible in the watch change aggregator.
1316+
.watchCurrents(limboQueryC, 'resume-token-1009')
1317+
.watchSnapshots(1009, [limboQueryC, fullQuery])
13121318

1313-
// However, docC is still in limbo because there has not been a global snapshot
1314-
.expectLimboDocs(docC.key)
1319+
// However, docC is still in limbo because there has not been a global snapshot
1320+
.expectLimboDocs(docC.key)
13151321

1316-
// Rapid events of document update and delete caused by application
1317-
.watchSends({ removed: [filterQuery] }, docA)
1318-
.watchCurrents(filterQuery, 'resume-token-1004')
1319-
.watchSends({ affects: [filterQuery] }, docC)
1320-
.watchCurrents(filterQuery, 'resume-token-1005')
1321-
.watchSends({ removed: [filterQuery] }, docC)
1322-
.watchSends({ affects: [filterQuery] }, docA2)
1323-
.watchCurrents(filterQuery, 'resume-token-1007')
1322+
// Rapid events of document update and delete caused by application
1323+
.watchRemovesDoc(docA.key, filterQuery)
1324+
.watchCurrents(filterQuery, 'resume-token-1004')
1325+
.watchSends({ affects: [filterQuery] }, docC)
1326+
.watchCurrents(filterQuery, 'resume-token-1005')
1327+
.watchRemovesDoc(docC.key, filterQuery)
1328+
.watchSends({ affects: [filterQuery] }, docA2)
1329+
.watchCurrents(filterQuery, 'resume-token-1007')
13241330

1325-
.watchSnapshots(1010, [fullQuery, limboQueryC])
1331+
.watchSnapshots(1010, [fullQuery, limboQueryC])
13261332

1327-
// All changes are current and we get a global snapshot
1328-
.watchSnapshots(1010, [])
1333+
// All changes are current and we get a global snapshot
1334+
.watchSnapshots(1010, [])
13291335

1330-
// Now docC is out of limbo
1331-
.expectLimboDocs()
1332-
.expectEvents(fullQuery, {
1333-
fromCache: false,
1334-
modified: [docA2],
1335-
removed: [docC]
1336-
})
1337-
// Now getDocs(filterQuery) can be resolved
1338-
.expectEvents(filterQuery, {
1339-
fromCache: false,
1340-
removed: [docC],
1341-
added: [docA2]
1342-
})
1336+
// Now docC is out of limbo
1337+
.expectLimboDocs()
1338+
.expectEvents(fullQuery, {
1339+
fromCache: false,
1340+
modified: [docA2],
1341+
removed: [docC]
1342+
})
1343+
// Now getDocs(filterQuery) can be resolved
1344+
.expectEvents(filterQuery, {
1345+
fromCache: false,
1346+
removed: [docC],
1347+
added: [docA2]
1348+
})
13431349

1344-
// No more expected events
1345-
.watchSnapshots(1100, [])
1346-
);
1347-
});
1350+
// No more expected events
1351+
.watchSnapshots(1100, [])
1352+
);
1353+
}
1354+
);
13481355

13491356
specTest(
13501357
'Fix #8474 - Handles code path of no ack for limbo resolution query before global snapshot',
@@ -1359,7 +1366,6 @@ describeSpec('Limbo Documents:', [], () => {
13591366
const docA = doc('collection/a', 1000, { key: 'a', included: false });
13601367
const docA2 = doc('collection/a', 1007, { key: 'a', included: true });
13611368
const docC = doc('collection/c', 1002, { key: 'c', included: true });
1362-
const docCDeleted = deletedDoc('collection/c', 1009);
13631369

13641370
const limboQueryC = newQueryForPath(docC.key.path);
13651371

@@ -1374,7 +1380,7 @@ describeSpec('Limbo Documents:', [], () => {
13741380
})
13751381

13761382
// docC was deleted, this puts docC in limbo and causes a snapshot from cache (metadata-only change)
1377-
.watchSends({ removed: [fullQuery] }, docC)
1383+
.watchRemovesDoc(docC.key, fullQuery)
13781384
.watchCurrents(fullQuery, 'resume-token-1002')
13791385
.watchSnapshots(1002)
13801386
.expectLimboDocs(docC.key)
@@ -1398,11 +1404,11 @@ describeSpec('Limbo Documents:', [], () => {
13981404
.expectLimboDocs(docC.key)
13991405

14001406
// Rapid events of document update and delete caused by application
1401-
.watchSends({ removed: [filterQuery] }, docA)
1407+
.watchRemovesDoc(docA.key, filterQuery)
14021408
.watchCurrents(filterQuery, 'resume-token-1004')
14031409
.watchSends({ affects: [filterQuery] }, docC)
14041410
.watchCurrents(filterQuery, 'resume-token-1005')
1405-
.watchSends({ removed: [filterQuery] }, docC)
1411+
.watchRemovesDoc(docC.key, filterQuery)
14061412
.watchSends({ affects: [filterQuery] }, docA2)
14071413
.watchCurrents(filterQuery, 'resume-token-1007')
14081414

@@ -1425,7 +1431,7 @@ describeSpec('Limbo Documents:', [], () => {
14251431
.watchAcks(limboQueryC)
14261432

14271433
// Watch responds to limboQueryC - docC was deleted
1428-
.watchSends({ affects: [limboQueryC] }, docCDeleted)
1434+
.watchDeletesDoc(docC.key, 1009, limboQueryC)
14291435
.watchCurrents(limboQueryC, 'resume-token-1009')
14301436
.watchSnapshots(1100, [limboQueryC])
14311437

packages/firestore/test/unit/specs/spec_builder.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import { forEach } from '../../../src/util/obj';
5151
import { ObjectMap } from '../../../src/util/obj_map';
5252
import { isNullOrUndefined } from '../../../src/util/types';
5353
import { firestore } from '../../util/api_helpers';
54-
import { TestSnapshotVersion } from '../../util/helpers';
54+
import { deletedDoc, TestSnapshotVersion } from '../../util/helpers';
5555

5656
import { RpcError } from './spec_rpc_error';
5757
import {
@@ -830,6 +830,23 @@ export class SpecBuilder {
830830
return this;
831831
}
832832

833+
watchDeletesDoc(
834+
key: DocumentKey,
835+
version: TestSnapshotVersion,
836+
...targets: Query[]
837+
): this {
838+
this.nextStep();
839+
this.currentStep = {
840+
watchEntity: {
841+
doc: SpecBuilder.docToSpec(
842+
deletedDoc(key.path.canonicalString(), version)
843+
),
844+
removedTargets: targets.map(query => this.getTargetId(query))
845+
}
846+
};
847+
return this;
848+
}
849+
833850
watchFilters(
834851
queries: Query[],
835852
docs: DocumentKey[] = [],

packages/firestore/test/util/helpers.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,12 +172,19 @@ export function doc(
172172
}
173173

174174
export function deletedDoc(
175-
keyStr: string,
175+
keyStrOrDocumentKey: string | DocumentKey,
176176
ver: TestSnapshotVersion
177177
): MutableDocument {
178-
return MutableDocument.newNoDocument(key(keyStr), version(ver)).setReadTime(
178+
if (
179+
keyStrOrDocumentKey instanceof String ||
180+
typeof keyStrOrDocumentKey === 'string'
181+
) {
182+
keyStrOrDocumentKey = key(keyStrOrDocumentKey as string);
183+
}
184+
return MutableDocument.newNoDocument(
185+
keyStrOrDocumentKey,
179186
version(ver)
180-
);
187+
).setReadTime(version(ver));
181188
}
182189

183190
export function unknownDoc(

0 commit comments

Comments
 (0)