diff options
Diffstat (limited to 'Firestore/Example')
5 files changed, 716 insertions, 10 deletions
diff --git a/Firestore/Example/Firestore.xcodeproj/project.pbxproj b/Firestore/Example/Firestore.xcodeproj/project.pbxproj index d20dc48..dd64351 100644 --- a/Firestore/Example/Firestore.xcodeproj/project.pbxproj +++ b/Firestore/Example/Firestore.xcodeproj/project.pbxproj @@ -131,6 +131,7 @@ 6003F5B1195388D20070C39A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F58D195388D20070C39A /* Foundation.framework */; }; 6003F5B2195388D20070C39A /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6003F591195388D20070C39A /* UIKit.framework */; }; 6003F5BA195388D20070C39A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6003F5B8195388D20070C39A /* InfoPlist.strings */; }; + 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */; }; 6ED54761B845349D43DB6B78 /* Pods_Firestore_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 75A6FE51C1A02DF38F62FAAD /* Pods_Firestore_Example.framework */; }; 71719F9F1E33DC2100824A3D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 71719F9D1E33DC2100824A3D /* LaunchScreen.storyboard */; }; 7346E61D20325C6900FD6CEF /* FSTDispatchQueueTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */; }; @@ -356,6 +357,7 @@ 6003F5AF195388D20070C39A /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 6003F5B7195388D20070C39A /* Tests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Tests-Info.plist"; sourceTree = "<group>"; }; 6003F5B9195388D20070C39A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FIRFirestoreSourceTests.mm; sourceTree = "<group>"; }; 69F6A10DBD6187489481CD76 /* Pods_Firestore_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Firestore_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71719F9E1E33DC2100824A3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; 7346E61C20325C6900FD6CEF /* FSTDispatchQueueTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FSTDispatchQueueTests.mm; sourceTree = "<group>"; }; @@ -885,6 +887,7 @@ isa = PBXGroup; children = ( 73866A9F2082B069009BB4FF /* FIRArrayTransformTests.mm */, + 6161B5012047140400A99DBB /* FIRFirestoreSourceTests.mm */, 5492E070202154D600B64F25 /* FIRCursorTests.mm */, 5492E06C202154D500B64F25 /* FIRDatabaseTests.mm */, 5492E06A202154D500B64F25 /* FIRFieldsTests.mm */, @@ -1559,6 +1562,7 @@ buildActionMask = 2147483647; files = ( 73866AA12082B0A5009BB4FF /* FIRArrayTransformTests.mm in Sources */, + 6161B5032047140C00A99DBB /* FIRFirestoreSourceTests.mm in Sources */, 5492E076202154D600B64F25 /* FIRValidationTests.mm in Sources */, 5492E072202154D600B64F25 /* FIRQueryTests.mm in Sources */, 5491BC731FB44593008B3588 /* FSTIntegrationTestCase.mm in Sources */, diff --git a/Firestore/Example/SwiftBuildTest/main.swift b/Firestore/Example/SwiftBuildTest/main.swift index 00839c4..f61da2b 100644 --- a/Firestore/Example/SwiftBuildTest/main.swift +++ b/Firestore/Example/SwiftBuildTest/main.swift @@ -32,8 +32,10 @@ func main() { addDocument(to: collectionRef) readDocument(at: documentRef) + readDocumentWithSource(at: documentRef) readDocuments(matching: query) + readDocumentsWithSource(matching: query) listenToDocument(at: documentRef) @@ -230,6 +232,15 @@ func readDocument(at docRef: DocumentReference) { } } +func readDocumentWithSource(at docRef: DocumentReference) { + docRef.getDocument(source: FirestoreSource.default) { document, error in + } + docRef.getDocument(source: .server) { document, error in + } + docRef.getDocument(source: FirestoreSource.cache) { document, error in + } +} + func readDocuments(matching query: Query) { query.getDocuments { querySnapshot, error in // TODO(mikelehen): Figure out how to make "for..in" syntax work @@ -240,6 +251,15 @@ func readDocuments(matching query: Query) { } } +func readDocumentsWithSource(matching query: Query) { + query.getDocuments(source: FirestoreSource.default) { querySnapshot, error in + } + query.getDocuments(source: .server) { querySnapshot, error in + } + query.getDocuments(source: FirestoreSource.cache) { querySnapshot, error in + } +} + func listenToDocument(at docRef: DocumentReference) { let listener = docRef.addSnapshotListener { document, error in if let error = error { diff --git a/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm b/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm new file mode 100644 index 0000000..0f844c6 --- /dev/null +++ b/Firestore/Example/Tests/Integration/API/FIRFirestoreSourceTests.mm @@ -0,0 +1,648 @@ +/* + * Copyright 2018 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import <FirebaseFirestore/FirebaseFirestore.h> + +#import <XCTest/XCTest.h> + +#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" +#import "Firestore/Source/API/FIRFirestore+Internal.h" +#import "Firestore/Source/Core/FSTFirestoreClient.h" + +@interface FIRFirestoreSourceTests : FSTIntegrationTestCase +@end + +@implementation FIRFirestoreSourceTests + +- (void)testGetDocumentWhileOnlineWithDefaultSource { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, is *not* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + XCTAssertTrue(result.exists); + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineWithDefaultSource { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to known values + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"} + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they are *not* from the cache, and match the + // initialDocs. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col]; + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects( + FIRQuerySnapshotGetData(result), + (@[ @{@"key1" : @"value1"}, @{@"key2" : @"value2"}, @{@"key3" : @"value3"} ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineWithDefaultSource { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary<NSString *, id> *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTAssertTrue(false, "Because we're offline, this should never occur."); + }]; + + // get doc and ensure it exists, *is* from the cache, and matches the + // newData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); +} + +- (void)testGetCollectionWhileOfflineWithDefaultSource { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to known values + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"} + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // get docs and ensure they *are* from the cache, and matches the updated data. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); +} + +- (void)testGetDocumentWhileOnlineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, *is* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they *are* from the cache, and matches the + // initialDocs. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, + @{@"key2" : @"value2"}, + @{@"key3" : @"value3"}, + ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary<NSString *, id> *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTFail("Because we're offline, this should never occur."); + }]; + + // get doc and ensure it exists, *is* from the cache, and matches the + // newData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); +} + +- (void)testGetCollectionWhileOfflineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // get docs and ensure they *are* from the cache, and matches the updated + // data. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); +} + +- (void)testGetDocumentWhileOnlineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key" : @"value"}; + [self writeDocumentRef:doc data:initialData]; + + // get doc and ensure that it exists, is *not* from the cache, and matches + // the initialData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRFirestoreSourceServer]; + XCTAssertTrue(result.exists); + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, initialData); +} + +- (void)testGetCollectionWhileOnlineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // get docs and ensure they are *not* from the cache, and matches the + // initialData. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRFirestoreSourceServer]; + XCTAssertFalse(result.metadata.fromCache); + XCTAssertFalse(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, + @{@"key2" : @"value2"}, + @{@"key3" : @"value3"}, + ])); + XCTAssertEqualObjects(FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3" : @"value3"} ] + ])); +} + +- (void)testGetDocumentWhileOfflineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc and ensure it cannot be retreived + XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; + [doc getDocumentWithSource:FIRFirestoreSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetCollectionWhileOfflineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get docs and ensure they cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithSource:FIRFirestoreSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetDocumentWhileOfflineWithDifferentSource { + FIRDocumentReference *doc = [self documentRef]; + + // set document to a known value + NSDictionary<NSString *, id> *initialData = @{@"key1" : @"value1"}; + [self writeDocumentRef:doc data:initialData]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the doc (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + NSDictionary<NSString *, id> *newData = @{@"key2" : @"value2"}; + [doc setData:newData + completion:^(NSError *_Nullable error) { + XCTAssertTrue(false, "Because we're offline, this should never occur."); + }]; + + // Create an initial listener for this query (to attempt to disrupt the gets below) and wait for + // the listener to deliver its initial snapshot before continuing. + XCTestExpectation *listenerReady = [self expectationWithDescription:@"listenerReady"]; + [doc addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) { + [listenerReady fulfill]; + }]; + [self awaitExpectations]; + + // get doc (from cache) and ensure it exists, *is* from the cache, and + // matches the newData. + FIRDocumentSnapshot *result = [self readDocumentForRef:doc source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); + + // attempt to get doc (with default get source) + result = [self readDocumentForRef:doc source:FIRFirestoreSourceDefault]; + XCTAssertTrue(result.exists); + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(result.data, newData); + + // attempt to get doc (from the server) and ensure it cannot be retreived + XCTestExpectation *failedGetDocCompletion = [self expectationWithDescription:@"failedGetDoc"]; + [doc getDocumentWithSource:FIRFirestoreSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetCollectionWhileOfflineWithDifferentSource { + FIRCollectionReference *col = [self collectionRef]; + + // set a few documents to a known value + NSDictionary<NSString *, NSDictionary<NSString *, id> *> *initialDocs = @{ + @"doc1" : @{@"key1" : @"value1"}, + @"doc2" : @{@"key2" : @"value2"}, + @"doc3" : @{@"key3" : @"value3"}, + }; + [self writeAllDocuments:initialDocs toCollection:col]; + + // go offline for the rest of this test + [self disableNetwork]; + + // update the docs (though don't wait for a server response. We're offline; so + // that ain't happening!). This allows us to further distinguished cached vs + // server responses below. + [[col documentWithPath:@"doc2"] setData:@{@"key2b" : @"value2b"} options:FIRSetOptions.merge]; + [[col documentWithPath:@"doc3"] setData:@{@"key3b" : @"value3b"}]; + [[col documentWithPath:@"doc4"] setData:@{@"key4" : @"value4"}]; + + // Create an initial listener for this query (to attempt to disrupt the gets + // below) and wait for the listener to deliver its initial snapshot before + // continuing. + XCTestExpectation *listenerReady = [self expectationWithDescription:@"listenerReady"]; + [col addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) { + [listenerReady fulfill]; + }]; + [self awaitExpectations]; + + // get docs (from cache) and ensure they *are* from the cache, and + // matches the updated data. + FIRQuerySnapshot *result = [self readDocumentSetForRef:col source:FIRFirestoreSourceCache]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertTrue(result.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); + + // attempt to get docs (with default get source) + result = [self readDocumentSetForRef:col source:FIRFirestoreSourceDefault]; + XCTAssertTrue(result.metadata.fromCache); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(result), (@[ + @{@"key1" : @"value1"}, @{@"key2" : @"value2", @"key2b" : @"value2b"}, + @{@"key3b" : @"value3b"}, @{@"key4" : @"value4"} + ])); + XCTAssertEqualObjects( + FIRQuerySnapshotGetDocChangesData(result), (@[ + @[ @(FIRDocumentChangeTypeAdded), @"doc1", @{@"key1" : @"value1"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc2", @{@"key2" : @"value2", @"key2b" : @"value2b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc3", @{@"key3b" : @"value3b"} ], + @[ @(FIRDocumentChangeTypeAdded), @"doc4", @{@"key4" : @"value4"} ] + ])); + + // attempt to get docs (from the server) and ensure they cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithSource:FIRFirestoreSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingDocWhileOnlineWithDefaultSource { + FIRDocumentReference *doc = [self documentRef]; + + // get doc and ensure that it does not exist and is *not* from the cache. + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertFalse(snapshot.exists); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingCollectionWhileOnlineWithDefaultSource { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure it's empty and that it's *not* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineWithDefaultSource { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineWithDefaultSource { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOnlineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithSource:FIRFirestoreSourceCache + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOnlineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRFirestoreSourceCache]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineCacheOnly { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithSource:FIRFirestoreSourceCache + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineCacheOnly { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // get collection and ensure it's empty and that it *is* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRFirestoreSourceCache]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertTrue(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOnlineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // get doc and ensure that it does not exist and is *not* from the cache. + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc source:FIRFirestoreSourceServer]; + XCTAssertFalse(snapshot.exists); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingCollectionWhileOnlineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // get collection and ensure that it's empty and that it's *not* from the cache. + FIRQuerySnapshot *snapshot = [self readDocumentSetForRef:col source:FIRFirestoreSourceServer]; + XCTAssertEqual(snapshot.count, 0); + XCTAssertEqual(snapshot.documentChanges.count, 0); + XCTAssertFalse(snapshot.metadata.fromCache); + XCTAssertFalse(snapshot.metadata.hasPendingWrites); +} + +- (void)testGetNonExistingDocWhileOfflineServerOnly { + FIRDocumentReference *doc = [self documentRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get doc. Currently, this is expected to fail. In the future, we + // might consider adding support for negative cache hits so that we know + // certain documents *don't* exist. + XCTestExpectation *getNonExistingDocCompletion = + [self expectationWithDescription:@"getNonExistingDoc"]; + [doc getDocumentWithSource:FIRFirestoreSourceServer + completion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [getNonExistingDocCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testGetNonExistingCollectionWhileOfflineServerOnly { + FIRCollectionReference *col = [self collectionRef]; + + // go offline for the rest of this test + [self disableNetwork]; + + // attempt to get collection and ensure that it cannot be retreived + XCTestExpectation *failedGetDocsCompletion = [self expectationWithDescription:@"failedGetDocs"]; + [col getDocumentsWithSource:FIRFirestoreSourceServer + completion:^(FIRQuerySnapshot *snapshot, NSError *error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeUnavailable); + [failedGetDocsCompletion fulfill]; + }]; + [self awaitExpectations]; +} + +@end diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h index e27491b..585d99a 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.h @@ -20,6 +20,8 @@ #import "Firestore/Example/Tests/Util/XCTestCase+Await.h" #import "Firestore/Source/Core/FSTTypes.h" +#import "FIRFirestoreSource.h" + @class FIRCollectionReference; @class FIRDocumentSnapshot; @class FIRDocumentReference; @@ -71,8 +73,13 @@ extern "C" { - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref; +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref + source:(FIRFirestoreSource)source; + - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query; +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query source:(FIRFirestoreSource)source; + - (FIRDocumentSnapshot *)readSnapshotForRef:(FIRDocumentReference *)query requireOnline:(BOOL)online; @@ -108,6 +115,10 @@ NSArray<NSDictionary<NSString *, id> *> *FIRQuerySnapshotGetData(FIRQuerySnapsho /** Converts the FIRQuerySnapshot to an NSArray containing the document IDs in order. */ NSArray<NSString *> *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs); +/** Converts the FIRQuerySnapshot to an NSArray containing an NSArray containing the doc change data + * in order of { type, doc title, doc data }. */ +NSArray<NSArray<id> *> *FIRQuerySnapshotGetDocChangesData(FIRQuerySnapshot *docs); + #if __cplusplus } // extern "C" #endif diff --git a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm index 9bfdb3b..1817015 100644 --- a/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm +++ b/Firestore/Example/Tests/Util/FSTIntegrationTestCase.mm @@ -212,28 +212,39 @@ NS_ASSUME_NONNULL_BEGIN } - (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref { + return [self readDocumentForRef:ref source:FIRFirestoreSourceDefault]; +} + +- (FIRDocumentSnapshot *)readDocumentForRef:(FIRDocumentReference *)ref + source:(FIRFirestoreSource)source { __block FIRDocumentSnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [ref getDocumentWithCompletion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { - XCTAssertNil(error); - result = doc; - [expectation fulfill]; - }]; + [ref getDocumentWithSource:source + completion:^(FIRDocumentSnapshot *doc, NSError *_Nullable error) { + XCTAssertNil(error); + result = doc; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; } - (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query { + return [self readDocumentSetForRef:query source:FIRFirestoreSourceDefault]; +} + +- (FIRQuerySnapshot *)readDocumentSetForRef:(FIRQuery *)query source:(FIRFirestoreSource)source { __block FIRQuerySnapshot *result; XCTestExpectation *expectation = [self expectationWithDescription:@"getData"]; - [query getDocumentsWithCompletion:^(FIRQuerySnapshot *documentSet, NSError *error) { - XCTAssertNil(error); - result = documentSet; - [expectation fulfill]; - }]; + [query getDocumentsWithSource:source + completion:^(FIRQuerySnapshot *documentSet, NSError *error) { + XCTAssertNil(error); + result = documentSet; + [expectation fulfill]; + }]; [self awaitExpectations]; return result; @@ -329,6 +340,18 @@ extern "C" NSArray<NSString *> *FIRQuerySnapshotGetIDs(FIRQuerySnapshot *docs) { return result; } +extern "C" NSArray<NSArray<id> *> *FIRQuerySnapshotGetDocChangesData(FIRQuerySnapshot *docs) { + NSMutableArray<NSMutableArray<id> *> *result = [NSMutableArray array]; + for (FIRDocumentChange *docChange in docs.documentChanges) { + NSMutableArray<id> *docChangeData = [NSMutableArray array]; + [docChangeData addObject:@(docChange.type)]; + [docChangeData addObject:docChange.document.documentID]; + [docChangeData addObject:docChange.document.data]; + [result addObject:docChangeData]; + } + return result; +} + @end NS_ASSUME_NONNULL_END |