Skip to content

Commit 0657f81

Browse files
Adding Watch conformance tests (#176)
* Adding Watch conformance tests * Adding newline
1 parent 910cd97 commit 0657f81

File tree

6 files changed

+138
-5
lines changed

6 files changed

+138
-5
lines changed

conformance/runner.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const is = require('is');
2222
const through = require('through2');
2323
const googleProtoFiles = require('google-proto-files');
2424
const protobufjs = require('protobufjs');
25+
const duplexify = require('duplexify');
2526
const grpc = require('google-gax').grpc().grpc;
2627

2728
const Firestore = require('../');
@@ -52,6 +53,10 @@ const collRef = function(path) {
5253
return firestore.collection(relativePath);
5354
};
5455

56+
const watchQuery = collRef(
57+
'projects/projectID/databases/(default)/documents/C'
58+
).orderBy('a');
59+
5560
/** Converts JSON test data into JavaScript types suitable for the Node API. */
5661
const convertInput = {
5762
argument: json => {
@@ -128,6 +133,40 @@ const convertInput = {
128133
}
129134
return args;
130135
},
136+
snapshot: snapshot => {
137+
const docs = [];
138+
const changes = [];
139+
const readTime = DocumentSnapshot.toISOTime(snapshot.readTime);
140+
141+
for (const doc of snapshot.docs) {
142+
const deepCopy = JSON.parse(JSON.stringify(doc));
143+
deepCopy.fields = convert.documentFromJson(deepCopy.fields);
144+
docs.push(firestore.snapshot_(deepCopy, readTime, 'json'));
145+
}
146+
147+
for (const change of snapshot.changes) {
148+
const deepCopy = JSON.parse(JSON.stringify(change.doc));
149+
deepCopy.fields = convert.documentFromJson(deepCopy.fields);
150+
const doc = firestore.snapshot_(deepCopy, readTime, 'json');
151+
const type = ['unspecified', 'added', 'removed', 'modified'][change.kind];
152+
changes.push(
153+
new Firestore.DocumentChange(
154+
type,
155+
doc,
156+
change.oldIndex,
157+
change.newIndex
158+
)
159+
);
160+
}
161+
162+
return new Firestore.QuerySnapshot(
163+
watchQuery,
164+
readTime,
165+
docs.length,
166+
() => docs,
167+
() => changes
168+
);
169+
},
131170
};
132171

133172
/** Converts Firestore Protobuf types in Proto3 JSON format to Protobuf JS. */
@@ -173,6 +212,23 @@ const convertProto = {
173212
}
174213
return deepCopy;
175214
},
215+
listenRequest: listenRequest => {
216+
const deepCopy = JSON.parse(JSON.stringify(listenRequest));
217+
218+
if (deepCopy.targetChange) {
219+
if (deepCopy.targetChange.targetChangeType === undefined) {
220+
deepCopy.targetChange.targetChangeType = 'NO_CHANGE';
221+
}
222+
}
223+
224+
if (deepCopy.documentChange) {
225+
deepCopy.documentChange.document.fields = convert.documentFromJson(
226+
deepCopy.documentChange.document.fields
227+
);
228+
}
229+
230+
return deepCopy;
231+
},
176232
};
177233

178234
/** Request handler for _commit. */
@@ -366,6 +422,38 @@ function runTest(spec) {
366422
});
367423
};
368424

425+
const watchTest = function(spec) {
426+
let expectedSnapshots = spec.snapshots;
427+
428+
const writeStream = through.obj();
429+
430+
firestore.api.Firestore._listen = () => {
431+
return duplexify.obj(through.obj(), writeStream);
432+
};
433+
434+
return new Promise((resolve, reject) => {
435+
const unlisten = watchQuery.onSnapshot(acutalSnap => {
436+
const expectedSnapshot = expectedSnapshots.shift();
437+
if (expectedSnapshot) {
438+
if (!acutalSnap.isEqual(convertInput.snapshot(expectedSnapshot))) {
439+
reject(new Error('Expected and actual snapshots do not match.'));
440+
}
441+
442+
if (expectedSnapshots.length === 0) {
443+
unlisten();
444+
resolve();
445+
}
446+
} else {
447+
reject(new Error('Received unexpected snapshot'));
448+
}
449+
});
450+
451+
for (const response of spec.responses) {
452+
writeStream.write(convertProto.listenRequest(response));
453+
}
454+
});
455+
};
456+
369457
let testSpec;
370458
let testPromise;
371459

@@ -375,6 +463,7 @@ function runTest(spec) {
375463
const updateSpec = spec.update || spec.updatePaths;
376464
const deleteSpec = spec.delete;
377465
const querySpec = spec.query;
466+
const listenSpec = spec.listen;
378467

379468
if (getSpec) {
380469
testSpec = getSpec;
@@ -394,6 +483,9 @@ function runTest(spec) {
394483
} else if (querySpec) {
395484
testSpec = querySpec;
396485
testPromise = queryTest(querySpec);
486+
} else if (listenSpec) {
487+
testSpec = listenSpec;
488+
testPromise = watchTest(listenSpec);
397489
} else {
398490
return Promise.reject(new Error(`Unhandled Spec: ${JSON.stringify(spec)}`));
399491
}

conformance/test-definition.proto

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ syntax = "proto3";
44

55
package tests;
66

7+
option php_namespace = "Google\\Cloud\\Firestore\\Tests\\Conformance";
78
option csharp_namespace = "Google.Cloud.Firestore.Tests.Proto";
89
option java_package = "com.google.cloud.firestore.conformance";
910

10-
import "google/firestore/v1beta1/firestore.proto";
1111
import "google/firestore/v1beta1/common.proto";
12+
import "google/firestore/v1beta1/document.proto";
13+
import "google/firestore/v1beta1/firestore.proto";
1214
import "google/firestore/v1beta1/query.proto";
15+
import "google/protobuf/timestamp.proto";
1316

1417
// A collection of tests.
1518
message TestSuite {
@@ -28,6 +31,7 @@ message Test {
2831
UpdatePathsTest update_paths = 6;
2932
DeleteTest delete = 7;
3033
QueryTest query = 8;
34+
ListenTest listen = 9;
3135
}
3236
}
3337

@@ -153,3 +157,40 @@ message DocSnapshot {
153157
message FieldPath {
154158
repeated string field = 1;
155159
}
160+
161+
// A test of the Listen streaming RPC (a.k.a. FireStore watch).
162+
// If the sequence of responses is provided to the implementation,
163+
// it should produce the sequence of snapshots.
164+
// If is_error is true, an error should occur after the snapshots.
165+
//
166+
// The tests assume that the query is
167+
// Collection("projects/projectID/databases/(default)/documents/C").OrderBy("a", Ascending)
168+
//
169+
// The watch target ID used in these tests is 1. Test interpreters
170+
// should either change their client's ID for testing,
171+
// or change the ID in the tests before running them.
172+
message ListenTest {
173+
repeated google.firestore.v1beta1.ListenResponse responses = 1;
174+
repeated Snapshot snapshots = 2;
175+
bool is_error = 3;
176+
}
177+
178+
message Snapshot {
179+
repeated google.firestore.v1beta1.Document docs = 1;
180+
repeated DocChange changes = 2;
181+
google.protobuf.Timestamp read_time = 3;
182+
}
183+
184+
message DocChange {
185+
enum Kind {
186+
KIND_UNSPECIFIED = 0;
187+
ADDED = 1;
188+
REMOVED = 2;
189+
MODIFIED = 3;
190+
}
191+
192+
Kind kind = 1;
193+
google.firestore.v1beta1.Document doc = 2;
194+
int32 old_index = 3;
195+
int32 new_index = 4;
196+
}

conformance/test-suite.binproto

2.67 KB
Binary file not shown.

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ module.exports.QuerySnapshot = reference.QuerySnapshot;
12571257
* @see DocumentChange
12581258
* @type DocumentChange
12591259
*/
1260-
module.exports.DocumentChange = document.DocumentChange;
1260+
module.exports.DocumentChange = reference.DocumentChange;
12611261

12621262
/**
12631263
* {@link Query} class.

src/watch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ let ResourcePath = require('./path').ResourcePath;
6767
* @private
6868
* @type {number}
6969
*/
70-
const WATCH_TARGET_ID = 0xf0;
70+
const WATCH_TARGET_ID = 0x1;
7171

7272
/*!
7373
* The change type for document change events.

test/watch.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -598,7 +598,7 @@ describe('Query watch', function() {
598598

599599
firestore = createInstance();
600600

601-
targetId = 0xf0;
601+
targetId = 0x1;
602602

603603
streamHelper = new StreamHelper(firestore);
604604
watchHelper = new WatchHelper(
@@ -2125,7 +2125,7 @@ describe('DocumentReference watch', function() {
21252125
Backoff.setTimeoutHandler(setImmediate);
21262126

21272127
firestore = createInstance();
2128-
targetId = 0xf0;
2128+
targetId = 0x1;
21292129
doc = firestore.doc('col/doc');
21302130
streamHelper = new StreamHelper(firestore);
21312131
watchHelper = new WatchHelper(streamHelper, doc, targetId);

0 commit comments

Comments
 (0)