@@ -689,6 +689,85 @@ describe("Sync", function() {
689
689
} ) ;
690
690
} ) ;
691
691
692
+ describe ( "Documents deleted both locally and on remote" , function ( ) {
693
+ beforeEach ( async function ( ) {
694
+ // three and four deleted on remote, three also deleted locally
695
+
696
+ const newRemoteMap = {
697
+ "one" : { "ETag" : "one" } ,
698
+ "two" : { "ETag" : "two" }
699
+ } ;
700
+
701
+ this . rs . local . setNodes ( {
702
+ "/foo/" : {
703
+ path : "/foo/" ,
704
+ common : {
705
+ itemsMap : { "one" : true , "two" : true , "three" : true , "four" : true } ,
706
+ revision : "remotefolderrevision" ,
707
+ timestamp : 1397210425598 ,
708
+ } ,
709
+ local : {
710
+ itemsMap : { "one" : true , "two" : true , "four" : true } ,
711
+ revision : "localfolderrevision" ,
712
+ timestamp : 1397210425612
713
+ }
714
+ } ,
715
+ "/foo/one" : {
716
+ path : "/foo/one" ,
717
+ common : {
718
+ body : { foo : "one" } ,
719
+ contentType : "application/json" ,
720
+ revision : "one" ,
721
+ timestamp : 1234567891000
722
+ }
723
+ } ,
724
+ "/foo/two" : {
725
+ path : "/foo/two" ,
726
+ local : {
727
+ body : { foo : "two" } ,
728
+ contentType : "application/json" ,
729
+ revision : "two" ,
730
+ timestamp : 1234567891000
731
+ }
732
+ } ,
733
+ "/foo/four" : {
734
+ path : "/foo/four" ,
735
+ common : {
736
+ body : { foo : "four" } ,
737
+ contentType : "application/json" ,
738
+ revision : "four" ,
739
+ timestamp : 1234567891000
740
+ }
741
+ }
742
+ } ) ;
743
+
744
+ await this . rs . sync . handleResponse ( '/foo/' , 'get' , {
745
+ statusCode : 200 , body : newRemoteMap ,
746
+ contentType : 'application/ld+json' ,
747
+ revision : 'newfolderrevision'
748
+ } ) ;
749
+
750
+ this . nodes = await this . rs . local . getNodes ( [
751
+ "/foo/" , "/foo/one" , "/foo/two" , "/foo/three" , "/foo/four"
752
+ ] ) ;
753
+ } ) ;
754
+
755
+ it ( "removes the document from the parent node's common itemsMap" , async function ( ) {
756
+ expect ( this . nodes [ "/foo/" ] . common . itemsMap ) . to . deep . equal ( {
757
+ "one" : true , "two" : true
758
+ } ) ;
759
+ } ) ;
760
+
761
+ it ( "removes the parent node's local and remote itemsMap" , function ( ) {
762
+ expect ( this . nodes [ "/foo/" ] . local ) . to . be . undefined ;
763
+ expect ( this . nodes [ "/foo/" ] . remote ) . to . be . undefined ;
764
+ } ) ;
765
+
766
+ it ( "removes the deleted node from the cache" , function ( ) {
767
+ expect ( this . nodes [ "/foo/four" ] ) . to . be . undefined ;
768
+ } ) ;
769
+ } ) ;
770
+
692
771
describe ( "PUT without conflict" , function ( ) {
693
772
beforeEach ( async function ( ) {
694
773
this . rs . local . setNodes ( {
@@ -872,6 +951,86 @@ describe("Sync", function() {
872
951
} ) ;
873
952
} ) ;
874
953
954
+ describe ( "#autoMergeFolder" , function ( ) {
955
+ describe ( "documents updated on both sides" , function ( ) {
956
+ beforeEach ( function ( ) {
957
+ this . addTask = sinon . spy ( this . rs . sync , "addTask" ) ;
958
+
959
+ this . nodeBefore = {
960
+ "path" : "/foo/" ,
961
+ "remote" : {
962
+ "revision" : "incomingrevision" ,
963
+ "timestamp" : 1750232323004 ,
964
+ "itemsMap" : {
965
+ "1750232100702" : true , // added remotely
966
+ "1750232294620" : true , // no change
967
+ }
968
+ } ,
969
+ "common" : {
970
+ "revision" : "oldrevision" ,
971
+ "timestamp" : 1750232313004 ,
972
+ "itemsMap" : {
973
+ "1750232294620" : true ,
974
+ }
975
+ } ,
976
+ "local" : {
977
+ "revision" : "localrevision" ,
978
+ "timestamp" : 1750232095577 ,
979
+ "itemsMap" : {
980
+ "1750232294620" : true ,
981
+ "1750232283970" : true , // uncertain state
982
+ "1750233526039" : true , // added locally
983
+ }
984
+ }
985
+ } ;
986
+
987
+ this . nodeAfter = this . rs . sync . autoMergeFolder ( this . nodeBefore ) ;
988
+ } ) ;
989
+
990
+ it ( "adds new documents to the local itemsMap as uncached" , function ( ) {
991
+ expect ( this . nodeAfter . local . itemsMap ) . to . deep . equal ( {
992
+ "1750232100702" : false , // marked for fetch
993
+ "1750232294620" : true ,
994
+ "1750232283970" : true ,
995
+ "1750233526039" : true
996
+ } ) ;
997
+ } ) ;
998
+
999
+ it ( "adds sync tasks for local items missing in incoming itemsMap" , function ( ) {
1000
+ expect ( this . addTask . callCount ) . to . equal ( 2 ) ;
1001
+ expect ( this . addTask . getCall ( 0 ) . args [ 0 ] ) . to . equal ( "/foo/1750232283970" ) ;
1002
+ expect ( this . addTask . getCall ( 1 ) . args [ 0 ] ) . to . equal ( "/foo/1750233526039" ) ;
1003
+ } ) ;
1004
+ } ) ;
1005
+
1006
+ describe ( "on an empty node" , function ( ) {
1007
+ it ( "removes a remote version if it has a null revision" , async function ( ) {
1008
+ const node = {
1009
+ path : "foo" , common : { } , remote : { revision : null }
1010
+ } ;
1011
+
1012
+ expect ( this . rs . sync . autoMergeDocument ( node ) ) . to
1013
+ . deep . equal ( { path : "foo" , common : { } } ) ;
1014
+ } ) ;
1015
+ } ) ;
1016
+
1017
+ it ( "merges mutual deletions" , function ( ) {
1018
+ const node = {
1019
+ "path" : "/myfavoritedrinks/b" ,
1020
+ "common" : { "timestamp" : 1405488508303 } ,
1021
+ "local" : { "body" : false , "timestamp" : 1405488515881 } ,
1022
+ "remote" : { "body" : false , "timestamp" : 1405488740722 }
1023
+ } ;
1024
+ const localAndRemoteRemoved = {
1025
+ "path" : "/myfavoritedrinks/b" ,
1026
+ "common" : { "timestamp" : 1405488508303 }
1027
+ } ;
1028
+
1029
+ expect ( this . rs . sync . autoMergeDocument ( node ) ) . to
1030
+ . deep . equal ( localAndRemoteRemoved ) ;
1031
+ } ) ;
1032
+ } ) ;
1033
+
875
1034
describe ( "#autoMerge" , function ( ) {
876
1035
describe ( "new node" , function ( ) {
877
1036
beforeEach ( function ( ) {
0 commit comments