FIRFieldsTests.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  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 <FirebaseFirestore/FIRTimestamp.h>
  17. #import <FirebaseFirestore/FirebaseFirestore.h>
  18. #import <XCTest/XCTest.h>
  19. #import "Firestore/core/src/firebase/firestore/util/warnings.h"
  20. #import "Firestore/Source/Core/FSTFirestoreClient.h"
  21. #import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
  22. @interface FIRFieldsTests : FSTIntegrationTestCase
  23. @end
  24. NSDictionary<NSString *, id> *testDataWithTimestamps(FIRTimestamp *timestamp) {
  25. return @{@"timestamp" : timestamp, @"nested" : @{@"timestamp2" : timestamp}};
  26. }
  27. @implementation FIRFieldsTests
  28. - (NSDictionary<NSString *, id> *)testNestedDataNumbered:(int)number {
  29. return @{
  30. @"name" : [NSString stringWithFormat:@"room %d", number],
  31. @"metadata" : @{
  32. @"createdAt" : @(number),
  33. @"deep" : @{@"field" : [NSString stringWithFormat:@"deep-field-%d", number]}
  34. }
  35. };
  36. }
  37. - (void)testNestedFieldsCanBeWrittenWithSet {
  38. NSDictionary<NSString *, id> *testData = [self testNestedDataNumbered:1];
  39. FIRDocumentReference *doc = [self documentRef];
  40. [self writeDocumentRef:doc data:testData];
  41. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  42. XCTAssertEqualObjects(result.data, testData);
  43. }
  44. - (void)testNestedFieldsCanBeReadDirectly {
  45. NSDictionary<NSString *, id> *testData = [self testNestedDataNumbered:1];
  46. FIRDocumentReference *doc = [self documentRef];
  47. [self writeDocumentRef:doc data:testData];
  48. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  49. XCTAssertEqualObjects(result[@"name"], testData[@"name"]);
  50. XCTAssertEqualObjects(result[@"metadata"], testData[@"metadata"]);
  51. XCTAssertEqualObjects(result[@"metadata.deep.field"], testData[@"metadata"][@"deep"][@"field"]);
  52. XCTAssertNil(result[@"metadata.nofield"]);
  53. XCTAssertNil(result[@"nometadata.nofield"]);
  54. }
  55. - (void)testNestedFieldsCanBeUpdated {
  56. NSDictionary<NSString *, id> *testData = [self testNestedDataNumbered:1];
  57. FIRDocumentReference *doc = [self documentRef];
  58. [self writeDocumentRef:doc data:testData];
  59. [self updateDocumentRef:doc data:@{@"metadata.deep.field" : @100, @"metadata.added" : @200}];
  60. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  61. XCTAssertEqualObjects(
  62. result.data, (@{
  63. @"name" : @"room 1",
  64. @"metadata" : @{@"createdAt" : @1, @"deep" : @{@"field" : @100}, @"added" : @200}
  65. }));
  66. }
  67. - (void)testNestedFieldsCanBeUsedInQueryFilters {
  68. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  69. @"1" : [self testNestedDataNumbered:300],
  70. @"2" : [self testNestedDataNumbered:100],
  71. @"3" : [self testNestedDataNumbered:200]
  72. };
  73. // inequality adds implicit sort on field
  74. NSArray<NSDictionary<NSString *, id> *> *expected =
  75. @[ [self testNestedDataNumbered:200], [self testNestedDataNumbered:300] ];
  76. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  77. FIRQuery *q = [coll queryWhereField:@"metadata.createdAt" isGreaterThanOrEqualTo:@200];
  78. FIRQuerySnapshot *results = [self readDocumentSetForRef:q];
  79. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), (expected));
  80. }
  81. - (void)testNestedFieldsCanBeUsedInOrderBy {
  82. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  83. @"1" : [self testNestedDataNumbered:300],
  84. @"2" : [self testNestedDataNumbered:100],
  85. @"3" : [self testNestedDataNumbered:200]
  86. };
  87. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  88. XCTestExpectation *queryCompletion = [self expectationWithDescription:@"query"];
  89. FIRQuery *q = [coll queryOrderedByField:@"metadata.createdAt"];
  90. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  91. XCTAssertNil(error);
  92. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), (@[
  93. [self testNestedDataNumbered:100], [self testNestedDataNumbered:200],
  94. [self testNestedDataNumbered:300]
  95. ]));
  96. [queryCompletion fulfill];
  97. }];
  98. [self awaitExpectations];
  99. }
  100. /**
  101. * Creates test data with special characters in field names. Datastore currently prohibits mixing
  102. * nested data with special characters so tests that use this data must be separate.
  103. */
  104. - (NSDictionary<NSString *, id> *)testDottedDataNumbered:(int)number {
  105. return @{
  106. @"a" : [NSString stringWithFormat:@"field %d", number],
  107. @"b.dot" : @(number),
  108. @"c\\slash" : @(number)
  109. };
  110. }
  111. - (void)testFieldsWithSpecialCharsCanBeWrittenWithSet {
  112. NSDictionary<NSString *, id> *testData = [self testDottedDataNumbered:1];
  113. FIRDocumentReference *doc = [self documentRef];
  114. [self writeDocumentRef:doc data:testData];
  115. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  116. XCTAssertEqualObjects(result.data, testData);
  117. }
  118. - (void)testFieldsWithSpecialCharsCanBeReadDirectly {
  119. NSDictionary<NSString *, id> *testData = [self testDottedDataNumbered:1];
  120. FIRDocumentReference *doc = [self documentRef];
  121. [self writeDocumentRef:doc data:testData];
  122. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  123. XCTAssertEqualObjects(result[@"a"], testData[@"a"]);
  124. XCTAssertEqualObjects(result[[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]]],
  125. testData[@"b.dot"]);
  126. XCTAssertEqualObjects(result[@"c\\slash"], testData[@"c\\slash"]);
  127. }
  128. - (void)testFieldsWithSpecialCharsCanBeUpdated {
  129. NSDictionary<NSString *, id> *testData = [self testDottedDataNumbered:1];
  130. FIRDocumentReference *doc = [self documentRef];
  131. [self writeDocumentRef:doc data:testData];
  132. [self updateDocumentRef:doc
  133. data:@{
  134. (id)[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]] : @100,
  135. (id) @"c\\slash" : @200
  136. }];
  137. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  138. XCTAssertEqualObjects(result.data, (@{@"a" : @"field 1", @"b.dot" : @100, @"c\\slash" : @200}));
  139. }
  140. - (void)testFieldsWithSpecialCharsCanBeUsedInQueryFilters {
  141. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  142. @"1" : [self testDottedDataNumbered:300],
  143. @"2" : [self testDottedDataNumbered:100],
  144. @"3" : [self testDottedDataNumbered:200]
  145. };
  146. // inequality adds implicit sort on field
  147. NSArray<NSDictionary<NSString *, id> *> *expected =
  148. @[ [self testDottedDataNumbered:200], [self testDottedDataNumbered:300] ];
  149. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  150. XCTestExpectation *queryCompletion = [self expectationWithDescription:@"query"];
  151. FIRQuery *q = [coll queryWhereFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]]
  152. isGreaterThanOrEqualTo:@200];
  153. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  154. XCTAssertNil(error);
  155. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  156. [queryCompletion fulfill];
  157. }];
  158. [self awaitExpectations];
  159. }
  160. - (void)testFieldsWithSpecialCharsCanBeUsedInOrderBy {
  161. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  162. @"1" : [self testDottedDataNumbered:300],
  163. @"2" : [self testDottedDataNumbered:100],
  164. @"3" : [self testDottedDataNumbered:200]
  165. };
  166. NSArray<NSDictionary<NSString *, id> *> *expected = @[
  167. [self testDottedDataNumbered:100], [self testDottedDataNumbered:200],
  168. [self testDottedDataNumbered:300]
  169. ];
  170. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  171. FIRQuery *q = [coll queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]]];
  172. XCTestExpectation *queryDot = [self expectationWithDescription:@"query dot"];
  173. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  174. XCTAssertNil(error);
  175. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  176. [queryDot fulfill];
  177. }];
  178. [self awaitExpectations];
  179. XCTestExpectation *querySlash = [self expectationWithDescription:@"query slash"];
  180. q = [coll queryOrderedByField:@"c\\slash"];
  181. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  182. XCTAssertNil(error);
  183. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  184. [querySlash fulfill];
  185. }];
  186. [self awaitExpectations];
  187. }
  188. - (FIRDocumentSnapshot *)snapshotWithTimestamps:(FIRTimestamp *)timestamp {
  189. FIRDocumentReference *doc = [self documentRef];
  190. NSDictionary<NSString *, id> *data =
  191. @{@"timestamp" : timestamp, @"nested" : @{@"timestamp2" : timestamp}};
  192. [self writeDocumentRef:doc data:data];
  193. return [self readDocumentForRef:doc];
  194. }
  195. - (void)testTimestampsInSnapshots {
  196. FIRTimestamp *originalTimestamp = [FIRTimestamp timestampWithSeconds:100 nanoseconds:123456789];
  197. FIRDocumentReference *doc = [self documentRef];
  198. [self writeDocumentRef:doc data:testDataWithTimestamps(originalTimestamp)];
  199. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  200. NSDictionary<NSString *, id> *data = [snapshot data];
  201. // Timestamp are currently truncated to microseconds after being written to the database.
  202. FIRTimestamp *truncatedTimestamp =
  203. [FIRTimestamp timestampWithSeconds:originalTimestamp.seconds
  204. nanoseconds:originalTimestamp.nanoseconds / 1000 * 1000];
  205. FIRTimestamp *timestampFromSnapshot = snapshot[@"timestamp"];
  206. FIRTimestamp *timestampFromData = data[@"timestamp"];
  207. XCTAssertEqualObjects(truncatedTimestamp, timestampFromData);
  208. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  209. timestampFromSnapshot = snapshot[@"nested.timestamp2"];
  210. timestampFromData = data[@"nested"][@"timestamp2"];
  211. XCTAssertEqualObjects(truncatedTimestamp, timestampFromData);
  212. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  213. }
  214. @end
  215. @interface FIRTimestampsInSnapshotsLegacyBehaviorTests : FSTIntegrationTestCase
  216. @end
  217. @implementation FIRTimestampsInSnapshotsLegacyBehaviorTests
  218. - (void)setUp {
  219. [super setUp];
  220. // Settings can only be redefined before client is initialized, so this has to happen in setUp.
  221. FIRFirestoreSettings *settings = self.db.settings;
  222. SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN()
  223. settings.timestampsInSnapshotsEnabled = NO;
  224. SUPPRESS_END()
  225. self.db.settings = settings;
  226. }
  227. - (void)testLegacyBehaviorForTimestampFields {
  228. NSDate *originalDate = [NSDate date];
  229. FIRDocumentReference *doc = [self documentRef];
  230. [self writeDocumentRef:doc
  231. data:testDataWithTimestamps([FIRTimestamp timestampWithDate:originalDate])];
  232. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  233. NSDictionary<NSString *, id> *data = [snapshot data];
  234. double microsecond = 0.000001;
  235. NSDate *timestampFromSnapshot = snapshot[@"timestamp"];
  236. NSDate *timestampFromData = data[@"timestamp"];
  237. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  238. XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970],
  239. [originalDate timeIntervalSince1970], microsecond);
  240. timestampFromSnapshot = snapshot[@"nested.timestamp2"];
  241. timestampFromData = data[@"nested"][@"timestamp2"];
  242. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  243. XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970],
  244. [originalDate timeIntervalSince1970], microsecond);
  245. }
  246. - (void)testLegacyBehaviorForServerTimestampFields {
  247. FIRDocumentReference *doc = [self documentRef];
  248. [self writeDocumentRef:doc data:@{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}];
  249. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  250. XCTAssertTrue([snapshot[@"when"] isKindOfClass:[NSDate class]]);
  251. XCTAssertTrue([snapshot.data[@"when"] isKindOfClass:[NSDate class]]);
  252. }
  253. @end