Skip to content

Commit 1742398

Browse files
test(deleteReferences): Add more test coverage
1 parent 4a4a982 commit 1742398

File tree

3 files changed

+177
-30
lines changed

3 files changed

+177
-30
lines changed

test/functions/index.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ module.exports.deleteReferencesWithMasterParam = integrify({
7373
foreignKey: 'masterId',
7474
},
7575
{
76-
collection: 'somecoll/{masterId}/detail2',
76+
collection: 'somecoll/$masterId/detail2',
7777
foreignKey: 'masterId',
7878
},
7979
],
@@ -98,7 +98,32 @@ module.exports.deleteReferencesWithSnapshotFields = integrify({
9898
foreignKey: 'masterId',
9999
},
100100
{
101-
collection: 'somecoll/{testId}/detail2',
101+
collection: 'somecoll/$testId/detail2',
102+
foreignKey: 'masterId',
103+
},
104+
],
105+
hooks: {
106+
pre: (snap, context) => {
107+
setState({
108+
snap,
109+
context,
110+
});
111+
},
112+
},
113+
});
114+
115+
module.exports.deleteReferencesWithMissingFields = integrify({
116+
rule: 'DELETE_REFERENCES',
117+
source: {
118+
collection: 'master',
119+
},
120+
targets: [
121+
{
122+
collection: 'detail1',
123+
foreignKey: 'masterId',
124+
},
125+
{
126+
collection: 'somecoll/$testId/detail2',
102127
foreignKey: 'masterId',
103128
},
104129
],

test/functions/integrify.rules.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ module.exports = [
5757
foreignKey: 'masterId',
5858
},
5959
{
60-
collection: 'somecoll/{masterId}/detail2',
60+
collection: 'somecoll/$masterId/detail2',
6161
foreignKey: 'masterId',
6262
},
6363
],
@@ -74,7 +74,24 @@ module.exports = [
7474
foreignKey: 'masterId',
7575
},
7676
{
77-
collection: 'somecoll/{testId}/detail2',
77+
collection: 'somecoll/$testId/detail2',
78+
foreignKey: 'masterId',
79+
},
80+
],
81+
},
82+
{
83+
rule: 'DELETE_REFERENCES',
84+
name: 'deleteReferencesWithMissingFields',
85+
source: {
86+
collection: 'master',
87+
},
88+
targets: [
89+
{
90+
collection: 'detail1',
91+
foreignKey: 'masterId',
92+
},
93+
{
94+
collection: 'somecoll/$testId/detail2',
7895
foreignKey: 'masterId',
7996
},
8097
],

test/unit.test.js

Lines changed: 131 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
const { credentials, makeid, sleep } = require('./util');
22
const fft = require('firebase-functions-test')(
3-
{ projectId: credentials.projectId },
3+
{
4+
projectId: credentials.projectId,
5+
},
46
credentials.serviceAccountKeyFile
57
);
68
const test = require('ava');
79
const { integrify } = require('../lib');
10+
const { replaceReferenceWithFields } = require('../lib/common');
811
const { getState, setState } = require('./functions/stateMachine');
912

1013
const admin = require('firebase-admin');
@@ -48,6 +51,8 @@ testsuites.forEach(testsuite => {
4851
t.true(sut.replicateMasterToDetail.name === 'cloudFunction');
4952
t.truthy(sut.replicateMasterToDetail.run);
5053
});
54+
test(`[${name}] test basic variable swap`, async t =>
55+
testVariableSwap(sut, t, name));
5156
test(`[${name}] test simple replicate attributes`, async t =>
5257
testReplicateAttributes(sut, t, name));
5358
test(`[${name}] test simple delete references`, async t =>
@@ -60,30 +65,93 @@ testsuites.forEach(testsuite => {
6065
test(`[${name}] test delete with snapshot fields in target reference`, async t =>
6166
testDeleteSnapshotFieldReferences(sut, t, name));
6267
test(`[${name}] test delete with missing snapshot fields in target reference`, async t =>
63-
testDeleteMissingSnapshotFieldReferences(sut, t, name));
68+
testDeleteMissingFieldsReferences(sut, t, name));
6469
});
6570

71+
async function testVariableSwap(sut, t, name) {
72+
// test no fields
73+
let collectionId = makeid();
74+
let targetCollection = 'collection';
75+
let doc = {
76+
collectionId,
77+
};
78+
79+
let result = replaceReferenceWithFields(doc, targetCollection);
80+
81+
t.false(result.hasFields);
82+
t.is(result.targetCollection, 'collection');
83+
84+
// Test one field
85+
targetCollection = 'collection/$collectionId/some_detail';
86+
87+
result = replaceReferenceWithFields(doc, targetCollection);
88+
89+
t.true(result.hasFields);
90+
t.is(result.targetCollection, `collection/${collectionId}/some_detail`);
91+
92+
// Test multiple fields
93+
const testId = makeid();
94+
const userId = makeid();
95+
targetCollection = 'collection/$testId/some_detail/$userId';
96+
doc = {
97+
collectionId,
98+
testId,
99+
userId,
100+
};
101+
102+
result = replaceReferenceWithFields(doc, targetCollection);
103+
104+
t.true(result.hasFields);
105+
t.is(result.targetCollection, `collection/${testId}/some_detail/${userId}`);
106+
107+
// Test missing field
108+
targetCollection = 'collection/$collectionId/some_detail';
109+
110+
const error = t.throws(() => {
111+
replaceReferenceWithFields({}, targetCollection);
112+
});
113+
t.is(error.message, 'integrify: Missing dynamic reference: [$collectionId]');
114+
115+
await t.pass();
116+
}
117+
66118
async function testReplicateAttributes(sut, t, name) {
67119
// Add a couple of detail documents to follow master
68120
const masterId = makeid();
69-
await db.collection('detail1').add({ masterId: masterId });
121+
await db.collection('detail1').add({
122+
masterId: masterId,
123+
});
70124
const nestedDocRef = db.collection('somecoll').doc('somedoc');
71-
await nestedDocRef.set({ x: 1 });
72-
await nestedDocRef.collection('detail2').add({ masterId: masterId });
125+
await nestedDocRef.set({
126+
x: 1,
127+
});
128+
await nestedDocRef.collection('detail2').add({
129+
masterId: masterId,
130+
});
73131

74132
// Call trigger to replicate attributes from master
75133
const beforeSnap = fft.firestore.makeDocumentSnapshot(
76134
{},
77135
`master/${masterId}`
78136
);
79137
const afterSnap = fft.firestore.makeDocumentSnapshot(
80-
{ masterField1: 'after1', masterField3: 'after3' },
138+
{
139+
masterField1: 'after1',
140+
masterField3: 'after3',
141+
},
81142
`master/${masterId}`
82143
);
83144
const change = fft.makeChange(beforeSnap, afterSnap);
84145
const wrapped = fft.wrap(sut.replicateMasterToDetail);
85-
setState({ change: null, context: null });
86-
await wrapped(change, { params: { masterId: masterId } });
146+
setState({
147+
change: null,
148+
context: null,
149+
});
150+
await wrapped(change, {
151+
params: {
152+
masterId: masterId,
153+
},
154+
});
87155

88156
// Assert pre-hook was called (only for rules-in-situ)
89157
if (name === 'rules-in-situ') {
@@ -111,22 +179,34 @@ async function testReplicateAttributes(sut, t, name) {
111179

112180
// Assert irrelevant update is safely ignored
113181
const irrelevantAfterSnap = fft.firestore.makeDocumentSnapshot(
114-
{ masterFieldIrrelevant: 'whatever' },
182+
{
183+
masterFieldIrrelevant: 'whatever',
184+
},
115185
`master/${masterId}`
116186
);
117187
const irreleventChange = fft.makeChange(beforeSnap, irrelevantAfterSnap);
118-
await wrapped(irreleventChange, { params: { masterId: masterId } });
188+
await wrapped(irreleventChange, {
189+
params: {
190+
masterId: masterId,
191+
},
192+
});
119193

120194
await t.pass();
121195
}
122196

123197
async function testDeleteReferences(sut, t, name) {
124198
// Create some docs referencing master doc
125199
const masterId = makeid();
126-
await db.collection('detail1').add({ masterId: masterId });
200+
await db.collection('detail1').add({
201+
masterId: masterId,
202+
});
127203
const nestedDocRef = db.collection('somecoll').doc('somedoc');
128-
await nestedDocRef.set({ x: 1 });
129-
await nestedDocRef.collection('detail2').add({ masterId: masterId });
204+
await nestedDocRef.set({
205+
x: 1,
206+
});
207+
await nestedDocRef.collection('detail2').add({
208+
masterId: masterId,
209+
});
130210
await assertQuerySizeEventually(
131211
db
132212
.collection('somecoll')
@@ -139,8 +219,15 @@ async function testDeleteReferences(sut, t, name) {
139219
// Trigger function to delete references
140220
const snap = fft.firestore.makeDocumentSnapshot({}, `master/${masterId}`);
141221
const wrapped = fft.wrap(sut.deleteReferencesToMaster);
142-
setState({ snap: null, context: null });
143-
await wrapped(snap, { params: { masterId: masterId } });
222+
setState({
223+
snap: null,
224+
context: null,
225+
});
226+
await wrapped(snap, {
227+
params: {
228+
masterId: masterId,
229+
},
230+
});
144231

145232
// Assert pre-hook was called (only for rules-in-situ)
146233
if (name === 'rules-in-situ') {
@@ -265,6 +352,7 @@ async function testDeleteSnapshotFieldReferences(sut, t, name) {
265352
await wrapped(snap, {
266353
params: {
267354
masterId: masterId,
355+
testId: testId,
268356
},
269357
});
270358

@@ -293,7 +381,7 @@ async function testDeleteSnapshotFieldReferences(sut, t, name) {
293381
t.pass();
294382
}
295383

296-
async function testDeleteMissingSnapshotFieldReferences(sut, t, name) {
384+
async function testDeleteMissingFieldsReferences(sut, t, name) {
297385
// Create some docs referencing master doc
298386
const masterId = makeid();
299387
const testId = makeid();
@@ -318,17 +406,22 @@ async function testDeleteMissingSnapshotFieldReferences(sut, t, name) {
318406

319407
// Trigger function to delete references
320408
const snap = fft.firestore.makeDocumentSnapshot({}, `master/${masterId}`);
321-
const wrapped = fft.wrap(sut.deleteReferencesWithSnapshotFields);
409+
const wrapped = fft.wrap(sut.deleteReferencesWithMissingFields);
322410
setState({
323411
snap: null,
324412
context: null,
325413
});
326-
await wrapped(snap, {
327-
params: {
328-
masterId: masterId,
329-
},
414+
415+
const error = await t.throwsAsync(async () => {
416+
await wrapped(snap, {
417+
params: {
418+
masterId: masterId,
419+
},
420+
});
330421
});
331422

423+
t.is(error.message, 'Error: integrify: Missing dynamic reference: [$testId]');
424+
332425
// Assert pre-hook was called (only for rules-in-situ)
333426
if (name === 'rules-in-situ') {
334427
const state = getState();
@@ -360,15 +453,19 @@ async function testMaintainCount(sut, t) {
360453
await db
361454
.collection('articles')
362455
.doc(articleId)
363-
.set({ favoritesCount: 0 });
456+
.set({
457+
favoritesCount: 0,
458+
});
364459

365460
// Favorite the article a few times
366461
const NUM_TIMES_TO_FAVORITE = 5;
367462
const wrappedUpdater = fft.wrap(sut.maintainFavoritesCount);
368463
const promises = [];
369464
const emptySnap = fft.firestore.makeDocumentSnapshot({});
370465
const snap = fft.firestore.makeDocumentSnapshot(
371-
{ articleId: articleId },
466+
{
467+
articleId: articleId,
468+
},
372469
`favorites/${makeid()}`
373470
);
374471
for (let i = 1; i <= NUM_TIMES_TO_FAVORITE; ++i) {
@@ -413,10 +510,18 @@ async function testMaintainCount(sut, t) {
413510
}
414511

415512
test('test error conditions', async t => {
416-
t.throws(() => integrify({}), { message: /Input must be rule or config/i });
417-
t.throws(() => integrify({ rule: 'UNKNOWN_RULE_4a4e261a2e37' }), {
418-
message: /Unknown rule/i,
513+
t.throws(() => integrify({}), {
514+
message: /Input must be rule or config/i,
419515
});
516+
t.throws(
517+
() =>
518+
integrify({
519+
rule: 'UNKNOWN_RULE_4a4e261a2e37',
520+
}),
521+
{
522+
message: /Unknown rule/i,
523+
}
524+
);
420525
t.throws(() => require('./functions-bad-rules-file'), {
421526
message: /Unknown rule/i,
422527
});

0 commit comments

Comments
 (0)