Skip to content

Commit e4b47aa

Browse files
test(deleteReferences): Add tests for delete flag and error checks
1 parent f9e8130 commit e4b47aa

File tree

3 files changed

+326
-1
lines changed

3 files changed

+326
-1
lines changed

test/functions/index.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,70 @@ module.exports.deleteReferencesWithMissingFields = integrify({
158158
},
159159
});
160160

161+
module.exports.deleteReferencesDeleteAllSubCollections = integrify({
162+
rule: 'DELETE_REFERENCES',
163+
source: {
164+
collection: 'master/{randomId}',
165+
},
166+
targets: [
167+
{
168+
collection: 'somecoll/$testId/detail2',
169+
foreignKey: 'randomId',
170+
deleteAll: true,
171+
},
172+
],
173+
hooks: {
174+
pre: (snap, context) => {
175+
setState({
176+
snap,
177+
context,
178+
});
179+
},
180+
},
181+
});
182+
183+
module.exports.deleteReferencesDeleteAllSubCollectionErrors = integrify({
184+
rule: 'DELETE_REFERENCES',
185+
source: {
186+
collection: 'master/{randomId}',
187+
},
188+
targets: [
189+
{
190+
collection: 'master/details',
191+
foreignKey: 'randomId',
192+
deleteAll: true,
193+
},
194+
],
195+
hooks: {
196+
pre: (snap, context) => {
197+
setState({
198+
snap,
199+
context,
200+
});
201+
},
202+
},
203+
});
204+
205+
module.exports.deleteReferencesMissingArgumentsErrors = integrify({
206+
rule: 'DELETE_REFERENCES',
207+
source: {
208+
collection: 'master/{randomId}',
209+
},
210+
targets: [
211+
{
212+
collection: 'master/details',
213+
},
214+
],
215+
hooks: {
216+
pre: (snap, context) => {
217+
setState({
218+
snap,
219+
context,
220+
});
221+
},
222+
},
223+
});
224+
161225
module.exports.maintainFavoritesCount = integrify({
162226
rule: 'MAINTAIN_COUNT',
163227
source: {

test/functions/integrify.rules.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,46 @@ module.exports = [
109109
},
110110
],
111111
},
112+
{
113+
rule: 'DELETE_REFERENCES',
114+
name: 'deleteReferencesDeleteAllSubCollections',
115+
source: {
116+
collection: 'master/{randomId}',
117+
},
118+
targets: [
119+
{
120+
collection: 'somecoll/$testId/detail2',
121+
foreignKey: 'randomId',
122+
deleteAll: true,
123+
},
124+
],
125+
},
126+
{
127+
rule: 'DELETE_REFERENCES',
128+
name: 'deleteReferencesDeleteAllSubCollectionErrors',
129+
source: {
130+
collection: 'master/{randomId}',
131+
},
132+
targets: [
133+
{
134+
collection: 'master/details',
135+
foreignKey: 'randomId',
136+
deleteAll: true,
137+
},
138+
],
139+
},
140+
{
141+
rule: 'DELETE_REFERENCES',
142+
name: 'deleteReferencesMissingArgumentsErrors',
143+
source: {
144+
collection: 'master/{randomId}',
145+
},
146+
targets: [
147+
{
148+
collection: 'master/details',
149+
},
150+
],
151+
},
112152
{
113153
rule: 'MAINTAIN_COUNT',
114154
name: 'maintainFavoritesCount',

test/unit.test.js

Lines changed: 222 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ const fft = require('firebase-functions-test')(
77
);
88
const test = require('ava');
99
const { integrify } = require('../lib');
10-
const { replaceReferencesWith, getPrimaryKey } = require('../lib/common');
10+
const {
11+
replaceReferencesWith,
12+
getPrimaryKey,
13+
isSubCollection,
14+
} = require('../lib/common');
1115
const { getState, setState } = require('./functions/stateMachine');
1216

1317
const admin = require('firebase-admin');
@@ -53,6 +57,8 @@ testsuites.forEach(testsuite => {
5357
});
5458
test(`[${name}] test get primary key`, async t =>
5559
testPrimaryKey(sut, t, name));
60+
test(`[${name}] test sub-collection check`, async t =>
61+
testSubCollection(sut, t, name));
5662
test(`[${name}] test target collection parameter swap`, async t =>
5763
testTargetVariableSwap(sut, t, name));
5864
test(`[${name}] test replicate attributes`, async t =>
@@ -69,6 +75,14 @@ testsuites.forEach(testsuite => {
6975
testDeleteMissingSourceCollectionKey(sut, t, name));
7076
test(`[${name}] test delete with missing snapshot fields in target reference`, async t =>
7177
testDeleteMissingFieldsReferences(sut, t, name));
78+
79+
test(`[${name}] test delete all sub-collections in target reference`, async t =>
80+
testDeleteAllSubCollections(sut, t, name));
81+
test(`[${name}] test delete all sub-collections in target reference error`, async t =>
82+
testDeleteAllSubCollectionsError(sut, t, name));
83+
84+
test(`[${name}] test delete missing arguments error`, async t =>
85+
testDeleteMissingArgumentsError(sut, t, name));
7286
});
7387

7488
async function testPrimaryKey(sut, t, name) {
@@ -94,6 +108,44 @@ async function testPrimaryKey(sut, t, name) {
94108
await t.pass();
95109
}
96110

111+
async function testSubCollection(sut, t, name) {
112+
// Test zero key
113+
let result = isSubCollection('');
114+
t.false(result);
115+
116+
// Test one key
117+
result = isSubCollection('collection');
118+
t.false(result);
119+
120+
// Test two keys
121+
result = isSubCollection('collection1/collection2');
122+
t.false(result);
123+
124+
// Test three keys
125+
result = isSubCollection('collection1/collection2/collection3');
126+
t.true(result);
127+
128+
// Test four keys
129+
result = isSubCollection('collection1/collection2/collection3/collection4');
130+
t.false(result);
131+
132+
// Test five keys
133+
result = isSubCollection(
134+
'collection1/collection2/collection3/collection4/collection5'
135+
);
136+
t.true(result);
137+
138+
// Check incorrect format of collection
139+
result = isSubCollection('collection1//');
140+
t.false(result);
141+
result = isSubCollection('//collection1//');
142+
t.false(result);
143+
result = isSubCollection('collection1///collection2///collection3');
144+
t.true(result);
145+
146+
await t.pass();
147+
}
148+
97149
async function testTargetVariableSwap(sut, t, name) {
98150
// test no fields
99151
let collectionId = makeid();
@@ -517,6 +569,175 @@ async function testDeleteMissingFieldsReferences(sut, t, name) {
517569
t.pass();
518570
}
519571

572+
async function testDeleteAllSubCollections(sut, t, name) {
573+
// Create some docs referencing master doc
574+
const randomId = makeid();
575+
const testId = makeid();
576+
const nestedDocRef = db.collection('somecoll').doc(testId);
577+
await nestedDocRef.set({
578+
x: 1,
579+
});
580+
await nestedDocRef.collection('detail2').add({
581+
randomId: randomId,
582+
});
583+
await nestedDocRef.collection('detail3').add({
584+
randomId: randomId,
585+
});
586+
await assertQuerySizeEventually(
587+
db
588+
.collection('somecoll')
589+
.doc(testId)
590+
.collection('detail2')
591+
.where('randomId', '==', randomId),
592+
1
593+
);
594+
await assertQuerySizeEventually(
595+
db
596+
.collection('somecoll')
597+
.doc(testId)
598+
.collection('detail3')
599+
.where('randomId', '==', randomId),
600+
1
601+
);
602+
603+
// Trigger function to delete references
604+
const snap = fft.firestore.makeDocumentSnapshot(
605+
{ testId },
606+
`master/${randomId}`
607+
);
608+
const wrapped = fft.wrap(sut.deleteReferencesDeleteAllSubCollections);
609+
setState({
610+
snap: null,
611+
context: null,
612+
});
613+
await wrapped(snap, {
614+
params: {
615+
randomId: randomId,
616+
testId: testId,
617+
},
618+
});
619+
620+
// Assert pre-hook was called (only for rules-in-situ)
621+
if (name === 'rules-in-situ') {
622+
const state = getState();
623+
t.truthy(state.snap);
624+
t.truthy(state.context);
625+
t.is(state.context.params.randomId, randomId);
626+
}
627+
628+
// Assert referencing docs were deleted
629+
await assertQuerySizeEventually(
630+
db
631+
.collection('somecoll')
632+
.doc(testId)
633+
.collection('detail2')
634+
.where('randomId', '==', randomId),
635+
0
636+
);
637+
await assertQuerySizeEventually(
638+
db
639+
.collection('somecoll')
640+
.doc(testId)
641+
.collection('detail3')
642+
.where('randomId', '==', randomId),
643+
1
644+
);
645+
646+
t.pass();
647+
}
648+
649+
async function testDeleteAllSubCollectionsError(sut, t, name) {
650+
// Create some docs referencing master doc
651+
const randomId = makeid();
652+
const testId = makeid();
653+
const nestedDocRef = db.collection('somecoll').doc(testId);
654+
await nestedDocRef.set({
655+
x: 1,
656+
});
657+
await nestedDocRef.collection('detail2').add({
658+
randomId: randomId,
659+
});
660+
await assertQuerySizeEventually(
661+
db
662+
.collection('somecoll')
663+
.doc(testId)
664+
.collection('detail2')
665+
.where('randomId', '==', randomId),
666+
1
667+
);
668+
669+
// Trigger function to delete references
670+
const snap = fft.firestore.makeDocumentSnapshot(
671+
{
672+
testId,
673+
},
674+
`master/${randomId}`
675+
);
676+
const wrapped = fft.wrap(sut.deleteReferencesDeleteAllSubCollectionErrors);
677+
setState({
678+
snap: null,
679+
context: null,
680+
});
681+
const error = await t.throwsAsync(async () => {
682+
await wrapped(snap, {
683+
params: {
684+
randomId: randomId,
685+
testId: testId,
686+
},
687+
});
688+
});
689+
t.is(
690+
error.message,
691+
`integrify: [master/details] is an invalid sub-collection`
692+
);
693+
694+
// Assert pre-hook was called (only for rules-in-situ)
695+
if (name === 'rules-in-situ') {
696+
const state = getState();
697+
t.truthy(state.snap);
698+
t.truthy(state.context);
699+
t.is(state.context.params.randomId, randomId);
700+
}
701+
702+
// Assert referencing docs were deleted
703+
await assertQuerySizeEventually(
704+
db
705+
.collection('somecoll')
706+
.doc(testId)
707+
.collection('detail2')
708+
.where('randomId', '==', randomId),
709+
1
710+
);
711+
712+
t.pass();
713+
}
714+
715+
async function testDeleteMissingArgumentsError(sut, t, name) {
716+
// Create some docs referencing master doc
717+
const randomId = makeid();
718+
719+
// Trigger function to delete references
720+
const snap = fft.firestore.makeDocumentSnapshot({}, `master/${randomId}`);
721+
const wrapped = fft.wrap(sut.deleteReferencesMissingArgumentsErrors);
722+
setState({
723+
snap: null,
724+
context: null,
725+
});
726+
const error = await t.throwsAsync(async () => {
727+
await wrapped(snap, {
728+
params: {
729+
randomId: randomId,
730+
},
731+
});
732+
});
733+
t.is(
734+
error.message,
735+
'integrify: missing foreign key or set deleteAll to true'
736+
);
737+
738+
t.pass();
739+
}
740+
520741
async function testMaintainCount(sut, t) {
521742
// Create an article to be favorited
522743
const articleId = makeid();

0 commit comments

Comments
 (0)