From 360e58901c359d7d21da4fff8043894c843427b7 Mon Sep 17 00:00:00 2001 From: Michael Lehenbauer Date: Mon, 16 Apr 2018 16:04:26 -0700 Subject: Implement local and server application of arrayUnion and arrayRemove transforms. (#1101) --- Firestore/Example/Tests/Model/FSTMutationTests.mm | 144 +++++++++++++++++++++- 1 file changed, 143 insertions(+), 1 deletion(-) (limited to 'Firestore/Example') diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.mm b/Firestore/Example/Tests/Model/FSTMutationTests.mm index 56bf1c2..0bb7518 100644 --- a/Firestore/Example/Tests/Model/FSTMutationTests.mm +++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm @@ -185,7 +185,126 @@ using firebase::firestore::model::TransformOperation; } } -- (void)testAppliesServerAckedTransformsToDocuments { +- (void)testAppliesLocalArrayUnionTransformToMissingField { + auto baseDoc = @{}; + auto transform = @{ @"missing" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] }; + auto expected = @{ @"missing" : @[ @1, @2 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformToNonArrayField { + auto baseDoc = @{ @"non-array" : @42 }; + auto transform = @{ @"non-array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] }; + auto expected = @{ @"non-array" : @[ @1, @2 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithNonExistingElements { + auto baseDoc = @{ @"array" : @[ @1, @3 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @4 ]] }; + auto expected = @{ @"array" : @[ @1, @3, @2, @4 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithExistingElements { + auto baseDoc = @{ @"array" : @[ @1, @3 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @3 ]] }; + auto expected = @{ @"array" : @[ @1, @3 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithDuplicateExistingElements { + // Duplicate entries in your existing array should be preserved. + auto baseDoc = @{ @"array" : @[ @1, @2, @2, @3 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2 ]] }; + auto expected = @{ @"array" : @[ @1, @2, @2, @3 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithDuplicateUnionElements { + // Duplicate entries in your union array should only be added once. + auto baseDoc = @{ @"array" : @[ @1, @3 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @2 ]] }; + auto expected = @{ @"array" : @[ @1, @3, @2 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithNonPrimitiveElements { + // Union nested object values (one existing, one not). + auto baseDoc = @{ @"array" : @[ @1, @{@"a" : @"b"} ] }; + auto transform = + @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @{@"a" : @"b"}, @{@"c" : @"d"} ]] }; + auto expected = @{ @"array" : @[ @1, @{@"a" : @"b"}, @{@"c" : @"d"} ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayUnionTransformWithPartiallyOverlappingElements { + // Union objects that partially overlap an existing object. + auto baseDoc = @{ @"array" : @[ @1, @{@"a" : @"b", @"c" : @"d"} ] }; + auto transform = + @{ @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @{@"a" : @"b"}, @{@"c" : @"d"} ]] }; + auto expected = + @{ @"array" : @[ @1, @{@"a" : @"b", @"c" : @"d"}, @{@"a" : @"b"}, @{@"c" : @"d"} ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayRemoveTransformToMissingField { + auto baseDoc = @{}; + auto transform = @{ @"missing" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @2 ]] }; + auto expected = @{ @"missing" : @[] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayRemoveTransformToNonArrayField { + auto baseDoc = @{ @"non-array" : @42 }; + auto transform = @{ @"non-array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @2 ]] }; + auto expected = @{ @"non-array" : @[] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayRemoveTransformWithNonExistingElements { + auto baseDoc = @{ @"array" : @[ @1, @3 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @2, @4 ]] }; + auto expected = @{ @"array" : @[ @1, @3 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayRemoveTransformWithExistingElements { + auto baseDoc = @{ @"array" : @[ @1, @2, @3, @4 ] }; + auto transform = @{ @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @3 ]] }; + auto expected = @{ @"array" : @[ @2, @4 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +- (void)testAppliesLocalArrayRemoveTransformWithNonPrimitiveElements { + // Remove nested object values (one existing, one not). + auto baseDoc = @{ @"array" : @[ @1, @{@"a" : @"b"} ] }; + auto transform = + @{ @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @{@"a" : @"b"}, @{@"c" : @"d"} ]] }; + auto expected = @{ @"array" : @[ @1 ] }; + [self transformBaseDoc:baseDoc with:transform expecting:expected]; +} + +// Helper to test a particular transform scenario. +- (void)transformBaseDoc:(NSDictionary *)baseData + with:(NSDictionary *)transformData + expecting:(NSDictionary *)expectedData { + FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, baseData, NO); + + FSTMutation *transform = FSTTestTransformMutation(@"collection/key", transformData); + + FSTMaybeDocument *transformedDoc = + [transform applyTo:baseDoc baseDocument:baseDoc localWriteTime:_timestamp]; + + FSTDocument *expectedDoc = [FSTDocument documentWithData:FSTTestObjectValue(expectedData) + key:FSTTestDocKey(@"collection/key") + version:FSTTestVersion(0) + hasLocalMutations:YES]; + + XCTAssertEqualObjects(transformedDoc, expectedDoc); +} + +- (void)testAppliesServerAckedServerTimestampTransformToDocuments { NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" }; FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); @@ -207,6 +326,29 @@ using firebase::firestore::model::TransformOperation; XCTAssertEqualObjects(transformedDoc, FSTTestDoc("collection/key", 0, expectedData, NO)); } +- (void)testAppliesServerAckedArrayTransformsToDocuments { + NSDictionary *docData = @{ @"array_1" : @[ @1, @2 ], @"array_2" : @[ @"a", @"b" ] }; + FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); + + FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @{ + @"array_1" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @3 ]], + @"array_2" : [FIRFieldValue fieldValueForArrayRemove:@[ @"a", @"c" ]] + }); + + // Server just sends null transform results for array operations. + FSTMutationResult *mutationResult = [[FSTMutationResult alloc] + initWithVersion:FSTTestVersion(1) + transformResults:@[ [FSTNullValue nullValue], [FSTNullValue nullValue] ]]; + + FSTMaybeDocument *transformedDoc = [transform applyTo:baseDoc + baseDocument:baseDoc + localWriteTime:_timestamp + mutationResult:mutationResult]; + + NSDictionary *expectedData = @{ @"array_1" : @[ @1, @2, @3 ], @"array_2" : @[ @"b" ] }; + XCTAssertEqualObjects(transformedDoc, FSTTestDoc("collection/key", 0, expectedData, NO)); +} + - (void)testDeleteDeletes { NSDictionary *docData = @{@"foo" : @"bar"}; FSTDocument *baseDoc = FSTTestDoc("collection/key", 0, docData, NO); -- cgit v1.2.3