Skip to content

Commit 4ff990a

Browse files
Add listen spec tests (#812)
1 parent c150044 commit 4ff990a

File tree

4 files changed

+97
-17
lines changed

4 files changed

+97
-17
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describeSpec('Limits:', [], () => {
5555
})
5656
.watchResets(query)
5757
.watchSends({ affects: [query] }, doc2, doc3)
58+
.watchCurrents(query, 'resume-token-' + 2000)
5859
.watchSnapshots(2000)
5960
.expectLimboDocs(doc1.key)
6061
// Limbo document causes query to be "inconsistent"
@@ -161,6 +162,7 @@ describeSpec('Limits:', [], () => {
161162
* D, since they are results for query2. query2 is just a hack to make
162163
* sure that C and D are in the local cache.
163164
*/
165+
.watchCurrents(query1, 'resume-token-' + 2000)
164166
.watchSnapshots(2000)
165167
.expectLimboDocs(docA.key, docB.key)
166168
// Limbo document causes query to be "inconsistent"

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

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { Query } from '../../../src/core/query';
1818
import { Code } from '../../../src/util/error';
19-
import { doc, filter, path } from '../../util/helpers';
19+
import { deletedDoc, doc, filter, path } from '../../util/helpers';
2020

2121
import { describeSpec, specTest } from './describe_spec';
2222
import { spec } from './spec_builder';
@@ -78,6 +78,24 @@ describeSpec('Listens:', [], () => {
7878
}
7979
);
8080

81+
specTest('Does not raise event for initial document delete', [], () => {
82+
const query = Query.atPath(path('collection'));
83+
const missingDoc = deletedDoc('collection/a', 1000);
84+
return (
85+
spec()
86+
.userListens(query)
87+
.watchAcks(query)
88+
// To indicate the document doesn't exist, watch sends a DocumentDelete
89+
// message as if the document previously existed and now is being
90+
// deleted/removed from the target.
91+
.watchSends({ removed: [query] }, missingDoc)
92+
.watchSnapshots(1000)
93+
.watchCurrents(query, 'resume-token-2000')
94+
.watchSnapshots(2000)
95+
.expectEvents(query, { fromCache: false })
96+
);
97+
});
98+
8199
specTest(
82100
'Will process removals without waiting for a consistent snapshot',
83101
[],
@@ -261,4 +279,47 @@ describeSpec('Listens:', [], () => {
261279
.watchAcksFull(query, 2000, docB)
262280
.expectEvents(query, { added: [docB] });
263281
});
282+
283+
specTest('Ignores update from inactive target', [], () => {
284+
const query = Query.atPath(path('collection'));
285+
const docA = doc('collection/a', 1000, { key: 'a' });
286+
const docB = doc('collection/b', 2000, { key: 'b' });
287+
return spec()
288+
.withGCEnabled(false)
289+
.userListens(query)
290+
.watchAcksFull(query, 1000, docA)
291+
.expectEvents(query, { added: [docA] })
292+
.userUnlistens(query)
293+
.watchSends({ affects: [query] }, docB)
294+
.watchSnapshots(2000)
295+
.watchRemoves(query)
296+
.userListens(query, 'resume-token-1000')
297+
.expectEvents(query, { added: [docA], fromCache: true });
298+
});
299+
300+
specTest(
301+
'Does not synthesize deletes for previously acked documents',
302+
[],
303+
() => {
304+
const query = Query.atPath(path('collection/a'));
305+
const docA = doc('collection/a', 1000, { key: 'a' });
306+
return (
307+
spec()
308+
.withGCEnabled(false)
309+
.userListens(query)
310+
.watchAcks(query)
311+
.watchSends({ affects: [query] }, docA)
312+
.watchSnapshots(1000)
313+
.expectEvents(query, { added: [docA], fromCache: true })
314+
.watchCurrents(query, 'resume-token-2000')
315+
.watchSnapshots(2000)
316+
// The snapshot is empty, but we have received 'docA' in a previous
317+
// snapshot and don't synthesize a document delete.
318+
.expectEvents(query, { fromCache: false })
319+
.userUnlistens(query)
320+
.userListens(query, 'resume-token-2000')
321+
.expectEvents(query, { added: [docA], fromCache: true })
322+
);
323+
}
324+
);
264325
});

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
import { Filter, Query, RelationFilter } from '../../../src/core/query';
1818
import { TargetIdGenerator } from '../../../src/core/target_id_generator';
1919
import { TargetId } from '../../../src/core/types';
20-
import { Document, NoDocument } from '../../../src/model/document';
20+
import {
21+
Document,
22+
MaybeDocument,
23+
NoDocument
24+
} from '../../../src/model/document';
2125
import { DocumentKey } from '../../../src/model/document_key';
2226
import { JsonObject } from '../../../src/model/field_value';
2327
import { mapRpcCodeFromCode } from '../../../src/remote/rpc_error';
@@ -412,7 +416,7 @@ export class SpecBuilder {
412416

413417
watchSends(
414418
targets: { affects?: Query[]; removed?: Query[] },
415-
...docs: Document[]
419+
...docs: MaybeDocument[]
416420
): SpecBuilder {
417421
this.nextStep();
418422
const affects =
@@ -614,15 +618,21 @@ export class SpecBuilder {
614618
return spec;
615619
}
616620

617-
private static docToSpec(doc: Document): SpecDocument {
621+
private static docToSpec(doc: MaybeDocument): SpecDocument {
622+
const data = doc instanceof Document ? doc.data.value() : null;
623+
const localMutations =
624+
doc instanceof Document ? doc.hasLocalMutations : false;
625+
618626
const spec: SpecDocument = [
619627
SpecBuilder.keyToSpec(doc.key),
620628
doc.version.toMicroseconds(),
621-
doc.data.value()
629+
data
622630
];
623-
if (doc.hasLocalMutations) {
631+
632+
if (localMutations) {
624633
spec.push('local');
625634
}
635+
626636
return spec;
627637
}
628638

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

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ import * as obj from '../../../src/util/obj';
7777
import { ObjectMap } from '../../../src/util/obj_map';
7878
import { Deferred, sequence } from '../../../src/util/promise';
7979
import {
80+
deletedDoc,
8081
deleteMutation,
8182
doc,
8283
filter,
@@ -631,33 +632,36 @@ abstract class TestRunner {
631632
'Exactly one of `doc` or `docs` needs to be set'
632633
);
633634
let count = 0;
634-
return sequence(watchEntity.docs, (d: SpecDocument) => {
635+
return sequence(watchEntity.docs, (specDocument: SpecDocument) => {
635636
count++;
636637
const isLast = count === watchEntity.docs!.length;
637638
return this.doWatchEntity(
638639
{
639-
doc: d,
640+
doc: specDocument,
640641
targets: watchEntity.targets,
641642
removedTargets: watchEntity.removedTargets
642643
},
643644
isLast ? watchSnapshot : undefined
644645
);
645646
});
646647
} else if (watchEntity.doc) {
647-
const d = doc(watchEntity.doc[0], watchEntity.doc[1], watchEntity.doc[2]);
648+
const [key, version, data] = watchEntity.doc;
649+
const document = data
650+
? doc(key, version, data)
651+
: deletedDoc(key, version);
648652
const change = new DocumentWatchChange(
649653
watchEntity.targets || [],
650654
watchEntity.removedTargets || [],
651-
d.key,
652-
d
655+
document.key,
656+
document
653657
);
654658
return this.doWatchEvent(change, watchSnapshot);
655659
} else if (watchEntity.key) {
656-
const k = key(watchEntity.key);
660+
const documentKey = key(watchEntity.key);
657661
const change = new DocumentWatchChange(
658662
watchEntity.targets || [],
659663
watchEntity.removedTargets || [],
660-
k,
664+
documentKey,
661665
null
662666
);
663667
return this.doWatchEvent(change, watchSnapshot);
@@ -1247,15 +1251,18 @@ export interface SpecQuery {
12471251

12481252
/**
12491253
* [<key>, <version>, <value>, <doc-options> (optional), ...]
1250-
* Represents a document.
1254+
* Represents a document. <value> is null for deleted documents.
12511255
* Doc options are:
12521256
* 'local': document has local modifications
12531257
*/
1254-
export type SpecDocument = [string, SpecSnapshotVersion, JsonObject<AnyJs>];
1258+
export type SpecDocument = [
1259+
string,
1260+
SpecSnapshotVersion,
1261+
JsonObject<AnyJs> | null
1262+
];
12551263

12561264
export interface SpecExpectation {
1257-
/** If a query is a string, that's treated as a query for a path. */
1258-
query: string | SpecQuery;
1265+
query: SpecQuery;
12591266
errorCode?: number;
12601267
fromCache?: boolean;
12611268
hasPendingWrites?: boolean;

0 commit comments

Comments
 (0)