diff options
author | Konstantin Varlamov <var-const@users.noreply.github.com> | 2018-03-30 14:50:15 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-30 14:50:15 -0400 |
commit | a17740e9146e4e2431d62964d044287cccc3ee85 (patch) | |
tree | 479f280a07f4200924e0ef7cce183aa35e14c5bb /Firestore/Example | |
parent | 653aea7b50247bb0f6a7e8e1b4ab782553849f74 (diff) |
Add a flag to control whether DocumentSnapshots return Dates or Timestamps for timestamp fields (#831)
* add a new property `timestampsInSnapshotsEnabled` to `FirestoreSettings`, `false` by default;
* add a verbose warning message urging users to opt into the new behavior;
* set `timestampsInSnapshotsEnabled` to true in the integration tests to reduce the verbose console
spam during the test run and make sure the flag won't break anything once it's flipped.
Diffstat (limited to 'Firestore/Example')
8 files changed, 115 insertions, 42 deletions
diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index cd2462b..3087085 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -52,6 +52,7 @@ func initializeDb() -> Firestore { let settings = FirestoreSettings() settings.host = "localhost" settings.isPersistenceEnabled = true + settings.areTimestampsInSnapshotsEnabled = true firestore.settings = settings return firestore @@ -333,6 +334,7 @@ func types() { let _: Firestore let _: FirestoreSettings let _: GeoPoint + let _: Timestamp let _: ListenerRegistration let _: QueryListenOptions let _: Query diff --git a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm index 751e7ff..312c3ff 100644 --- a/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRDatabaseTests.mm @@ -181,7 +181,7 @@ FIRDocumentSnapshot *document = [self readDocumentForRef:doc]; XCTAssertEqual(document[@"updated"], @NO); - XCTAssertTrue([document[@"time"] isKindOfClass:[NSDate class]]); + XCTAssertTrue([document[@"time"] isKindOfClass:[FIRTimestamp class]]); } - (void)testCanDeleteFieldUsingMerge { diff --git a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm index 0e75b8e..30db5e3 100644 --- a/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRFieldsTests.mm @@ -26,6 +26,10 @@ @interface FIRFieldsTests : FSTIntegrationTestCase @end +NSDictionary<NSString *, id> *testDataWithTimestamps(FIRTimestamp *timestamp) { + return @{ @"timestamp" : timestamp, @"nested" : @{@"timestamp2" : timestamp} }; +} + @implementation FIRFieldsTests - (NSDictionary<NSString *, id> *)testNestedDataNumbered:(int)number { @@ -221,28 +225,83 @@ [self awaitExpectations]; } -- (NSDictionary<NSString *, id> *)testDataWithTimestamp:(FIRTimestamp *)timestamp { - return @{ - @"timestamp" : [timestamp approximateDateValue], - @"metadata" : @{@"nestedTimestamp" : [timestamp approximateDateValue]} - }; +- (FIRDocumentSnapshot *)snapshotWithTimestamps:(FIRTimestamp *)timestamp { + FIRDocumentReference *doc = [self documentRef]; + NSDictionary<NSString *, id> *data = + @{ @"timestamp" : timestamp, + @"nested" : @{@"timestamp2" : timestamp} }; + [self writeDocumentRef:doc data:data]; + return [self readDocumentForRef:doc]; } -// This test should break once the default for how timestamps are returned changes. -- (void)testThatDataContainsNativeDateType { - NSDate *date = [NSDate date]; - FIRTimestamp *timestamp = [FIRTimestamp timestampWithDate:date]; +// Note: timestampsInSnapshotsEnabled is set to "true" in FSTIntegrationTestCase, so this test is +// not affected by the current default in FIRFirestoreSettings. +- (void)testTimestampsInSnapshots { + FIRTimestamp *originalTimestamp = [FIRTimestamp timestampWithSeconds:100 nanoseconds:123456789]; FIRDocumentReference *doc = [self documentRef]; - [self writeDocumentRef:doc data:[self testDataWithTimestamp:timestamp]]; + [self writeDocumentRef:doc data:testDataWithTimestamps(originalTimestamp)]; + + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + NSDictionary<NSString *, id> *data = [snapshot data]; + // Timestamp are currently truncated to microseconds after being written to the database. + FIRTimestamp *truncatedTimestamp = + [FIRTimestamp timestampWithSeconds:originalTimestamp.seconds + nanoseconds:originalTimestamp.nanoseconds / 1000 * 1000]; + + FIRTimestamp *timestampFromSnapshot = snapshot[@"timestamp"]; + FIRTimestamp *timestampFromData = data[@"timestamp"]; + XCTAssertEqualObjects(truncatedTimestamp, timestampFromData); + XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData); + + timestampFromSnapshot = snapshot[@"nested.timestamp2"]; + timestampFromData = data[@"nested"][@"timestamp2"]; + XCTAssertEqualObjects(truncatedTimestamp, timestampFromData); + XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData); +} +@end - FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; - NSDate *resultDate = result.data[@"timestamp"]; - XCTAssertEqualWithAccuracy([resultDate timeIntervalSince1970], [date timeIntervalSince1970], - 0.000001); - XCTAssertEqualObjects(result.data[@"timestamp"], resultDate); - NSDate *resultNestedDate = result[@"metadata.nestedTimestamp"]; - XCTAssertEqualWithAccuracy([resultNestedDate timeIntervalSince1970], [date timeIntervalSince1970], - 0.000001); +@interface FIRTimestampsInSnapshotsLegacyBehaviorTests : FSTIntegrationTestCase +@end + +@implementation FIRTimestampsInSnapshotsLegacyBehaviorTests + +- (void)setUp { + [super setUp]; + // Settings can only be redefined before client is initialized, so this has to happen in setUp. + FIRFirestoreSettings *settings = self.db.settings; + settings.timestampsInSnapshotsEnabled = NO; + self.db.settings = settings; +} + +- (void)testLegacyBehaviorForTimestampFields { + NSDate *originalDate = [NSDate date]; + FIRDocumentReference *doc = [self documentRef]; + [self writeDocumentRef:doc + data:testDataWithTimestamps([FIRTimestamp timestampWithDate:originalDate])]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + NSDictionary<NSString *, id> *data = [snapshot data]; + double microsecond = 0.000001; + + NSDate *timestampFromSnapshot = snapshot[@"timestamp"]; + NSDate *timestampFromData = data[@"timestamp"]; + XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData); + XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970], + [originalDate timeIntervalSince1970], microsecond); + + timestampFromSnapshot = snapshot[@"nested.timestamp2"]; + timestampFromData = data[@"nested"][@"timestamp2"]; + XCTAssertEqualObjects(timestampFromSnapshot, timestampFromData); + XCTAssertEqualWithAccuracy([timestampFromSnapshot timeIntervalSince1970], + [originalDate timeIntervalSince1970], microsecond); +} + +- (void)testLegacyBehaviorForServerTimestampFields { + FIRDocumentReference *doc = [self documentRef]; + [self writeDocumentRef:doc data:@{@"when" : [FIRFieldValue fieldValueForServerTimestamp]}]; + + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertTrue([snapshot[@"when"] isKindOfClass:[NSDate class]]); + XCTAssertTrue([snapshot.data[@"when"] isKindOfClass:[NSDate class]]); } @end diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm index 916ce7e..4d51434 100644 --- a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.mm @@ -125,7 +125,7 @@ /** Verifies a snapshot containing _setData but with a local estimate for the timestamps. */ - (void)verifyTimestampsAreEstimatedInSnapshot:(FIRDocumentSnapshot *)snapshot { id timestamp = [snapshot valueForField:@"when" options:_returnEstimatedValue]; - XCTAssertTrue([timestamp isKindOfClass:[NSDate class]]); + XCTAssertTrue([timestamp isKindOfClass:[FIRTimestamp class]]); XCTAssertEqualObjects([snapshot dataWithOptions:_returnEstimatedValue], [self expectedDataWithTimestamp:timestamp]); } @@ -148,10 +148,10 @@ /** Verifies a snapshot containing _setData but with resolved server timestamps. */ - (void)verifySnapshotWithResolvedTimestamps:(FIRDocumentSnapshot *)snapshot { XCTAssertTrue(snapshot.exists); - NSDate *when = snapshot[@"when"]; - XCTAssertTrue([when isKindOfClass:[NSDate class]]); + FIRTimestamp *when = snapshot[@"when"]; + XCTAssertTrue([when isKindOfClass:[FIRTimestamp class]]); // Tolerate up to 10 seconds of clock skew between client and server. - XCTAssertEqualWithAccuracy(when.timeIntervalSinceNow, 0, 10); + XCTAssertEqualWithAccuracy(when.seconds, [FIRTimestamp timestamp].seconds, 10); // Validate the rest of the document. XCTAssertEqualObjects(snapshot.data, [self expectedDataWithTimestamp:when]); @@ -213,14 +213,14 @@ XCTAssertEqualObjects([localSnapshot valueForField:@"a"], [NSNull null]); XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42); XCTAssertTrue([[localSnapshot valueForField:@"a" options:_returnEstimatedValue] - isKindOfClass:[NSDate class]]); + isKindOfClass:[FIRTimestamp class]]); FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent]; - XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]); + XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[FIRTimestamp class]]); XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnPreviousValue] - isKindOfClass:[NSDate class]]); + isKindOfClass:[FIRTimestamp class]]); XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnEstimatedValue] - isKindOfClass:[NSDate class]]); + isKindOfClass:[FIRTimestamp class]]); } - (void)testServerTimestampsWithConsecutiveUpdates { @@ -241,7 +241,7 @@ [self enableNetwork]; FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent]; - XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]); + XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[FIRTimestamp class]]); } - (void)testServerTimestampsPreviousValueFromLocalMutation { @@ -266,7 +266,7 @@ [self enableNetwork]; FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent]; - XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]); + XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[FIRTimestamp class]]); } - (void)testServerTimestampsWorkViaTransactionSet { diff --git a/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm b/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm index 5140b90..740cde0 100644 --- a/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRTypeTests.mm @@ -56,9 +56,22 @@ }]; } -- (void)testCanReadAndWriteTimestampFields { +- (void)testCanReadAndWriteDateFields { // Choose a value that can be converted losslessly between fixed point and double - NSDate *timestamp = [NSDate dateWithTimeIntervalSince1970:1491847082.125]; + NSDate *date = [NSDate dateWithTimeIntervalSince1970:1491847082.125]; + + // NSDates are read back as FIRTimestamps, so assertSuccessfulRoundtrip cannot be used here. + FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"]; + [self writeDocumentRef:doc data:@{@"date" : date}]; + FIRDocumentSnapshot *document = [self readDocumentForRef:doc]; + XCTAssertTrue(document.exists); + XCTAssertEqualObjects(document.data, @{@"date" : [FIRTimestamp timestampWithDate:date]}); +} + +- (void)testCanReadAndWriteTimestampFields { + // Timestamps are currently truncated to microseconds on the backend, so only be precise to + // microseconds to ensure the value read back is exactly the same. + FIRTimestamp *timestamp = [FIRTimestamp timestampWithSeconds:123456 nanoseconds:123456000]; [self assertSuccessfulRoundtrip:@{@"timestamp" : timestamp}]; } diff --git a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm index 1a207f4..98504b5 100644 --- a/Firestore/Example/Tests/Model/FSTFieldValueTests.mm +++ b/Firestore/Example/Tests/Model/FSTFieldValueTests.mm @@ -227,10 +227,8 @@ union DoubleBits { for (id value in values) { FSTFieldValue *wrapped = FSTTestFieldValue(value); XCTAssertEqualObjects([wrapped class], [FSTTimestampValue class]); - XCTAssertEqualObjects([wrapped value], value); - - XCTAssertEqualObjects(((FSTTimestampValue *)wrapped).internalValue, - [FIRTimestamp timestampWithDate:value]); + XCTAssertEqualObjects([[wrapped value] class], [FIRTimestamp class]); + XCTAssertEqualObjects([wrapped value], [FIRTimestamp timestampWithDate:value]); } } @@ -572,14 +570,14 @@ union DoubleBits { FSTObjectValue *value = FSTTestObjectValue(input); id output = [value value]; { - XCTAssertTrue([output[@"array"][1] isKindOfClass:[NSDate class]]); - NSDate *actual = output[@"array"][1]; - XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, 0.000001); + XCTAssertTrue([output[@"array"][1] isKindOfClass:[FIRTimestamp class]]); + FIRTimestamp *actual = output[@"array"][1]; + XCTAssertEqualObjects([FIRTimestamp timestampWithDate:date], actual); } { - XCTAssertTrue([output[@"obj"][@"date"] isKindOfClass:[NSDate class]]); - NSDate *actual = output[@"obj"][@"date"]; - XCTAssertEqualWithAccuracy(date.timeIntervalSince1970, actual.timeIntervalSince1970, 0.000001); + XCTAssertTrue([output[@"obj"][@"date"] isKindOfClass:[FIRTimestamp class]]); + FIRTimestamp *actual = output[@"array"][1]; + XCTAssertEqualObjects([FIRTimestamp timestampWithDate:date], actual); } } diff --git a/Firestore/Example/Tests/Model/FSTMutationTests.mm b/Firestore/Example/Tests/Model/FSTMutationTests.mm index 40ded40..1f9193e 100644 --- a/Firestore/Example/Tests/Model/FSTMutationTests.mm +++ b/Firestore/Example/Tests/Model/FSTMutationTests.mm @@ -143,7 +143,7 @@ using firebase::firestore::model::DocumentKey; mutationResult:mutationResult]; NSDictionary *expectedData = - @{ @"foo" : @{@"bar" : _timestamp.approximateDateValue}, + @{ @"foo" : @{@"bar" : _timestamp.dateValue}, @"baz" : @"baz-value" }; XCTAssertEqualObjects(transformedDoc, FSTTestDoc("collection/key", 0, expectedData, NO)); } diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 059f257..611bcc8 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -129,6 +129,7 @@ NS_ASSUME_NONNULL_BEGIN } settings.host = host; settings.persistenceEnabled = YES; + settings.timestampsInSnapshotsEnabled = YES; NSLog(@"Configured integration test for %@ with SSL: %@", settings.host, settings.sslEnabled ? @"YES" : @"NO"); return settings; |