@@ -17,7 +17,7 @@ admin.initializeApp({
1717const db = admin . firestore ( ) ;
1818
1919async 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,12 +55,20 @@ 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
67+ test ( `[${ name } ] test replicate attributes delete when field is not there` , async t =>
68+ testReplicateAttributesDeleteEmpty ( sut , t , name ) ) ;
69+ test ( `[${ name } ] test replicate attributes with missing primary key in source reference` , async t =>
70+ testReplicateMissingSourceCollectionKey ( sut , t , name ) ) ;
71+
6472 test ( `[${ name } ] test delete with masterId in target reference` , async t =>
6573 testDeleteParamReferences ( sut , t , name ) ) ;
6674 test ( `[${ name } ] test delete with snapshot fields in target reference` , async t =>
@@ -72,7 +80,6 @@ testsuites.forEach(testsuite => {
7280
7381 test ( `[${ name } ] test delete all sub-collections in target reference` , async t =>
7482 testDeleteAllSubCollections ( sut , t , name ) ) ;
75-
7683 test ( `[${ name } ] test delete missing arguments error` , async t =>
7784 testDeleteMissingArgumentsError ( sut , t , name ) ) ;
7885} ) ;
@@ -163,7 +170,9 @@ async function testReplicateAttributes(sut, t, name) {
163170
164171 // Call trigger to replicate attributes from master
165172 const beforeSnap = fft . firestore . makeDocumentSnapshot (
166- { } ,
173+ {
174+ masterField5 : 'missing' ,
175+ } ,
167176 `master/${ masterId } `
168177 ) ;
169178 const afterSnap = fft . firestore . makeDocumentSnapshot (
@@ -226,6 +235,137 @@ async function testReplicateAttributes(sut, t, name) {
226235 await t . pass ( ) ;
227236}
228237
238+ async function testReplicateAttributesDeleteEmpty ( sut , t , name ) {
239+ // Add a couple of detail documents to follow master
240+ const primaryKey = makeid ( ) ;
241+ await db . collection ( 'detail1' ) . add ( {
242+ tempId : primaryKey ,
243+ foreignDetail1 : 'foreign_detail_1' ,
244+ foreignDetail2 : 'foreign_detail_2' ,
245+ } ) ;
246+
247+ // Call trigger to replicate attributes from master
248+ const beforeSnap = fft . firestore . makeDocumentSnapshot (
249+ {
250+ masterDetail1 : 'after1' ,
251+ masterDetail2 : 'after2' ,
252+ } ,
253+ `master/${ primaryKey } `
254+ ) ;
255+ const afterSnap = fft . firestore . makeDocumentSnapshot (
256+ {
257+ masterDetail2 : 'after3' ,
258+ } ,
259+ `master/${ primaryKey } `
260+ ) ;
261+ const change = fft . makeChange ( beforeSnap , afterSnap ) ;
262+ const wrapped = fft . wrap ( sut . replicateMasterDeleteWhenEmpty ) ;
263+ setState ( {
264+ change : null ,
265+ context : null ,
266+ } ) ;
267+ await wrapped ( change , {
268+ params : {
269+ primaryKey : primaryKey ,
270+ } ,
271+ } ) ;
272+
273+ // Assert pre-hook was called (only for rules-in-situ)
274+ if ( name === 'rules-in-situ' ) {
275+ const state = getState ( ) ;
276+ t . truthy ( state . change ) ;
277+ t . truthy ( state . context ) ;
278+ t . is ( state . context . params . primaryKey , primaryKey ) ;
279+ }
280+
281+ // Assert that attributes get replicated to detail documents
282+ await assertQuerySizeEventually (
283+ db
284+ . collection ( 'detail1' )
285+ . where ( 'tempId' , '==' , primaryKey )
286+ . where ( 'foreignDetail1' , '==' , 'foreign_detail_1' ) ,
287+ 0
288+ ) ;
289+ await assertQuerySizeEventually (
290+ db
291+ . collection ( 'detail1' )
292+ . where ( 'tempId' , '==' , primaryKey )
293+ . where ( 'foreignDetail2' , '==' , 'after3' ) ,
294+ 1
295+ ) ;
296+
297+ await t . pass ( ) ;
298+ }
299+
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+
229369async function testDeleteReferences ( sut , t , name ) {
230370 // Create some docs referencing master doc
231371 const masterId = makeid ( ) ;
0 commit comments