FIRFieldsTests.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. if ([FSTIntegrationTestCase isRunningAgainstEmulator]) return; // b/112104025
  130. NSDictionary<NSString *, id> *testData = [self testDottedDataNumbered:1];
  131. FIRDocumentReference *doc = [self documentRef];
  132. [self writeDocumentRef:doc data:testData];
  133. [self updateDocumentRef:doc
  134. data:@{
  135. (id)[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]] : @100,
  136. (id) @"c\\slash" : @200
  137. }];
  138. FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
  139. XCTAssertEqualObjects(result.data, (@{@"a" : @"field 1", @"b.dot" : @100, @"c\\slash" : @200}));
  140. }
  141. - (void)testFieldsWithSpecialCharsCanBeUsedInQueryFilters {
  142. if ([FSTIntegrationTestCase isRunningAgainstEmulator]) return; // b/112104025
  143. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  144. @"1" : [self testDottedDataNumbered:300],
  145. @"2" : [self testDottedDataNumbered:100],
  146. @"3" : [self testDottedDataNumbered:200]
  147. };
  148. // inequality adds implicit sort on field
  149. NSArray<NSDictionary<NSString *, id> *> *expected =
  150. @[ [self testDottedDataNumbered:200], [self testDottedDataNumbered:300] ];
  151. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  152. XCTestExpectation *queryCompletion = [self expectationWithDescription:@"query"];
  153. FIRQuery *q = [coll queryWhereFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]]
  154. isGreaterThanOrEqualTo:@200];
  155. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  156. XCTAssertNil(error);
  157. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  158. [queryCompletion fulfill];
  159. }];
  160. [self awaitExpectations];
  161. }
  162. - (void)testFieldsWithSpecialCharsCanBeUsedInOrderBy {
  163. if ([FSTIntegrationTestCase isRunningAgainstEmulator]) return; // b/112104025
  164. NSDictionary<NSString *, NSDictionary<NSString *, id> *> *testDocs = @{
  165. @"1" : [self testDottedDataNumbered:300],
  166. @"2" : [self testDottedDataNumbered:100],
  167. @"3" : [self testDottedDataNumbered:200]
  168. };
  169. NSArray<NSDictionary<NSString *, id> *> *expected = @[
  170. [self testDottedDataNumbered:100], [self testDottedDataNumbered:200],
  171. [self testDottedDataNumbered:300]
  172. ];
  173. FIRCollectionReference *coll = [self collectionRefWithDocuments:testDocs];
  174. FIRQuery *q = [coll queryOrderedByFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"b.dot" ]]];
  175. XCTestExpectation *queryDot = [self expectationWithDescription:@"query dot"];
  176. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  177. XCTAssertNil(error);
  178. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  179. [queryDot fulfill];
  180. }];
  181. [self awaitExpectations];
  182. XCTestExpectation *querySlash = [self expectationWithDescription:@"query slash"];
  183. q = [coll queryOrderedByField:@"c\\slash"];
  184. [q getDocumentsWithCompletion:^(FIRQuerySnapshot *results, NSError *error) {
  185. XCTAssertNil(error);
  186. XCTAssertEqualObjects(FIRQuerySnapshotGetData(results), expected);
  187. [querySlash fulfill];
  188. }];
  189. [self awaitExpectations];
  190. }
  191. - (FIRDocumentSnapshot *)snapshotWithTimestamps:(FIRTimestamp *)timestamp {
  192. FIRDocumentReference *doc = [self documentRef];
  193. NSDictionary<NSString *, id> *data =
  194. @{@"timestamp" : timestamp, @"nested" : @{@"timestamp2" : timestamp}};
  195. [self writeDocumentRef:doc data:data];
  196. return [self readDocumentForRef:doc];
  197. }
  198. - (void)testTimestampsInSnapshots {
  199. FIRTimestamp *originalTimestamp = [FIRTimestamp timestampWithSeconds:100 nanoseconds:123456789];
  200. FIRDocumentReference *doc = [self documentRef];
  201. [self writeDocumentRef:doc data:testDataWithTimestamps(originalTimestamp)];
  202. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  203. NSDictionary<NSString *, id> *data = [snapshot data];
  204. // Timestamp are currently truncated to microseconds after being written to the database.
  205. FIRTimestamp *truncatedTimestamp =
  206. [FIRTimestamp timestampWithSeconds:originalTimestamp.seconds
  207. nanoseconds:originalTimestamp.nanoseconds / 1000 * 1000];
  208. FIRTimestamp *timestampFromSnapshot = snapshot[@"timestamp"];
  209. FIRTimestamp *timestampFromData = data[@"timestamp"];
  210. XCTAssertEqualObjects(truncatedTimestamp, timestampFromData);
  211. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  212. timestampFromSnapshot = snapshot[@"nested.timestamp2"];
  213. timestampFromData = data[@"nested"][@"timestamp2"];
  214. XCTAssertEqualObjects(truncatedTimestamp, timestampFromData);
  215. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  216. }
  217. @end
  218. @interface FIRTimestampsInSnapshotsLegacyBehaviorTests : FSTIntegrationTestCase
  219. @end
  220. @implementation FIRTimestampsInSnapshotsLegacyBehaviorTests
  221. - (void)setUp {
  222. [super setUp];
  223. // Settings can only be redefined before client is initialized, so this has to happen in setUp.
  224. FIRFirestoreSettings *settings = self.db.settings;
  225. SUPPRESS_DEPRECATED_DECLARATIONS_BEGIN()
  226. settings.timestampsInSnapshotsEnabled = NO;
  227. SUPPRESS_END()
  228. self.db.settings = settings;
  229. }
  230. - (void)testLegacyBehaviorForTimestampFields {
  231. NSDate *originalDate = [NSDate date];
  232. FIRDocumentReference *doc = [self documentRef];
  233. [self writeDocumentRef:doc
  234. data:testDataWithTimestamps([FIRTimestamp timestampWithDate:originalDate])];
  235. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  236. NSDictionary<NSString *, id> *data = [snapshot data];
  237. double microsecond = 0.000001;
  238. NSDate *timestampFromSnapshot = snapshot[@"timestamp"];
  239. NSDate *timestampFromData = data[@"timestamp"];
  240. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  241. XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970],
  242. [originalDate timeIntervalSince1970], microsecond);
  243. timestampFromSnapshot = snapshot[@"nested.timestamp2"];
  244. timestampFromData = data[@"nested"][@"timestamp2"];
  245. XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData);
  246. XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970],
  247. [originalDate timeIntervalSince1970], microsecond);
  248. }
  249. - (void)testLegacyBehaviorForServerTimestampFields {
  250. FIRDocumentReference *doc = [self documentRef];
  251. [self writeDocumentRef:doc data:@{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}];
  252. FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc];
  253. XCTAssertTrue([snapshot[@"when"] isKindOfClass:[NSDate class]]);
  254. XCTAssertTrue([snapshot.data[@"when"] isKindOfClass:[NSDate class]]);
  255. }
  256. @end