Skip to content

Commit 9552c32

Browse files
authoredJul 12, 2018
Test initialization refactor (#254)
1 parent a3d91e1 commit 9552c32

13 files changed

+2101
-1746
lines changed
 

‎conformance/runner.js

+64-60
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,20 @@ const document = require('../src/document')(reference.DocumentReference);
3232
const DocumentSnapshot = document.DocumentSnapshot;
3333
const convert = require('../src/convert');
3434
const ResourcePath = require('../src/path').ResourcePath;
35-
36-
const firestore = new Firestore({
37-
projectId: 'projectID',
38-
sslCreds: grpc.credentials.createInsecure(),
39-
timestampsInSnapshots: true,
40-
keyFilename: './test/fake-certificate.json',
41-
});
35+
const createInstanceHelper = require('../test/util/helpers').createInstance;
4236

4337
/** List of test cases that are ignored. */
4438
const ignoredRe = [];
4539

4640
/** If non-empty, list the test cases to run exclusively. */
4741
const exclusiveRe = [];
4842

43+
// The project ID used in the conformance test protos.
44+
const CONFORMANCE_TEST_PROJECT_ID = 'projectID';
45+
46+
// Firestore instance initialized by the test runner.
47+
let firestore;
48+
4949
const docRef = function(path) {
5050
const relativePath = ResourcePath.fromSlashSeparatedString(path).relativeName;
5151
return firestore.doc(relativePath);
@@ -56,8 +56,18 @@ const collRef = function(path) {
5656
return firestore.collection(relativePath);
5757
};
5858

59-
const watchQuery =
60-
collRef('projects/projectID/databases/(default)/documents/C').orderBy('a');
59+
const watchQuery = function() {
60+
return firestore.collection('C').orderBy('a');
61+
};
62+
63+
const createInstance = function(overrides) {
64+
return createInstanceHelper(
65+
overrides, {projectId: CONFORMANCE_TEST_PROJECT_ID})
66+
.then(firestoreClient => {
67+
firestore = firestoreClient;
68+
});
69+
};
70+
6171

6272
/** Converts JSON test data into JavaScript types suitable for the Node API. */
6373
const convertInput = {
@@ -154,7 +164,7 @@ const convertInput = {
154164
}
155165

156166
return new Firestore.QuerySnapshot(
157-
watchQuery, readTime, docs.length, () => docs, () => changes);
167+
watchQuery(), readTime, docs.length, () => docs, () => changes);
158168
},
159169
};
160170

@@ -279,9 +289,9 @@ function runTest(spec) {
279289
console.log(`Running Spec:\n${JSON.stringify(spec, null, 2)}\n`);
280290

281291
const updateTest = function(spec) {
282-
firestore._firestoreClient._innerApiCalls.commit = commitHandler(spec);
292+
const overrides = {commit: commitHandler(spec)};
283293

284-
return Promise.resolve().then(() => {
294+
return createInstance(overrides).then(() => {
285295
let varargs = [];
286296

287297
if (spec.jsonData) {
@@ -305,7 +315,7 @@ function runTest(spec) {
305315
};
306316

307317
const queryTest = function(spec) {
308-
firestore._firestoreClient._innerApiCalls.runQuery = queryHandler(spec);
318+
const overrides = {runQuery: queryHandler(spec)};
309319

310320
const applyClause = function(query, clause) {
311321
if (clause.select) {
@@ -337,9 +347,8 @@ function runTest(spec) {
337347
return query;
338348
};
339349

340-
let query = collRef(spec.collPath);
341-
342-
return Promise.resolve().then(() => {
350+
return createInstance(overrides).then(() => {
351+
let query = collRef(spec.collPath);
343352
for (let clause of spec.clauses) {
344353
query = applyClause(query, clause);
345354
}
@@ -348,9 +357,9 @@ function runTest(spec) {
348357
};
349358

350359
const deleteTest = function(spec) {
351-
firestore._firestoreClient._innerApiCalls.commit = commitHandler(spec);
360+
const overrides = {commit: commitHandler(spec)};
352361

353-
return Promise.resolve().then(() => {
362+
return createInstance(overrides).then(() => {
354363
if (spec.precondition) {
355364
const precondition = convertInput.precondition(deleteSpec.precondition);
356365
return docRef(spec.docRefPath).delete(precondition);
@@ -361,9 +370,9 @@ function runTest(spec) {
361370
};
362371

363372
const setTest = function(spec) {
364-
firestore._firestoreClient._innerApiCalls.commit = commitHandler(spec);
373+
const overrides = {commit: commitHandler(spec)};
365374

366-
return Promise.resolve().then(() => {
375+
return createInstance(overrides).then(() => {
367376
const setOption = {};
368377

369378
if (spec.option && spec.option.all) {
@@ -381,19 +390,18 @@ function runTest(spec) {
381390
};
382391

383392
const createTest = function(spec) {
384-
firestore._firestoreClient._innerApiCalls.commit = commitHandler(spec);
393+
const overrides = {commit: commitHandler(spec)};
385394

386-
return Promise.resolve().then(() => {
395+
return createInstance(overrides).then(() => {
387396
return docRef(spec.docRefPath)
388397
.create(convertInput.argument(spec.jsonData));
389398
});
390399
};
391400

392401
const getTest = function(spec) {
393-
firestore._firestoreClient._innerApiCalls.batchGetDocuments =
394-
getHandler(spec);
402+
const overrides = {batchGetDocuments: getHandler(spec)};
395403

396-
return Promise.resolve().then(() => {
404+
return createInstance(overrides).then(() => {
397405
return docRef(spec.docRefPath).get();
398406
});
399407
};
@@ -403,38 +411,38 @@ function runTest(spec) {
403411

404412
const writeStream = through.obj();
405413

406-
firestore._firestoreClient._innerApiCalls.listen = () => {
407-
return duplexify.obj(through.obj(), writeStream);
408-
};
409-
410-
return new Promise((resolve, reject) => {
411-
const unlisten = watchQuery.onSnapshot(
412-
actualSnap => {
413-
const expectedSnapshot = expectedSnapshots.shift();
414-
if (expectedSnapshot) {
415-
if (!actualSnap.isEqual(
416-
convertInput.snapshot(expectedSnapshot))) {
417-
reject(
418-
new Error('Expected and actual snapshots do not match.'));
419-
}
420-
421-
if (expectedSnapshots.length === 0 || !spec.isError) {
422-
unlisten();
423-
resolve();
414+
const overrides = {listen: () => duplexify.obj(through.obj(), writeStream)};
415+
416+
return createInstance(overrides).then(() => {
417+
return new Promise((resolve, reject) => {
418+
const unlisten = watchQuery().onSnapshot(
419+
actualSnap => {
420+
const expectedSnapshot = expectedSnapshots.shift();
421+
if (expectedSnapshot) {
422+
if (!actualSnap.isEqual(
423+
convertInput.snapshot(expectedSnapshot))) {
424+
reject(
425+
new Error('Expected and actual snapshots do not match.'));
426+
}
427+
428+
if (expectedSnapshots.length === 0 || !spec.isError) {
429+
unlisten();
430+
resolve();
431+
}
432+
} else {
433+
reject(new Error('Received unexpected snapshot'));
424434
}
425-
} else {
426-
reject(new Error('Received unexpected snapshot'));
427-
}
428-
},
429-
err => {
430-
assert.equal(expectedSnapshots.length, 0);
431-
unlisten();
432-
reject(err);
433-
});
434-
435-
for (const response of spec.responses) {
436-
writeStream.write(convertProto.listenRequest(response));
437-
}
435+
},
436+
err => {
437+
assert.equal(expectedSnapshots.length, 0);
438+
unlisten();
439+
reject(err);
440+
});
441+
442+
for (const response of spec.responses) {
443+
writeStream.write(convertProto.listenRequest(response));
444+
}
445+
});
438446
});
439447
};
440448

@@ -509,10 +517,6 @@ describe('Conformance Tests', function() {
509517
return testSuite.tests;
510518
};
511519

512-
before(() => {
513-
firestore._ensureClient();
514-
});
515-
516520
for (let testCase of loadTestCases()) {
517521
const isIgnored = ignoredRe.find(re => re.test(testCase.description));
518522
const isExclusive = exclusiveRe.find(re => re.test(testCase.description));

‎src/path.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ const FIELD_PATH_RE = /^[^*~/[\]]+$/;
5757
* @class
5858
*/
5959
abstract class Path<T> {
60-
private _formattedName: string|undefined = undefined;
6160
/**
6261
* Creates a new Path with the given segments.
6362
*
@@ -74,11 +73,7 @@ abstract class Path<T> {
7473
* @type {string}
7574
*/
7675
get formattedName(): string {
77-
if (this._formattedName === undefined) {
78-
this._formattedName = this.canonicalString();
79-
}
80-
81-
return this._formattedName!;
76+
return this.canonicalString()!;
8277
}
8378

8479
abstract construct(segments: string[]|string): T;

‎test/collection.js

+47-54
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,13 @@ const is = require('is');
2424
const Firestore = require('../src');
2525
const DocumentReference =
2626
require('../src/reference')(Firestore).DocumentReference;
27+
const createInstance = require('../test/util/helpers').createInstance;
2728

2829
// Change the argument to 'console.log' to enable debug output.
2930
Firestore.setLogFunction(() => {});
3031

3132
const PROJECT_ID = 'test-project';
3233

33-
function createInstance() {
34-
let firestore = new Firestore({
35-
projectId: PROJECT_ID,
36-
sslCreds: grpc.credentials.createInsecure(),
37-
timestampsInSnapshots: true,
38-
keyFilename: './test/fake-certificate.json',
39-
});
40-
41-
return firestore._ensureClient().then(() => firestore);
42-
}
43-
4434
describe('Collection interface', function() {
4535
let firestore;
4636

@@ -101,54 +91,57 @@ describe('Collection interface', function() {
10191
it('has add() method', function() {
10292
const dbPrefix = `projects/${PROJECT_ID}/databases`;
10393

104-
firestore._firestoreClient._innerApiCalls.commit = function(
105-
request, options, callback) {
106-
// Verify that the document name uses an auto-generated id.
107-
let docIdRe = new RegExp(
108-
`${dbPrefix}/\\(default\\)/documents/collectionId/[a-zA-Z0-9]{20}`);
109-
assert.ok(docIdRe.test(request.writes[0].update.name));
110-
delete request.writes[0].update.name;
111-
112-
// Verify that the rest of the protobuf matches.
113-
assert.deepEqual(request, {
114-
database: dbPrefix + '/(default)',
115-
writes: [
116-
{
117-
update: {
118-
fields: {},
94+
const overrides = {
95+
commit: (request, options, callback) => {
96+
// Verify that the document name uses an auto-generated id.
97+
let docIdRe = new RegExp(
98+
`${dbPrefix}/\\(default\\)/documents/collectionId/[a-zA-Z0-9]{20}`);
99+
assert.ok(docIdRe.test(request.writes[0].update.name));
100+
delete request.writes[0].update.name;
101+
102+
// Verify that the rest of the protobuf matches.
103+
assert.deepEqual(request, {
104+
database: dbPrefix + '/(default)',
105+
writes: [
106+
{
107+
update: {
108+
fields: {},
109+
},
110+
currentDocument: {
111+
exists: false,
112+
},
119113
},
120-
currentDocument: {
121-
exists: false,
122-
},
123-
},
124-
],
125-
});
114+
],
115+
});
126116

127-
callback(null, {
128-
commitTime: {
129-
nanos: 0,
130-
seconds: 0,
131-
},
132-
writeResults: [
133-
{
134-
updateTime: {
135-
nanos: 0,
136-
seconds: 0,
137-
},
117+
callback(null, {
118+
commitTime: {
119+
nanos: 0,
120+
seconds: 0,
138121
},
139-
],
140-
});
122+
writeResults: [
123+
{
124+
updateTime: {
125+
nanos: 0,
126+
seconds: 0,
127+
},
128+
},
129+
],
130+
});
131+
}
141132
};
142133

143-
let collectionRef = firestore.collection('collectionId');
144-
assert.ok(collectionRef.add);
145-
let promise = collectionRef.add({});
146-
assert.ok(is.instance(promise, Promise));
147-
148-
return promise.then(documentRef => {
149-
assert.ok(is.instance(documentRef, DocumentReference));
150-
assert.equal(collectionRef.id, 'collectionId');
151-
assert.ok(documentRef.id.length, 20);
134+
return createInstance(overrides).then(firestore => {
135+
let collectionRef = firestore.collection('collectionId');
136+
assert.ok(collectionRef.add);
137+
let promise = collectionRef.add({});
138+
assert.ok(is.instance(promise, Promise));
139+
140+
return promise.then(documentRef => {
141+
assert.ok(is.instance(documentRef, DocumentReference));
142+
assert.equal(collectionRef.id, 'collectionId');
143+
assert.ok(documentRef.id.length, 20);
144+
});
152145
});
153146
});
154147

‎test/document.js

+926-774
Large diffs are not rendered by default.

‎test/index.js

+187-175
Large diffs are not rendered by default.

‎test/order.js

+1-11
Original file line numberDiff line numberDiff line change
@@ -28,23 +28,13 @@ const DocumentSnapshot = document.DocumentSnapshot;
2828
const GeoPoint = document.GeoPoint;
2929
const order = require('../src/order');
3030
const ResourcePath = require('../src/path').ResourcePath;
31+
const createInstance = require('../test/util/helpers').createInstance;
3132

3233
// Change the argument to 'console.log' to enable debug output.
3334
Firestore.setLogFunction(() => {});
3435

3536
const PROJECT_ID = 'test-project';
3637

37-
function createInstance() {
38-
let firestore = new Firestore({
39-
projectId: PROJECT_ID,
40-
sslCreds: grpc.credentials.createInsecure(),
41-
timestampsInSnapshots: true,
42-
keyFilename: './test/fake-certificate.json',
43-
});
44-
45-
return firestore._ensureClient().then(() => firestore);
46-
}
47-
4838
describe('Order', function() {
4939
let firestore;
5040

‎test/query.js

+600-436
Large diffs are not rendered by default.

‎test/timestamp.js

+8-15
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,22 @@ const is = require('is');
2323
const through = require('through2');
2424
const assert = require('assert');
2525

26+
const createInstanceHelper = require('../test/util/helpers').createInstance;
27+
2628
function createInstance(opts, document) {
27-
let firestore = new Firestore(Object.assign({}, opts, {
28-
projectId: 'test-project',
29-
sslCreds: grpc.credentials.createInsecure(),
30-
keyFilename: './test/fake-certificate.json',
31-
}));
32-
33-
return firestore._ensureClient().then(() => {
34-
firestore._firestoreClient._innerApiCalls.batchGetDocuments = function() {
29+
const overrides = {
30+
batchGetDocuments: () => {
3531
const stream = through.obj();
3632
setImmediate(function() {
3733
stream.push({found: document, readTime: {seconds: 5, nanos: 6}});
3834
stream.push(null);
3935
});
4036

4137
return stream;
42-
};
38+
}
39+
};
4340

44-
return firestore;
45-
});
41+
return createInstanceHelper(overrides, opts)
4642
}
4743

4844
function document(field, value) {
@@ -78,10 +74,7 @@ const DOCUMENT_WITH_EMPTY_TIMESTAMP = document('moonLanding', {
7874
describe('timestamps', function() {
7975
it('returned when enabled', function() {
8076
return createInstance(
81-
{
82-
timestampsInSnapshots: true,
83-
},
84-
DOCUMENT_WITH_TIMESTAMP)
77+
{timestampsInSnapshots: true}, DOCUMENT_WITH_TIMESTAMP)
8578
.then(firestore => {
8679
const expected = new Firestore.Timestamp(-14182920, 123000123);
8780
return firestore.doc('coll/doc').get().then(res => {

‎test/transaction.js

+105-121
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const grpc = new gax.GrpcClient().grpc;
2222
const through = require('through2');
2323

2424
const Firestore = require('../src');
25-
let firestore;
25+
const createInstance = require('../test/util/helpers').createInstance;
2626

2727
const PROJECT_ID = 'test-project';
2828
const DATABASE_ROOT = `projects/${PROJECT_ID}/databases/(default)`;
@@ -32,17 +32,6 @@ const DOCUMENT_NAME = `${COLLECTION_ROOT}/documentId`;
3232
// Change the argument to 'console.log' to enable debug output.
3333
Firestore.setLogFunction(() => {});
3434

35-
function createInstance() {
36-
let firestore = new Firestore({
37-
projectId: PROJECT_ID,
38-
sslCreds: grpc.credentials.createInsecure(),
39-
timestampsInSnapshots: true,
40-
keyFilename: './test/fake-certificate.json',
41-
});
42-
43-
return firestore._ensureClient().then(() => firestore);
44-
}
45-
4635
function commit(transaction, writes, err) {
4736
let proto = {
4837
database: DATABASE_ROOT,
@@ -183,7 +172,7 @@ function query(transaction) {
183172
structuredQuery: {
184173
from: [
185174
{
186-
collectionId: 'col',
175+
collectionId: 'collectionId',
187176
},
188177
],
189178
where: {
@@ -224,66 +213,61 @@ function query(transaction) {
224213
};
225214
}
226215

227-
function runTransaction(callback, request) {
228-
let requests = Array.prototype.slice.call(arguments, 1);
229-
230-
firestore._firestoreClient._innerApiCalls.beginTransaction = function(
231-
actual, options, callback) {
232-
request = requests.shift();
233-
assert.equal(request.type, 'begin');
234-
assert.deepEqual(actual, request.request);
235-
callback(request.error, request.response);
236-
};
237-
238-
firestore._firestoreClient._innerApiCalls.commit = function(
239-
actual, options, callback) {
240-
request = requests.shift();
241-
assert.equal(request.type, 'commit');
242-
assert.deepEqual(actual, request.request);
243-
callback(request.error, request.response);
244-
};
245-
246-
firestore._firestoreClient._innerApiCalls.rollback = function(
247-
actual, options, callback) {
248-
request = requests.shift();
249-
assert.equal(request.type, 'rollback');
250-
assert.deepEqual(actual, request.request);
251-
callback(request.error, request.response);
252-
};
253-
254-
firestore._firestoreClient._innerApiCalls.batchGetDocuments = function(
255-
actual) {
256-
request = requests.shift();
257-
assert.equal(request.type, 'getDocument');
258-
assert.deepEqual(actual, request.request);
259-
return request.stream;
260-
};
261-
262-
firestore._firestoreClient._innerApiCalls.runQuery = function(actual) {
263-
request = requests.shift();
264-
assert.equal(request.type, 'query');
265-
assert.deepEqual(actual, request.request);
266-
return request.stream;
216+
/**
217+
* Asserts that the given transaction function issues the expected requests.
218+
*/
219+
function runTransaction(transactionCallback, ...expectedRequests) {
220+
const overrides = {
221+
beginTransaction: (actual, options, callback) => {
222+
const request = expectedRequests.shift();
223+
assert.equal(request.type, 'begin');
224+
assert.deepEqual(actual, request.request);
225+
callback(request.error, request.response);
226+
},
227+
commit: (actual, options, callback) => {
228+
const request = expectedRequests.shift();
229+
assert.equal(request.type, 'commit');
230+
assert.deepEqual(actual, request.request);
231+
callback(request.error, request.response);
232+
},
233+
rollback: (actual, options, callback) => {
234+
const request = expectedRequests.shift();
235+
assert.equal(request.type, 'rollback');
236+
assert.deepEqual(actual, request.request);
237+
callback(request.error, request.response);
238+
},
239+
batchGetDocuments: (actual) => {
240+
const request = expectedRequests.shift();
241+
assert.equal(request.type, 'getDocument');
242+
assert.deepEqual(actual, request.request);
243+
return request.stream;
244+
},
245+
runQuery: (actual) => {
246+
const request = expectedRequests.shift();
247+
assert.equal(request.type, 'query');
248+
assert.deepEqual(actual, request.request);
249+
return request.stream;
250+
}
267251
};
268252

269-
return firestore.runTransaction(callback)
270-
.then(val => {
271-
assert.equal(requests.length, 0);
272-
return val;
273-
})
274-
.catch(err => {
275-
assert.equal(requests.length, 0);
276-
return Promise.reject(err);
277-
});
253+
return createInstance(overrides).then(firestore => {
254+
return firestore
255+
.runTransaction(transaction => {
256+
const docRef = firestore.doc('collectionId/documentId');
257+
return transactionCallback(transaction, docRef);
258+
})
259+
.then(val => {
260+
assert.equal(expectedRequests.length, 0);
261+
return val;
262+
})
263+
.catch(err => {
264+
assert.equal(expectedRequests.length, 0);
265+
return Promise.reject(err);
266+
});
267+
});
278268
}
279269

280270
describe('successful transactions', function() {
281-
beforeEach(() => {
282-
return createInstance().then(firestoreInstance => {
283-
firestore = firestoreInstance;
284-
});
285-
});
286-
287271
it('empty transaction', function() {
288272
return runTransaction(() => {
289273
return Promise.resolve();
@@ -300,30 +284,36 @@ describe('successful transactions', function() {
300284
});
301285

302286
describe('failed transactions', function() {
303-
beforeEach(() => {
304-
return createInstance().then(firestoreInstance => {
305-
firestore = firestoreInstance;
306-
});
307-
});
308-
309287
it('requires update function', function() {
310-
assert.throws(function() {
311-
return runTransaction();
312-
}, /Argument "updateFunction" is not a valid function\./);
288+
const overrides = {
289+
beginTransaction: () => {
290+
assert.fail();
291+
}
292+
};
293+
294+
return createInstance(overrides).then(firestore => {
295+
assert.throws(function() {
296+
return firestore.runTransaction();
297+
}, /Argument "updateFunction" is not a valid function\./);
298+
});
313299
});
314300

315301
it('requires valid retry number', function() {
316-
firestore._firestoreClient._innerApiCalls.beginTransaction = function() {
317-
assert.fail();
302+
const overrides = {
303+
beginTransaction: () => {
304+
assert.fail();
305+
}
318306
};
319307

320-
assert.throws(function() {
321-
firestore.runTransaction(() => {}, {maxAttempts: 'foo'});
322-
}, /Argument "transactionOptions.maxAttempts" is not a valid integer\./);
308+
return createInstance(overrides).then(firestore => {
309+
assert.throws(function() {
310+
firestore.runTransaction(() => {}, {maxAttempts: 'foo'});
311+
}, /Argument "transactionOptions.maxAttempts" is not a valid integer\./);
323312

324-
assert.throws(function() {
325-
firestore.runTransaction(() => {}, {maxAttempts: 0});
326-
}, /Argument "transactionOptions.maxAttempts" is not a valid integer\./);
313+
assert.throws(function() {
314+
firestore.runTransaction(() => {}, {maxAttempts: 0});
315+
}, /Argument "transactionOptions.maxAttempts" is not a valid integer\./);
316+
});
327317
});
328318

329319
it('requires a promise', function() {
@@ -339,19 +329,22 @@ describe('failed transactions', function() {
339329
});
340330

341331
it('handles exception', function() {
342-
firestore.request = function() {
343-
return Promise.reject(new Error('Expected exception'));
344-
};
345-
346-
return runTransaction(() => {
347-
return Promise.resolve();
348-
})
349-
.then(() => {
350-
throw new Error('Unexpected success in Promise');
351-
})
352-
.catch(err => {
353-
assert.equal(err.message, 'Expected exception');
354-
});
332+
return createInstance().then(firestore => {
333+
firestore.request = function() {
334+
return Promise.reject(new Error('Expected exception'));
335+
};
336+
337+
return firestore
338+
.runTransaction(() => {
339+
return Promise.resolve();
340+
})
341+
.then(() => {
342+
throw new Error('Unexpected success in Promise');
343+
})
344+
.catch(err => {
345+
assert.equal(err.message, 'Expected exception');
346+
});
347+
});
355348
});
356349

357350
it('doesn\'t retry on callback failure', function() {
@@ -438,17 +431,8 @@ describe('failed transactions', function() {
438431
});
439432

440433
describe('transaction operations', function() {
441-
let docRef;
442-
443-
beforeEach(() => {
444-
return createInstance().then(firestoreInstance => {
445-
firestore = firestoreInstance;
446-
docRef = firestore.doc('collectionId/documentId');
447-
});
448-
});
449-
450434
it('support get with document ref', function() {
451-
return runTransaction(transaction => {
435+
return runTransaction((transaction, docRef) => {
452436
return transaction.get(docRef).then(doc => {
453437
assert.equal(doc.id, 'documentId');
454438
});
@@ -471,7 +455,7 @@ describe('transaction operations', function() {
471455

472456
it('enforce that gets come before writes', function() {
473457
return runTransaction(
474-
transaction => {
458+
(transaction, docRef) => {
475459
transaction.set(docRef, {foo: 'bar'});
476460
return transaction.get(docRef);
477461
},
@@ -488,19 +472,19 @@ describe('transaction operations', function() {
488472
});
489473

490474
it('support get with query', function() {
491-
return runTransaction(transaction => {
492-
let query = firestore.collection('col').where('foo', '==', 'bar');
475+
return runTransaction((transaction, docRef) => {
476+
let query = docRef.parent.where('foo', '==', 'bar');
493477
return transaction.get(query).then(results => {
494478
assert.equal(results.docs[0].id, 'documentId');
495479
});
496480
}, begin(), query(), commit());
497481
});
498482

499483
it('support getAll', function() {
500-
const firstDoc = firestore.doc('collectionId/firstDocument');
501-
const secondDoc = firestore.doc('collectionId/secondDocument');
484+
return runTransaction((transaction, docRef) => {
485+
const firstDoc = docRef.parent.doc('firstDocument');
486+
const secondDoc = docRef.parent.doc('secondDocument');
502487

503-
return runTransaction(transaction => {
504488
return transaction.getAll(firstDoc, secondDoc).then(docs => {
505489
assert.equal(docs.length, 2);
506490
assert.equal(docs[0].id, 'firstDocument');
@@ -511,7 +495,7 @@ describe('transaction operations', function() {
511495

512496
it('enforce that getAll come before writes', function() {
513497
return runTransaction(
514-
transaction => {
498+
(transaction, docRef) => {
515499
transaction.set(docRef, {foo: 'bar'});
516500
return transaction.getAll(docRef);
517501
},
@@ -538,7 +522,7 @@ describe('transaction operations', function() {
538522
},
539523
};
540524

541-
return runTransaction(transaction => {
525+
return runTransaction((transaction, docRef) => {
542526
transaction.create(docRef, {});
543527
return Promise.resolve();
544528
}, begin(), commit(null, [create]));
@@ -570,7 +554,7 @@ describe('transaction operations', function() {
570554
},
571555
};
572556

573-
return runTransaction(transaction => {
557+
return runTransaction((transaction, docRef) => {
574558
transaction.update(docRef, {'a.b': 'c'});
575559
transaction.update(docRef, 'a.b', 'c');
576560
transaction.update(docRef, new Firestore.FieldPath('a', 'b'), 'c');
@@ -591,7 +575,7 @@ describe('transaction operations', function() {
591575
},
592576
};
593577

594-
return runTransaction(transaction => {
578+
return runTransaction((transaction, docRef) => {
595579
transaction.set(docRef, {'a.b': 'c'});
596580
return Promise.resolve();
597581
}, begin(), commit(null, [set]));
@@ -613,7 +597,7 @@ describe('transaction operations', function() {
613597
},
614598
};
615599

616-
return runTransaction(transaction => {
600+
return runTransaction((transaction, docRef) => {
617601
transaction.set(docRef, {'a.b': 'c'}, {merge: true});
618602
return Promise.resolve();
619603
}, begin(), commit(null, [set]));
@@ -624,7 +608,7 @@ describe('transaction operations', function() {
624608
delete: DOCUMENT_NAME,
625609
};
626610

627-
return runTransaction(transaction => {
611+
return runTransaction((transaction, docRef) => {
628612
transaction.delete(docRef);
629613
return Promise.resolve();
630614
}, begin(), commit(null, [remove]));
@@ -642,7 +626,7 @@ describe('transaction operations', function() {
642626
},
643627
};
644628

645-
return runTransaction(transaction => {
629+
return runTransaction((transaction, docRef) => {
646630
transaction.delete(docRef).set(docRef, {});
647631
return Promise.resolve();
648632
}, begin(), commit(null, [remove, set]));

‎test/util/helpers.ts

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/**
2+
* Copyright 2018 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict';
18+
19+
import {GrpcClient} from 'google-gax';
20+
21+
import Firestore from '../../src';
22+
23+
/* tslint:disable:no-any */
24+
const grpc = new GrpcClient({} as any).grpc;
25+
const SSL_CREDENTIALS = (grpc.credentials as any).createInsecure();
26+
/* tslint:enable:no-any */
27+
28+
const PROJECT_ID = 'test-project';
29+
30+
/**
31+
* Interface that defines the request handlers used by Firestore.
32+
*/
33+
export interface ApiOverride {
34+
beginTransaction(request, options, callback): void;
35+
commit(request, options, callback): void;
36+
rollback(request, options, callback): void;
37+
listCollectionIds(request, options, callback): void;
38+
batchGetDocuments(request): NodeJS.ReadableStream;
39+
runQuery(request): NodeJS.ReadableStream;
40+
listen(): NodeJS.ReadWriteStream;
41+
}
42+
43+
/**
44+
* Creates a new Firestore instance for testing. Request handlers can be
45+
* overridden by providing `apiOverrides`.
46+
*
47+
* @param {ApiOverride} apiOverrides An object with the request handlers to
48+
* override.
49+
* @param {Object} firestoreSettings Firestore Settings to configure the client.
50+
* @return {Firestore} A new Firestore client.
51+
*/
52+
export function createInstance(
53+
apiOverrides?: ApiOverride, firestoreSettings?: {}): Promise<Firestore> {
54+
const initializationOptions = Object.assign(
55+
{
56+
projectId: PROJECT_ID,
57+
sslCreds: SSL_CREDENTIALS,
58+
timestampsInSnapshots: true,
59+
keyFilename: './test/fake-certificate.json',
60+
},
61+
firestoreSettings);
62+
63+
const firestore = new Firestore(initializationOptions);
64+
65+
return firestore._ensureClient().then(() => {
66+
if (apiOverrides) {
67+
Object.keys(apiOverrides).forEach(override => {
68+
firestore._firestoreClient._innerApiCalls[override] =
69+
apiOverrides[override];
70+
});
71+
}
72+
return firestore;
73+
});
74+
}

‎test/watch.js

+33-36
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const DocumentReference = reference.DocumentReference;
2929
const DocumentSnapshot =
3030
require('../src/document')(DocumentReference).DocumentSnapshot;
3131
const Backoff = require('../src/backoff')(Firestore);
32+
const createInstance = require('../test/util/helpers').createInstance;
3233

3334
// Change the argument to 'console.log' to enable debug output.
3435
Firestore.setLogFunction(() => {});
@@ -37,18 +38,6 @@ let PROJECT_ID = process.env.PROJECT_ID;
3738
if (!PROJECT_ID) {
3839
PROJECT_ID = 'test-project';
3940
}
40-
41-
function createInstance() {
42-
let firestore = new Firestore({
43-
projectId: PROJECT_ID,
44-
sslCreds: grpc.credentials.createInsecure(),
45-
timestampsInSnapshots: true,
46-
keyFilename: './test/fake-certificate.json',
47-
});
48-
49-
return firestore._ensureClient().then(() => firestore);
50-
}
51-
5241
/**
5342
* Asserts that the given list of docs match.
5443
* @param actual The computed docs array.
@@ -192,15 +181,15 @@ class DeferredListener {
192181
* sequential invocations of the Listen API.
193182
*/
194183
class StreamHelper {
195-
/**
196-
* @param firestore The Firestore client.
197-
*/
198-
constructor(firestore) {
184+
constructor() {
199185
this.streamCount = 0;
200186
this.deferredListener = new DeferredListener();
187+
}
201188

202-
// Create a mock backend whose stream we can return.
203-
firestore._firestoreClient._innerApiCalls.listen = () => {
189+
/** Returns the GAPIC callback to use with this stream helper. */
190+
getListenCallback() {
191+
return () => {
192+
// Create a mock backend whose stream we can return.
204193
++this.streamCount;
205194

206195
this.readStream = through.obj();
@@ -585,30 +574,36 @@ describe('Query watch', function() {
585574
};
586575
};
587576

577+
/** The GAPIC callback that executes the listen. */
578+
let listenCallback;
579+
588580
beforeEach(function() {
589581
// We are intentionally skipping the delays to ensure fast test execution.
590582
// The retry semantics are uneffected by this, as we maintain their
591583
// asynchronous behavior.
592584
Backoff.setTimeoutHandler(setImmediate);
593585

594-
return createInstance().then(firestoreClient => {
595-
firestore = firestoreClient;
586+
targetId = 0x1;
596587

597-
targetId = 0x1;
588+
streamHelper = new StreamHelper();
589+
listenCallback = streamHelper.getListenCallback();
598590

599-
streamHelper = new StreamHelper(firestore);
600-
watchHelper =
601-
new WatchHelper(streamHelper, firestore.collection('col'), targetId);
591+
return createInstance({listen: () => listenCallback()})
592+
.then(firestoreClient => {
593+
firestore = firestoreClient;
602594

603-
colRef = firestore.collection('col');
595+
watchHelper = new WatchHelper(
596+
streamHelper, firestore.collection('col'), targetId);
604597

605-
doc1 = firestore.doc('col/doc1');
606-
doc2 = firestore.doc('col/doc2');
607-
doc3 = firestore.doc('col/doc3');
608-
doc4 = firestore.doc('col/doc4');
598+
colRef = firestore.collection('col');
609599

610-
lastSnapshot = EMPTY;
611-
});
600+
doc1 = firestore.doc('col/doc1');
601+
doc2 = firestore.doc('col/doc2');
602+
doc3 = firestore.doc('col/doc3');
603+
doc4 = firestore.doc('col/doc4');
604+
605+
lastSnapshot = EMPTY;
606+
});
612607
});
613608

614609
afterEach(function() {
@@ -1026,8 +1021,8 @@ describe('Query watch', function() {
10261021
lastSnapshot =
10271022
snapshotsEqual(lastSnapshot, 1, results, EMPTY);
10281023

1029-
// Return a stream that always errors on write
1030-
firestore._firestoreClient._innerApiCalls.listen = () => {
1024+
listenCallback = () => {
1025+
// Return a stream that always errors on write
10311026
++streamHelper.streamCount;
10321027
return through.obj((chunk, enc, callback) => {
10331028
callback(new Error(
@@ -2085,11 +2080,13 @@ describe('DocumentReference watch', function() {
20852080
// asynchronous behavior.
20862081
Backoff.setTimeoutHandler(setImmediate);
20872082

2088-
return createInstance().then(firestoreClient => {
2083+
targetId = 0x1;
2084+
streamHelper = new StreamHelper(firestore);
2085+
2086+
const overrides = {listen: streamHelper.getListenCallback()};
2087+
return createInstance(overrides).then(firestoreClient => {
20892088
firestore = firestoreClient;
2090-
targetId = 0x1;
20912089
doc = firestore.doc('col/doc');
2092-
streamHelper = new StreamHelper(firestore);
20932090
watchHelper = new WatchHelper(streamHelper, doc, targetId);
20942091
});
20952092
});

‎test/write-batch.js

+50-58
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,13 @@ const gax = require('google-gax');
2121
const grpc = new gax.GrpcClient().grpc;
2222

2323
const Firestore = require('../src');
24+
const createInstance = require('../test/util/helpers').createInstance;
2425

2526
// Change the argument to 'console.log' to enable debug output.
2627
Firestore.setLogFunction(() => {});
2728

2829
const PROJECT_ID = 'test-project';
2930

30-
function createInstance() {
31-
let firestore = new Firestore({
32-
projectId: PROJECT_ID,
33-
sslCreds: grpc.credentials.createInsecure(),
34-
timestampsInSnapshots: true,
35-
keyFilename: './test/fake-certificate.json',
36-
});
37-
38-
return firestore._ensureClient().then(() => firestore);
39-
}
40-
4131
describe('set() method', function() {
4232
let firestore;
4333
let writeBatch;
@@ -152,12 +142,8 @@ describe('batch support', function() {
152142
let writeBatch;
153143

154144
beforeEach(() => {
155-
return createInstance().then(firestoreClient => {
156-
firestore = firestoreClient;
157-
writeBatch = firestore.batch();
158-
159-
firestore._firestoreClient._innerApiCalls.commit = function(
160-
request, options, callback) {
145+
const overrides = {
146+
commit: (request, options, callback) => {
161147
assert.deepEqual(request, {
162148
database: `projects/${PROJECT_ID}/databases/(default)`,
163149
writes: [
@@ -215,8 +201,8 @@ describe('batch support', function() {
215201
seconds: 0,
216202
},
217203
writeResults: [
218-
// This write result conforms to the Write + DocumentTransform and
219-
// won't be returned in the response.
204+
// This write result conforms to the Write +
205+
// DocumentTransform and won't be returned in the response.
220206
{
221207
updateTime: {
222208
nanos: 1337,
@@ -249,7 +235,11 @@ describe('batch support', function() {
249235
},
250236
],
251237
});
252-
};
238+
}
239+
};
240+
return createInstance(overrides).then(firestoreClient => {
241+
firestore = firestoreClient;
242+
writeBatch = firestore.batch();
253243
});
254244
});
255245

@@ -331,35 +321,38 @@ describe('batch support', function() {
331321
});
332322

333323
it('can return same write result', function() {
334-
firestore._firestoreClient._innerApiCalls.commit = function(
335-
request, options, callback) {
336-
callback(null, {
337-
commitTime: {
338-
nanos: 0,
339-
seconds: 0,
340-
},
341-
writeResults: [
342-
{
343-
updateTime: {
344-
nanos: 0,
345-
seconds: 0,
346-
},
347-
},
348-
{
349-
updateTime: {},
324+
const overrides = {
325+
commit: (request, options, callback) => {
326+
callback(null, {
327+
commitTime: {
328+
nanos: 0,
329+
seconds: 0,
350330
},
351-
],
352-
});
331+
writeResults: [
332+
{
333+
updateTime: {
334+
nanos: 0,
335+
seconds: 0,
336+
},
337+
},
338+
{
339+
updateTime: {},
340+
},
341+
],
342+
});
343+
}
353344
};
354345

355-
let documentName = firestore.doc('col/doc');
346+
return createInstance(overrides).then(firestore => {
347+
let documentName = firestore.doc('col/doc');
356348

357-
let batch = firestore.batch();
358-
batch.set(documentName, {});
359-
batch.set(documentName, {});
349+
let batch = firestore.batch();
350+
batch.set(documentName, {});
351+
batch.set(documentName, {});
360352

361-
return batch.commit().then(results => {
362-
assert.ok(results[0].isEqual(results[1]));
353+
return batch.commit().then(results => {
354+
assert.ok(results[0].isEqual(results[1]));
355+
});
363356
});
364357
});
365358

@@ -368,29 +361,28 @@ describe('batch support', function() {
368361
// we are running on GCF.
369362
process.env.FUNCTION_TRIGGER_TYPE = 'http-trigger';
370363

371-
return createInstance().then(firestore => {
372-
firestore._preferTransactions = true;
373-
firestore._lastSuccessfulRequest = null;
374-
375-
let beginCalled = 0;
376-
let commitCalled = 0;
364+
let beginCalled = 0;
365+
let commitCalled = 0;
377366

378-
firestore._firestoreClient._innerApiCalls.beginTransaction = function(
379-
actual, options, callback) {
367+
const overrides = {
368+
beginTransaction: (actual, options, callback) => {
380369
++beginCalled;
381370
callback(null, {transaction: 'foo'});
382-
};
383-
384-
firestore._firestoreClient._innerApiCalls.commit = function(
385-
actual, options, callback) {
371+
},
372+
commit: (actual, options, callback) => {
386373
++commitCalled;
387374
callback(null, {
388375
commitTime: {
389376
nanos: 0,
390377
seconds: 0,
391378
},
392-
});
393-
};
379+
})
380+
}
381+
};
382+
383+
return createInstance(overrides).then(firestore => {
384+
firestore._preferTransactions = true;
385+
firestore._lastSuccessfulRequest = null;
394386

395387
return firestore.batch()
396388
.commit()

‎tsconfig.json

+5
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
"src/*.ts",
1515
"src/**/*.js",
1616
"test/*.js",
17+
"test/*.ts",
18+
"test/**/*.ts",
1719
"system-test/*.js",
1820
"conformance/*.js"
21+
],
22+
"exclude": [
23+
"test/typescript.ts"
1924
]
2025
}

0 commit comments

Comments
 (0)
Please sign in to comment.