aboutsummaryrefslogtreecommitdiffhomepage
path: root/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
diff options
context:
space:
mode:
Diffstat (limited to 'Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m')
-rw-r--r--Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m187
1 files changed, 165 insertions, 22 deletions
diff --git a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
index 2ee3966..da3c03c 100644
--- a/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
+++ b/Firestore/Example/Tests/Integration/API/FIRServerTimestampTests.m
@@ -18,10 +18,10 @@
#import <XCTest/XCTest.h>
-#import "Firestore/Source/Core/FSTFirestoreClient.h"
-
#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h"
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
+#import "Firestore/Source/API/FIRFirestore+Internal.h"
+#import "Firestore/Source/Core/FSTFirestoreClient.h"
@interface FIRServerTimestampTests : FSTIntegrationTestCase
@end
@@ -42,11 +42,20 @@
// Listener registration for a listener maintained during the course of the test.
id<FIRListenerRegistration> _listenerRegistration;
+
+ // Snapshot options that return the previous value for pending server timestamps.
+ FIRSnapshotOptions *_returnPreviousValue;
+ FIRSnapshotOptions *_returnEstimatedValue;
}
- (void)setUp {
[super setUp];
+ _returnPreviousValue =
+ [FIRSnapshotOptions serverTimestampBehavior:FIRServerTimestampBehaviorPrevious];
+ _returnEstimatedValue =
+ [FIRSnapshotOptions serverTimestampBehavior:FIRServerTimestampBehaviorEstimate];
+
// Data written in tests via set.
_setData = @{
@"a" : @42,
@@ -63,7 +72,7 @@
_docRef = [self documentRef];
_accumulator = [FSTEventAccumulator accumulatorForTest:self];
- _listenerRegistration = [_docRef addSnapshotListener:_accumulator.handler];
+ _listenerRegistration = [_docRef addSnapshotListener:_accumulator.valueEventHandler];
// Wait for initial nil snapshot to avoid potential races.
FIRDocumentSnapshot *initialSnapshot = [_accumulator awaitEventWithName:@"initial event"];
@@ -76,8 +85,10 @@
[super tearDown];
}
-// Returns the expected data, with an arbitrary timestamp substituted in.
-- (NSDictionary *)expectedDataWithTimestamp:(id _Nullable)timestamp {
+#pragma mark - Test Helpers
+
+/** Returns the expected data, with the specified timestamp substituted in. */
+- (NSDictionary *)expectedDataWithTimestamp:(nullable id)timestamp {
return @{ @"a" : @42, @"when" : timestamp, @"deep" : @{@"when" : timestamp} };
}
@@ -88,26 +99,61 @@
XCTAssertEqualObjects(initialDataSnap.data, _initialData);
}
-/** Waits for a snapshot containing _setData but with NSNull for the timestamps. */
-- (void)waitForLocalEvent {
- FIRDocumentSnapshot *localSnap = [_accumulator awaitEventWithName:@"Local event."];
- XCTAssertEqualObjects(localSnap.data, [self expectedDataWithTimestamp:[NSNull null]]);
+/** Waits for a snapshot with local writes. */
+- (FIRDocumentSnapshot *)waitForLocalEvent {
+ FIRDocumentSnapshot *snapshot = [_accumulator awaitEventWithName:@"Local event."];
+ XCTAssertTrue(snapshot.metadata.hasPendingWrites);
+ return snapshot;
+}
+
+/** Waits for a snapshot that has no pending writes */
+- (FIRDocumentSnapshot *)waitForRemoteEvent {
+ FIRDocumentSnapshot *snapshot = [_accumulator awaitEventWithName:@"Remote event."];
+ XCTAssertFalse(snapshot.metadata.hasPendingWrites);
+ return snapshot;
+}
+
+/** Verifies a snapshot containing _setData but with NSNull for the timestamps. */
+- (void)verifyTimestampsAreNullInSnapshot:(FIRDocumentSnapshot *)snapshot {
+ XCTAssertEqualObjects(snapshot.data, [self expectedDataWithTimestamp:[NSNull null]]);
+}
+
+/** 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]]);
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnEstimatedValue],
+ [self expectedDataWithTimestamp:timestamp]);
+}
+
+/**
+ * Verifies a snapshot containing _setData but using the previous field value for server
+ * timestamps.
+ */
+- (void)verifyTimestampsInSnapshot:(FIRDocumentSnapshot *)snapshot
+ fromPreviousSnapshot:(nullable FIRDocumentSnapshot *)previousSnapshot {
+ if (previousSnapshot == nil) {
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnPreviousValue],
+ [self expectedDataWithTimestamp:[NSNull null]]);
+ } else {
+ XCTAssertEqualObjects([snapshot dataWithOptions:_returnPreviousValue],
+ [self expectedDataWithTimestamp:previousSnapshot[@"when"]]);
+ }
}
-/** Waits for a snapshot containing _setData but with resolved server timestamps. */
-- (void)waitForRemoteEvent {
- // server event should have a resolved timestamp; verify it.
- FIRDocumentSnapshot *remoteSnap = [_accumulator awaitEventWithName:@"Remote event"];
- XCTAssertTrue(remoteSnap.exists);
- NSDate *when = remoteSnap[@"when"];
+/** Verifies a snapshot containing _setData but with resolved server timestamps. */
+- (id)verifySnapshotWithResolvedTimestamps:(FIRDocumentSnapshot *)snapshot {
+ XCTAssertTrue(snapshot.exists);
+ NSDate *when = snapshot[@"when"];
XCTAssertTrue([when isKindOfClass:[NSDate class]]);
// Tolerate up to 10 seconds of clock skew between client and server.
XCTAssertEqualWithAccuracy(when.timeIntervalSinceNow, 0, 10);
// Validate the rest of the document.
- XCTAssertEqualObjects(remoteSnap.data, [self expectedDataWithTimestamp:when]);
+ XCTAssertEqualObjects(snapshot.data, [self expectedDataWithTimestamp:when]);
}
+/** Runs a transaction block. */
- (void)runTransactionBlock:(void (^)(FIRTransaction *transaction))transactionBlock {
XCTestExpectation *expectation = [self expectationWithDescription:@"transaction complete"];
[_docRef.firestore runTransactionWithBlock:^id(FIRTransaction *transaction, NSError **pError) {
@@ -121,17 +167,114 @@
[self awaitExpectations];
}
+/** Disables the network synchronously. */
+- (void)disableNetwork {
+ [_docRef.firestore.client disableNetworkWithCompletion:_accumulator.errorEventHandler];
+ [_accumulator awaitEventWithName:@"Disconnect event."];
+}
+
+/** Enables the network synchronously. */
+- (void)enableNetwork {
+ [_docRef.firestore.client enableNetworkWithCompletion:_accumulator.errorEventHandler];
+ [_accumulator awaitEventWithName:@"Reconnect event."];
+}
+
+#pragma mark - Test Cases
+
- (void)testServerTimestampsWorkViaSet {
[self writeDocumentRef:_docRef data:_setData];
- [self waitForLocalEvent];
- [self waitForRemoteEvent];
+ [self verifyTimestampsAreNullInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
}
- (void)testServerTimestampsWorkViaUpdate {
[self writeInitialData];
[self updateDocumentRef:_docRef data:_updateData];
- [self waitForLocalEvent];
- [self waitForRemoteEvent];
+ [self verifyTimestampsAreNullInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithEstimatedValue {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsAreEstimatedInSnapshot:[self waitForLocalEvent]];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithPreviousValue {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+
+ [_docRef updateData:_updateData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:remoteSnapshot];
+
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+}
+
+- (void)testServerTimestampsWithPreviousValueOfDifferentType {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a"], [NSNull null]);
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+ XCTAssertTrue([[localSnapshot valueForField:@"a" options:_returnEstimatedValue]
+ isKindOfClass:[NSDate class]]);
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnPreviousValue]
+ isKindOfClass:[NSDate class]]);
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a" options:_returnEstimatedValue]
+ isKindOfClass:[NSDate class]]);
+}
+
+- (void)testServerTimestampsWithConsecutiveUpdates {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [self disableNetwork];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [self enableNetwork];
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
+}
+
+- (void)testServerTimestampsPreviousValueFromLocalMutation {
+ [self writeDocumentRef:_docRef data:_setData];
+ [self verifyTimestampsInSnapshot:[self waitForLocalEvent] fromPreviousSnapshot:nil];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
+
+ [self disableNetwork];
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ FIRDocumentSnapshot *localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @42);
+
+ [_docRef updateData:@{ @"a" : @1337 }];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a"], @1337);
+
+ [_docRef updateData:@{@"a" : [FIRFieldValue fieldValueForServerTimestamp]}];
+ localSnapshot = [self waitForLocalEvent];
+ XCTAssertEqualObjects([localSnapshot valueForField:@"a" options:_returnPreviousValue], @1337);
+
+ [self enableNetwork];
+
+ FIRDocumentSnapshot *remoteSnapshot = [self waitForRemoteEvent];
+ XCTAssertTrue([[remoteSnapshot valueForField:@"a"] isKindOfClass:[NSDate class]]);
}
- (void)testServerTimestampsWorkViaTransactionSet {
@@ -139,7 +282,7 @@
[transaction setData:_setData forDocument:_docRef];
}];
- [self waitForRemoteEvent];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
}
- (void)testServerTimestampsWorkViaTransactionUpdate {
@@ -147,7 +290,7 @@
[self runTransactionBlock:^(FIRTransaction *transaction) {
[transaction updateData:_updateData forDocument:_docRef];
}];
- [self waitForRemoteEvent];
+ [self verifySnapshotWithResolvedTimestamps:[self waitForRemoteEvent]];
}
- (void)testServerTimestampsFailViaUpdateOnNonexistentDocument {