FSTMutationTests.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Copyright 2017 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import "Firestore/Source/Model/FSTMutation.h"
  17. #import <XCTest/XCTest.h>
  18. #import "Firestore/Source/Core/FSTTimestamp.h"
  19. #import "Firestore/Source/Model/FSTDocument.h"
  20. #import "Firestore/Source/Model/FSTDocumentKey.h"
  21. #import "Firestore/Source/Model/FSTFieldValue.h"
  22. #import "Firestore/Source/Model/FSTPath.h"
  23. #import "Firestore/Example/Tests/Util/FSTHelpers.h"
  24. @interface FSTMutationTests : XCTestCase
  25. @end
  26. @implementation FSTMutationTests {
  27. FSTTimestamp *_timestamp;
  28. }
  29. - (void)setUp {
  30. _timestamp = [FSTTimestamp timestamp];
  31. }
  32. - (void)testAppliesSetsToDocuments {
  33. NSDictionary *docData = @{@"foo" : @"foo-value", @"baz" : @"baz-value"};
  34. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  35. FSTMutation *set = FSTTestSetMutation(@"collection/key", @{@"bar" : @"bar-value"});
  36. FSTMaybeDocument *setDoc = [set applyTo:baseDoc localWriteTime:_timestamp];
  37. NSDictionary *expectedData = @{@"bar" : @"bar-value"};
  38. XCTAssertEqualObjects(setDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
  39. }
  40. - (void)testAppliesPatchesToDocuments {
  41. NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" };
  42. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  43. FSTMutation *patch =
  44. FSTTestPatchMutation(@"collection/key", @{@"foo.bar" : @"new-bar-value"}, nil);
  45. FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
  46. NSDictionary *expectedData = @{ @"foo" : @{@"bar" : @"new-bar-value"}, @"baz" : @"baz-value" };
  47. XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
  48. }
  49. - (void)testDeletesValuesFromTheFieldMask {
  50. NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value", @"baz" : @"baz-value"} };
  51. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  52. FSTDocumentKey *key = [FSTDocumentKey keyWithPathString:@"collection/key"];
  53. FSTFieldMask *mask = [[FSTFieldMask alloc] initWithFields:@[ FSTTestFieldPath(@"foo.bar") ]];
  54. FSTMutation *patch = [[FSTPatchMutation alloc] initWithKey:key
  55. fieldMask:mask
  56. value:[FSTObjectValue objectValue]
  57. precondition:[FSTPrecondition none]];
  58. FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
  59. NSDictionary *expectedData = @{ @"foo" : @{@"baz" : @"baz-value"} };
  60. XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
  61. }
  62. - (void)testPatchesPrimitiveValue {
  63. NSDictionary *docData = @{@"foo" : @"foo-value", @"baz" : @"baz-value"};
  64. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  65. FSTMutation *patch =
  66. FSTTestPatchMutation(@"collection/key", @{@"foo.bar" : @"new-bar-value"}, nil);
  67. FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
  68. NSDictionary *expectedData = @{ @"foo" : @{@"bar" : @"new-bar-value"}, @"baz" : @"baz-value" };
  69. XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, YES));
  70. }
  71. - (void)testPatchingDeletedDocumentsDoesNothing {
  72. FSTMaybeDocument *baseDoc = FSTTestDeletedDoc(@"collection/key", 0);
  73. FSTMutation *patch = FSTTestPatchMutation(@"collection/key", @{@"foo" : @"bar"}, nil);
  74. FSTMaybeDocument *patchedDoc = [patch applyTo:baseDoc localWriteTime:_timestamp];
  75. XCTAssertEqualObjects(patchedDoc, baseDoc);
  76. }
  77. - (void)testAppliesLocalTransformsToDocuments {
  78. NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" };
  79. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  80. FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @[ @"foo.bar" ]);
  81. FSTMaybeDocument *transformedDoc = [transform applyTo:baseDoc localWriteTime:_timestamp];
  82. // Server timestamps aren't parsed, so we manually insert it.
  83. FSTObjectValue *expectedData = FSTTestObjectValue(
  84. @{ @"foo" : @{@"bar" : @"<server-timestamp>"},
  85. @"baz" : @"baz-value" });
  86. expectedData =
  87. [expectedData objectBySettingValue:[FSTServerTimestampValue
  88. serverTimestampValueWithLocalWriteTime:_timestamp]
  89. forPath:FSTTestFieldPath(@"foo.bar")];
  90. FSTDocument *expectedDoc = [FSTDocument documentWithData:expectedData
  91. key:FSTTestDocKey(@"collection/key")
  92. version:FSTTestVersion(0)
  93. hasLocalMutations:YES];
  94. XCTAssertEqualObjects(transformedDoc, expectedDoc);
  95. }
  96. - (void)testAppliesServerAckedTransformsToDocuments {
  97. NSDictionary *docData = @{ @"foo" : @{@"bar" : @"bar-value"}, @"baz" : @"baz-value" };
  98. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  99. FSTMutation *transform = FSTTestTransformMutation(@"collection/key", @[ @"foo.bar" ]);
  100. FSTMutationResult *mutationResult = [[FSTMutationResult alloc]
  101. initWithVersion:FSTTestVersion(1)
  102. transformResults:@[ [FSTTimestampValue timestampValue:_timestamp] ]];
  103. FSTMaybeDocument *transformedDoc =
  104. [transform applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
  105. NSDictionary *expectedData =
  106. @{ @"foo" : @{@"bar" : _timestamp.approximateDateValue},
  107. @"baz" : @"baz-value" };
  108. XCTAssertEqualObjects(transformedDoc, FSTTestDoc(@"collection/key", 0, expectedData, NO));
  109. }
  110. - (void)testDeleteDeletes {
  111. NSDictionary *docData = @{@"foo" : @"bar"};
  112. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  113. FSTMutation *mutation = FSTTestDeleteMutation(@"collection/key");
  114. FSTMaybeDocument *result = [mutation applyTo:baseDoc localWriteTime:_timestamp];
  115. XCTAssertEqualObjects(result, FSTTestDeletedDoc(@"collection/key", 0));
  116. }
  117. - (void)testSetWithMutationResult {
  118. NSDictionary *docData = @{@"foo" : @"bar"};
  119. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  120. FSTMutation *set = FSTTestSetMutation(@"collection/key", @{@"foo" : @"new-bar"});
  121. FSTMutationResult *mutationResult =
  122. [[FSTMutationResult alloc] initWithVersion:FSTTestVersion(4) transformResults:nil];
  123. FSTMaybeDocument *setDoc =
  124. [set applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
  125. NSDictionary *expectedData = @{@"foo" : @"new-bar"};
  126. XCTAssertEqualObjects(setDoc, FSTTestDoc(@"collection/key", 0, expectedData, NO));
  127. }
  128. - (void)testPatchWithMutationResult {
  129. NSDictionary *docData = @{@"foo" : @"bar"};
  130. FSTDocument *baseDoc = FSTTestDoc(@"collection/key", 0, docData, NO);
  131. FSTMutation *patch = FSTTestPatchMutation(@"collection/key", @{@"foo" : @"new-bar"}, nil);
  132. FSTMutationResult *mutationResult =
  133. [[FSTMutationResult alloc] initWithVersion:FSTTestVersion(4) transformResults:nil];
  134. FSTMaybeDocument *patchedDoc =
  135. [patch applyTo:baseDoc localWriteTime:_timestamp mutationResult:mutationResult];
  136. NSDictionary *expectedData = @{@"foo" : @"new-bar"};
  137. XCTAssertEqualObjects(patchedDoc, FSTTestDoc(@"collection/key", 0, expectedData, NO));
  138. }
  139. #define ASSERT_VERSION_TRANSITION(mutation, base, expected) \
  140. do { \
  141. FSTMutationResult *mutationResult = \
  142. [[FSTMutationResult alloc] initWithVersion:FSTTestVersion(0) transformResults:nil]; \
  143. FSTMaybeDocument *actual = \
  144. [mutation applyTo:base localWriteTime:_timestamp mutationResult:mutationResult]; \
  145. XCTAssertEqualObjects(actual, expected); \
  146. } while (0);
  147. /**
  148. * Tests the transition table documented in FSTMutation.h.
  149. */
  150. - (void)testTransitions {
  151. FSTDocument *docV0 = FSTTestDoc(@"collection/key", 0, @{}, NO);
  152. FSTDeletedDocument *deletedV0 = FSTTestDeletedDoc(@"collection/key", 0);
  153. FSTDocument *docV3 = FSTTestDoc(@"collection/key", 3, @{}, NO);
  154. FSTDeletedDocument *deletedV3 = FSTTestDeletedDoc(@"collection/key", 3);
  155. FSTMutation *setMutation = FSTTestSetMutation(@"collection/key", @{});
  156. FSTMutation *patchMutation = FSTTestPatchMutation(@"collection/key", @{}, nil);
  157. FSTMutation *deleteMutation = FSTTestDeleteMutation(@"collection/key");
  158. ASSERT_VERSION_TRANSITION(setMutation, docV3, docV3);
  159. ASSERT_VERSION_TRANSITION(setMutation, deletedV3, docV0);
  160. ASSERT_VERSION_TRANSITION(setMutation, nil, docV0);
  161. ASSERT_VERSION_TRANSITION(patchMutation, docV3, docV3);
  162. ASSERT_VERSION_TRANSITION(patchMutation, deletedV3, deletedV3);
  163. ASSERT_VERSION_TRANSITION(patchMutation, nil, nil);
  164. ASSERT_VERSION_TRANSITION(deleteMutation, docV3, deletedV0);
  165. ASSERT_VERSION_TRANSITION(deleteMutation, deletedV3, deletedV0);
  166. ASSERT_VERSION_TRANSITION(deleteMutation, nil, deletedV0);
  167. }
  168. #undef ASSERT_TRANSITION
  169. @end