Skip to content

Commit 0c7494c

Browse files
test(replicateAttributes): Add test for primary key
1 parent 2ed10ac commit 0c7494c

File tree

3 files changed

+125
-12
lines changed

3 files changed

+125
-12
lines changed

test/functions/index.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ module.exports.replicateMasterToDetail = integrify({
4242
module.exports.replicateMasterDeleteWhenEmpty = integrify({
4343
rule: 'REPLICATE_ATTRIBUTES',
4444
source: {
45-
collection: 'master',
45+
collection: 'master/{primaryKey}',
4646
},
4747
targets: [
4848
{
@@ -65,6 +65,30 @@ module.exports.replicateMasterDeleteWhenEmpty = integrify({
6565
},
6666
});
6767

68+
module.exports.replicateReferencesWithMissingKey = integrify({
69+
rule: 'REPLICATE_ATTRIBUTES',
70+
source: {
71+
collection: 'master/{masterId}',
72+
},
73+
targets: [
74+
{
75+
collection: 'detail1',
76+
foreignKey: 'randomId',
77+
attributeMapping: {
78+
masterDetail1: 'foreignDetail1',
79+
},
80+
},
81+
],
82+
hooks: {
83+
pre: (snap, context) => {
84+
setState({
85+
snap,
86+
context,
87+
});
88+
},
89+
},
90+
});
91+
6892
module.exports.deleteReferencesToMaster = integrify({
6993
rule: 'DELETE_REFERENCES',
7094
source: {

test/functions/integrify.rules.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ module.exports = [
3131
rule: 'REPLICATE_ATTRIBUTES',
3232
name: 'replicateMasterDeleteWhenEmpty',
3333
source: {
34-
collection: 'master',
34+
collection: 'master/{primaryKey}',
3535
},
3636
targets: [
3737
{
@@ -45,6 +45,22 @@ module.exports = [
4545
},
4646
],
4747
},
48+
{
49+
rule: 'REPLICATE_ATTRIBUTES',
50+
name: 'replicateReferencesWithMissingKey',
51+
source: {
52+
collection: 'master/{masterId}',
53+
},
54+
targets: [
55+
{
56+
collection: 'detail1',
57+
foreignKey: 'randomId',
58+
attributeMapping: {
59+
masterDetail1: 'foreignDetail1',
60+
},
61+
},
62+
],
63+
},
4864
{
4965
rule: 'DELETE_REFERENCES',
5066
name: 'deleteReferencesToMaster',

test/unit.test.js

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ admin.initializeApp({
1717
const db = admin.firestore();
1818

1919
async function clearFirestore() {
20-
const collections = ['detail1', 'detail2', 'somecoll'];
20+
const collections = ['detail1', 'detail2', 'detail3', 'somecoll'];
2121
for (const collection of collections) {
2222
const { docs } = await admin
2323
.firestore()
@@ -55,14 +55,19 @@ testsuites.forEach(testsuite => {
5555
testPrimaryKey(sut, t, name));
5656
test(`[${name}] test target collection parameter swap`, async t =>
5757
testTargetVariableSwap(sut, t, name));
58+
59+
// Standard functionality
5860
test(`[${name}] test replicate attributes`, async t =>
5961
testReplicateAttributes(sut, t, name));
6062
test(`[${name}] test delete references`, async t =>
6163
testDeleteReferences(sut, t, name));
6264
test(`[${name}] test maintain count`, async t => testMaintainCount(sut, t));
6365

66+
// Added by GitLive
6467
test(`[${name}] test replicate attributes delete when field is not there`, async t =>
6568
testReplicateAttributesDeleteEmpty(sut, t, name));
69+
test(`[${name}] test replicate attributes with missing primary key in source reference`, async t =>
70+
testReplicateMissingSourceCollectionKey(sut, t, name));
6671

6772
test(`[${name}] test delete with masterId in target reference`, async t =>
6873
testDeleteParamReferences(sut, t, name));
@@ -75,7 +80,6 @@ testsuites.forEach(testsuite => {
7580

7681
test(`[${name}] test delete all sub-collections in target reference`, async t =>
7782
testDeleteAllSubCollections(sut, t, name));
78-
7983
test(`[${name}] test delete missing arguments error`, async t =>
8084
testDeleteMissingArgumentsError(sut, t, name));
8185
});
@@ -233,9 +237,9 @@ async function testReplicateAttributes(sut, t, name) {
233237

234238
async function testReplicateAttributesDeleteEmpty(sut, t, name) {
235239
// Add a couple of detail documents to follow master
236-
const masterId = makeid();
240+
const primaryKey = makeid();
237241
await db.collection('detail1').add({
238-
tempId: masterId,
242+
tempId: primaryKey,
239243
foreignDetail1: 'foreign_detail_1',
240244
foreignDetail2: 'foreign_detail_2',
241245
});
@@ -246,13 +250,13 @@ async function testReplicateAttributesDeleteEmpty(sut, t, name) {
246250
masterDetail1: 'after1',
247251
masterDetail2: 'after2',
248252
},
249-
`master/${masterId}`
253+
`master/${primaryKey}`
250254
);
251255
const afterSnap = fft.firestore.makeDocumentSnapshot(
252256
{
253257
masterDetail2: 'after3',
254258
},
255-
`master/${masterId}`
259+
`master/${primaryKey}`
256260
);
257261
const change = fft.makeChange(beforeSnap, afterSnap);
258262
const wrapped = fft.wrap(sut.replicateMasterDeleteWhenEmpty);
@@ -262,7 +266,7 @@ async function testReplicateAttributesDeleteEmpty(sut, t, name) {
262266
});
263267
await wrapped(change, {
264268
params: {
265-
masterId: masterId,
269+
primaryKey: primaryKey,
266270
},
267271
});
268272

@@ -271,28 +275,97 @@ async function testReplicateAttributesDeleteEmpty(sut, t, name) {
271275
const state = getState();
272276
t.truthy(state.change);
273277
t.truthy(state.context);
274-
t.is(state.context.params.masterId, masterId);
278+
t.is(state.context.params.primaryKey, primaryKey);
275279
}
276280

277281
// Assert that attributes get replicated to detail documents
278282
await assertQuerySizeEventually(
279283
db
280284
.collection('detail1')
281-
.where('tempId', '==', masterId)
285+
.where('tempId', '==', primaryKey)
282286
.where('foreignDetail1', '==', 'foreign_detail_1'),
283287
0
284288
);
285289
await assertQuerySizeEventually(
286290
db
287291
.collection('detail1')
288-
.where('tempId', '==', masterId)
292+
.where('tempId', '==', primaryKey)
289293
.where('foreignDetail2', '==', 'after3'),
290294
1
291295
);
292296

293297
await t.pass();
294298
}
295299

300+
async function testReplicateMissingSourceCollectionKey(sut, t, name) {
301+
// Create some docs referencing master doc
302+
const randomId = makeid();
303+
await db.collection('detail1').add({
304+
tempId: randomId,
305+
foreignDetail1: 'foreign_detail_1',
306+
foreignDetail2: 'foreign_detail_2',
307+
});
308+
309+
// Trigger function to delete references
310+
const beforeSnap = fft.firestore.makeDocumentSnapshot(
311+
{
312+
masterDetail1: 'after1',
313+
masterDetail2: 'after2',
314+
},
315+
`master/${randomId}`
316+
);
317+
const afterSnap = fft.firestore.makeDocumentSnapshot(
318+
{
319+
masterDetail2: 'after3',
320+
},
321+
`master/${randomId}`
322+
);
323+
const change = fft.makeChange(beforeSnap, afterSnap);
324+
const wrapped = fft.wrap(sut.replicateReferencesWithMissingKey);
325+
setState({
326+
snap: null,
327+
context: null,
328+
});
329+
330+
const error = await t.throwsAsync(async () => {
331+
await wrapped(change, {
332+
params: {
333+
randomId: randomId,
334+
},
335+
});
336+
});
337+
338+
t.is(
339+
error.message,
340+
'integrify: Missing a primary key [masterId] in the source params'
341+
);
342+
343+
// Assert pre-hook was called (only for rules-in-situ)
344+
if (name === 'rules-in-situ') {
345+
const state = getState();
346+
t.is(state.snap, null);
347+
t.is(state.context, null);
348+
}
349+
350+
// Assert referencing docs were not deleted
351+
await assertQuerySizeEventually(
352+
db
353+
.collection('detail1')
354+
.where('tempId', '==', randomId)
355+
.where('foreignDetail1', '==', 'foreign_detail_1'),
356+
1
357+
);
358+
await assertQuerySizeEventually(
359+
db
360+
.collection('detail1')
361+
.where('tempId', '==', randomId)
362+
.where('foreignDetail2', '==', 'foreign_detail_2'),
363+
1
364+
);
365+
366+
t.pass();
367+
}
368+
296369
async function testDeleteReferences(sut, t, name) {
297370
// Create some docs referencing master doc
298371
const masterId = makeid();

0 commit comments

Comments
 (0)