mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-10 22:45:59 +05:45
* Expose network management in public API * Clean up a few more references to the internal access of network management * Move test * Update comments * Swap _Nullable for nullable * Fix comment * Add tests, including swift * Styling
964 lines
34 KiB
Objective-C
964 lines
34 KiB
Objective-C
/*
|
|
* Copyright 2017 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;
|
|
|
|
#import <FirebaseFirestore/FIRFirestore.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 FIRDatabaseTests : FSTIntegrationTestCase
|
|
@end
|
|
|
|
@implementation FIRDatabaseTests
|
|
|
|
- (void)testCanUpdateAnExistingDocument {
|
|
FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
|
|
NSDictionary<NSString *, id> *initialData =
|
|
@{ @"desc" : @"Description",
|
|
@"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
|
|
NSDictionary<NSString *, id> *updateData =
|
|
@{@"desc" : @"NewDescription", @"owner.email" : @"new@xyz.com"};
|
|
NSDictionary<NSString *, id> *finalData =
|
|
@{ @"desc" : @"NewDescription",
|
|
@"owner" : @{@"name" : @"Jonny", @"email" : @"new@xyz.com"} };
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
|
|
XCTestExpectation *updateCompletion = [self expectationWithDescription:@"updateData"];
|
|
[doc updateData:updateData
|
|
completion:^(NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
[updateCompletion fulfill];
|
|
}];
|
|
[self awaitExpectations];
|
|
|
|
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
|
|
XCTAssertTrue(result.exists);
|
|
XCTAssertEqualObjects(result.data, finalData);
|
|
}
|
|
|
|
- (void)testCanDeleteAFieldWithAnUpdate {
|
|
FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
|
|
NSDictionary<NSString *, id> *initialData =
|
|
@{ @"desc" : @"Description",
|
|
@"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
|
|
NSDictionary<NSString *, id> *updateData =
|
|
@{@"owner.email" : [FIRFieldValue fieldValueForDelete]};
|
|
NSDictionary<NSString *, id> *finalData =
|
|
@{ @"desc" : @"Description",
|
|
@"owner" : @{@"name" : @"Jonny"} };
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
[self updateDocumentRef:doc data:updateData];
|
|
|
|
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
|
|
XCTAssertTrue(result.exists);
|
|
XCTAssertEqualObjects(result.data, finalData);
|
|
}
|
|
|
|
- (void)testDeleteDocument {
|
|
FIRDocumentReference *doc = [self.db documentWithPath:@"rooms/eros"];
|
|
NSDictionary<NSString *, id> *data = @{@"value" : @"foo"};
|
|
[self writeDocumentRef:doc data:data];
|
|
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
|
|
XCTAssertEqualObjects(result.data, data);
|
|
[self deleteDocumentRef:doc];
|
|
result = [self readDocumentForRef:doc];
|
|
XCTAssertFalse(result.exists);
|
|
}
|
|
|
|
- (void)testCanRetrieveDocumentThatDoesNotExist {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
|
|
XCTAssertNil(result.data);
|
|
XCTAssertNil(result[@"foo"]);
|
|
}
|
|
|
|
- (void)testCannotUpdateNonexistentDocument {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
XCTestExpectation *setCompletion = [self expectationWithDescription:@"setData"];
|
|
[doc updateData:@{@"owner" : @"abc"}
|
|
completion:^(NSError *_Nullable error) {
|
|
XCTAssertNotNil(error);
|
|
XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain);
|
|
XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound);
|
|
[setCompletion fulfill];
|
|
}];
|
|
[self awaitExpectations];
|
|
FIRDocumentSnapshot *result = [self readDocumentForRef:doc];
|
|
XCTAssertFalse(result.exists);
|
|
}
|
|
|
|
- (void)testCanOverwriteDataAnExistingDocumentUsingSet {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData =
|
|
@{ @"desc" : @"Description",
|
|
@"owner" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"} };
|
|
NSDictionary<NSString *, id> *udpateData = @{@"desc" : @"NewDescription"};
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
[self writeDocumentRef:doc data:udpateData];
|
|
|
|
FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
|
|
XCTAssertEqualObjects(document.data, udpateData);
|
|
}
|
|
|
|
- (void)testCanMergeDataWithAnExistingDocumentUsingSet {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{
|
|
@"desc" : @"Description",
|
|
@"owner.data" : @{@"name" : @"Jonny", @"email" : @"abc@xyz.com"}
|
|
};
|
|
NSDictionary<NSString *, id> *mergeData =
|
|
@{ @"updated" : @YES,
|
|
@"owner.data" : @{@"name" : @"Sebastian"} };
|
|
NSDictionary<NSString *, id> *finalData = @{
|
|
@"desc" : @"Description",
|
|
@"updated" : @YES,
|
|
@"owner.data" : @{@"name" : @"Sebastian", @"email" : @"abc@xyz.com"}
|
|
};
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
|
|
XCTestExpectation *completed =
|
|
[self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
|
|
|
|
[doc setData:mergeData
|
|
options:[FIRSetOptions merge]
|
|
completion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[completed fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
|
|
XCTAssertEqualObjects(document.data, finalData);
|
|
}
|
|
|
|
- (void)testCanMergeServerTimestamps {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{
|
|
@"updated" : @NO,
|
|
};
|
|
NSDictionary<NSString *, id> *mergeData =
|
|
@{@"time" : [FIRFieldValue fieldValueForServerTimestamp]};
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
|
|
XCTestExpectation *completed =
|
|
[self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
|
|
|
|
[doc setData:mergeData
|
|
options:[FIRSetOptions merge]
|
|
completion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[completed fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
|
|
XCTAssertEqual(document[@"updated"], @NO);
|
|
XCTAssertTrue([document[@"time"] isKindOfClass:[NSDate class]]);
|
|
}
|
|
|
|
- (void)testCanDeleteFieldUsingMerge {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{
|
|
@"untouched" : @YES,
|
|
@"foo" : @"bar",
|
|
@"nested" : @{@"untouched" : @YES, @"foo" : @"bar"}
|
|
};
|
|
NSDictionary<NSString *, id> *mergeData = @{
|
|
@"foo" : [FIRFieldValue fieldValueForDelete],
|
|
@"nested" : @{@"foo" : [FIRFieldValue fieldValueForDelete]}
|
|
};
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
|
|
XCTestExpectation *completed =
|
|
[self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
|
|
|
|
[doc setData:mergeData
|
|
options:[FIRSetOptions merge]
|
|
completion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[completed fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
|
|
XCTAssertEqual(document[@"untouched"], @YES);
|
|
XCTAssertNil(document[@"foo"]);
|
|
XCTAssertEqual(document[@"nested.untouched"], @YES);
|
|
XCTAssertNil(document[@"nested.foo"]);
|
|
}
|
|
|
|
- (void)testMergeReplacesArrays {
|
|
FIRDocumentReference *doc = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{
|
|
@"untouched" : @YES,
|
|
@"data" : @"old",
|
|
@"topLevel" : @[ @"old", @"old" ],
|
|
@"mapInArray" : @[ @{@"data" : @"old"} ]
|
|
};
|
|
NSDictionary<NSString *, id> *mergeData =
|
|
@{ @"data" : @"new",
|
|
@"topLevel" : @[ @"new" ],
|
|
@"mapInArray" : @[ @{@"data" : @"new"} ] };
|
|
NSDictionary<NSString *, id> *finalData = @{
|
|
@"untouched" : @YES,
|
|
@"data" : @"new",
|
|
@"topLevel" : @[ @"new" ],
|
|
@"mapInArray" : @[ @{@"data" : @"new"} ]
|
|
};
|
|
|
|
[self writeDocumentRef:doc data:initialData];
|
|
|
|
XCTestExpectation *completed =
|
|
[self expectationWithDescription:@"testCanMergeDataWithAnExistingDocumentUsingSet"];
|
|
|
|
[doc setData:mergeData
|
|
options:[FIRSetOptions merge]
|
|
completion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[completed fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
FIRDocumentSnapshot *document = [self readDocumentForRef:doc];
|
|
XCTAssertEqualObjects(document.data, finalData);
|
|
}
|
|
|
|
- (void)testAddingToACollectionYieldsTheCorrectDocumentReference {
|
|
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
|
|
FIRDocumentReference *ref = [coll addDocumentWithData:@{ @"foo" : @1 }];
|
|
|
|
XCTestExpectation *getCompletion = [self expectationWithDescription:@"getData"];
|
|
[ref getDocumentWithCompletion:^(FIRDocumentSnapshot *_Nullable document,
|
|
NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertEqualObjects(document.data, (@{ @"foo" : @1 }));
|
|
|
|
[getCompletion fulfill];
|
|
}];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)testListenCanBeCalledMultipleTimes {
|
|
FIRCollectionReference *coll = [self.db collectionWithPath:@"collection"];
|
|
FIRDocumentReference *doc = [coll documentWithAutoID];
|
|
|
|
XCTestExpectation *completed = [self expectationWithDescription:@"multiple addSnapshotListeners"];
|
|
|
|
__block NSDictionary<NSString *, id> *resultingData;
|
|
|
|
// Shut the compiler up about strong references to doc.
|
|
FIRDocumentReference *__weak weakDoc = doc;
|
|
|
|
[doc setData:@{@"foo" : @"bar"}
|
|
completion:^(NSError *error1) {
|
|
XCTAssertNil(error1);
|
|
FIRDocumentReference *strongDoc = weakDoc;
|
|
|
|
[strongDoc addSnapshotListener:^(FIRDocumentSnapshot *snapshot2, NSError *error2) {
|
|
XCTAssertNil(error2);
|
|
|
|
FIRDocumentReference *strongDoc2 = weakDoc;
|
|
[strongDoc2 addSnapshotListener:^(FIRDocumentSnapshot *snapshot3, NSError *error3) {
|
|
XCTAssertNil(error3);
|
|
resultingData = snapshot3.data;
|
|
[completed fulfill];
|
|
}];
|
|
}];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
XCTAssertEqualObjects(resultingData, @{@"foo" : @"bar"});
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_nonExistent {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
XCTestExpectation *snapshotCompletion = [self expectationWithDescription:@"snapshot"];
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertNotNil(doc);
|
|
XCTAssertFalse(doc.exists);
|
|
[snapshotCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forAdd {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
|
|
__block XCTestExpectation *dataCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertNotNil(doc);
|
|
XCTAssertFalse(doc.exists);
|
|
[emptyCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
|
|
[dataCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
dataCompletion = [self expectationWithDescription:@"data snapshot"];
|
|
|
|
[docRef setData:@{ @"a" : @1 }];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forAddIncludingMetadata {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
|
|
__block XCTestExpectation *dataCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
FIRDocumentListenOptions *options =
|
|
[[FIRDocumentListenOptions options] includeMetadataChanges:YES];
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListenerWithOptions:options
|
|
listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertNotNil(doc);
|
|
XCTAssertFalse(doc.exists);
|
|
[emptyCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTAssertEqualObjects(doc.data, (@{ @"a" : @1 }));
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
[dataCompletion fulfill];
|
|
|
|
} else if (callbacks == 4) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
dataCompletion = [self expectationWithDescription:@"data snapshot"];
|
|
|
|
[docRef setData:@{ @"a" : @1 }];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forChange {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqualObjects(doc.data, changedData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef setData:changedData];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forChangeIncludingMetadata {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
FIRDocumentListenOptions *options =
|
|
[[FIRDocumentListenOptions options] includeMetadataChanges:YES];
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListenerWithOptions:options
|
|
listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, YES);
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, NO);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTAssertEqualObjects(doc.data, changedData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, YES);
|
|
XCTAssertEqual(doc.metadata.isFromCache, NO);
|
|
|
|
} else if (callbacks == 4) {
|
|
XCTAssertEqualObjects(doc.data, changedData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, NO);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 5) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef setData:changedData];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forDelete {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, YES);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertFalse(doc.exists);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef deleteDocument];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testDocumentSnapshotEvents_forDeleteIncludingMetadata {
|
|
FIRDocumentReference *docRef = [[self.db collectionWithPath:@"rooms"] documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
FIRDocumentListenOptions *options =
|
|
[[FIRDocumentListenOptions options] includeMetadataChanges:YES];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[docRef addSnapshotListenerWithOptions:options
|
|
listener:^(FIRDocumentSnapshot *_Nullable doc, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, YES);
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqualObjects(doc.data, initialData);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, NO);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTAssertFalse(doc.exists);
|
|
XCTAssertEqual(doc.metadata.hasPendingWrites, NO);
|
|
XCTAssertEqual(doc.metadata.isFromCache, NO);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 4) {
|
|
XCTFail("Should not have received this callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef deleteDocument];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testQuerySnapshotEvents_forAdd {
|
|
FIRCollectionReference *roomsRef = [self collectionRef];
|
|
FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *newData = @{ @"a" : @1 };
|
|
|
|
XCTestExpectation *emptyCompletion = [self expectationWithDescription:@"empty snapshot"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqual(docSet.count, 0);
|
|
[emptyCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqual(docSet.count, 1);
|
|
XCTAssertEqualObjects(docSet.documents[0].data, newData);
|
|
XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTFail("Should not have received a third callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"changed snapshot"];
|
|
|
|
[docRef setData:newData];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testQuerySnapshotEvents_forChange {
|
|
FIRCollectionReference *roomsRef = [self collectionRef];
|
|
FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
NSDictionary<NSString *, id> *changedData = @{ @"b" : @2 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqual(docSet.count, 1);
|
|
XCTAssertEqualObjects(docSet.documents[0].data, initialData);
|
|
XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqual(docSet.count, 1);
|
|
XCTAssertEqualObjects(docSet.documents[0].data, changedData);
|
|
XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, YES);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 3) {
|
|
XCTFail("Should not have received a third callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef setData:changedData];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testQuerySnapshotEvents_forDelete {
|
|
FIRCollectionReference *roomsRef = [self collectionRef];
|
|
FIRDocumentReference *docRef = [roomsRef documentWithAutoID];
|
|
|
|
NSDictionary<NSString *, id> *initialData = @{ @"a" : @1 };
|
|
|
|
[self writeDocumentRef:docRef data:initialData];
|
|
|
|
XCTestExpectation *initialCompletion = [self expectationWithDescription:@"initial data"];
|
|
__block XCTestExpectation *changeCompletion;
|
|
__block int callbacks = 0;
|
|
|
|
id<FIRListenerRegistration> listenerRegistration =
|
|
[roomsRef addSnapshotListener:^(FIRQuerySnapshot *_Nullable docSet, NSError *error) {
|
|
callbacks++;
|
|
|
|
if (callbacks == 1) {
|
|
XCTAssertEqual(docSet.count, 1);
|
|
XCTAssertEqualObjects(docSet.documents[0].data, initialData);
|
|
XCTAssertEqual(docSet.documents[0].metadata.hasPendingWrites, NO);
|
|
[initialCompletion fulfill];
|
|
|
|
} else if (callbacks == 2) {
|
|
XCTAssertEqual(docSet.count, 0);
|
|
[changeCompletion fulfill];
|
|
|
|
} else if (callbacks == 4) {
|
|
XCTFail("Should not have received a third callback");
|
|
}
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
changeCompletion = [self expectationWithDescription:@"listen for changed data"];
|
|
|
|
[docRef deleteDocument];
|
|
[self awaitExpectations];
|
|
|
|
[listenerRegistration remove];
|
|
}
|
|
|
|
- (void)testExposesFirestoreOnDocumentReferences {
|
|
FIRDocumentReference *doc = [self.db documentWithPath:@"foo/bar"];
|
|
XCTAssertEqual(doc.firestore, self.db);
|
|
}
|
|
|
|
- (void)testExposesFirestoreOnQueries {
|
|
FIRQuery *q = [[self.db collectionWithPath:@"foo"] queryLimitedTo:5];
|
|
XCTAssertEqual(q.firestore, self.db);
|
|
}
|
|
|
|
- (void)testDocumentReferenceEquality {
|
|
FIRFirestore *firestore = self.db;
|
|
FIRDocumentReference *docRef = [firestore documentWithPath:@"foo/bar"];
|
|
XCTAssertEqualObjects([firestore documentWithPath:@"foo/bar"], docRef);
|
|
XCTAssertEqualObjects([docRef collectionWithPath:@"blah"].parent, docRef);
|
|
|
|
XCTAssertNotEqualObjects([firestore documentWithPath:@"foo/BAR"], docRef);
|
|
|
|
FIRFirestore *otherFirestore = [self firestore];
|
|
XCTAssertNotEqualObjects([otherFirestore documentWithPath:@"foo/bar"], docRef);
|
|
}
|
|
|
|
- (void)testQueryReferenceEquality {
|
|
FIRFirestore *firestore = self.db;
|
|
FIRQuery *query =
|
|
[[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
|
|
isEqualTo:@42];
|
|
FIRQuery *query2 =
|
|
[[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"] queryWhereField:@"baz"
|
|
isEqualTo:@42];
|
|
XCTAssertEqualObjects(query, query2);
|
|
|
|
FIRQuery *query3 =
|
|
[[[firestore collectionWithPath:@"foo"] queryOrderedByField:@"BAR"] queryWhereField:@"baz"
|
|
isEqualTo:@42];
|
|
XCTAssertNotEqualObjects(query, query3);
|
|
|
|
FIRFirestore *otherFirestore = [self firestore];
|
|
FIRQuery *query4 = [[[otherFirestore collectionWithPath:@"foo"] queryOrderedByField:@"bar"]
|
|
queryWhereField:@"baz"
|
|
isEqualTo:@42];
|
|
XCTAssertNotEqualObjects(query, query4);
|
|
}
|
|
|
|
- (void)testCanTraverseCollectionsAndDocuments {
|
|
NSString *expected = @"a/b/c/d";
|
|
// doc path from root Firestore.
|
|
XCTAssertEqualObjects([self.db documentWithPath:@"a/b/c/d"].path, expected);
|
|
// collection path from root Firestore.
|
|
XCTAssertEqualObjects([[self.db collectionWithPath:@"a/b/c"] documentWithPath:@"d"].path,
|
|
expected);
|
|
// doc path from CollectionReference.
|
|
XCTAssertEqualObjects([[self.db collectionWithPath:@"a"] documentWithPath:@"b/c/d"].path,
|
|
expected);
|
|
// collection path from DocumentReference.
|
|
XCTAssertEqualObjects([[self.db documentWithPath:@"a/b"] collectionWithPath:@"c/d/e"].path,
|
|
@"a/b/c/d/e");
|
|
}
|
|
|
|
- (void)testCanTraverseCollectionAndDocumentParents {
|
|
FIRCollectionReference *collection = [self.db collectionWithPath:@"a/b/c"];
|
|
XCTAssertEqualObjects(collection.path, @"a/b/c");
|
|
|
|
FIRDocumentReference *doc = collection.parent;
|
|
XCTAssertEqualObjects(doc.path, @"a/b");
|
|
|
|
collection = doc.parent;
|
|
XCTAssertEqualObjects(collection.path, @"a");
|
|
|
|
FIRDocumentReference *nilDoc = collection.parent;
|
|
XCTAssertNil(nilDoc);
|
|
}
|
|
|
|
- (void)testUpdateFieldsWithDots {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
|
|
[self writeDocumentRef:doc data:@{@"a.b" : @"old", @"c.d" : @"old"}];
|
|
|
|
[self updateDocumentRef:doc data:@{ [[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new" }];
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"];
|
|
|
|
[doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertEqualObjects(snapshot.data, (@{@"a.b" : @"new", @"c.d" : @"old"}));
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)testUpdateNestedFields {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
|
|
[self writeDocumentRef:doc
|
|
data:@{
|
|
@"a" : @{@"b" : @"old"},
|
|
@"c" : @{@"d" : @"old"},
|
|
@"e" : @{@"f" : @"old"}
|
|
}];
|
|
|
|
[self updateDocumentRef:doc
|
|
data:@{
|
|
@"a.b" : @"new",
|
|
[[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new"
|
|
}];
|
|
|
|
XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"];
|
|
|
|
[doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertEqualObjects(snapshot.data, (@{
|
|
@"a" : @{@"b" : @"new"},
|
|
@"c" : @{@"d" : @"new"},
|
|
@"e" : @{@"f" : @"old"}
|
|
}));
|
|
[expectation fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)testCollectionID {
|
|
XCTAssertEqualObjects([self.db collectionWithPath:@"foo"].collectionID, @"foo");
|
|
XCTAssertEqualObjects([self.db collectionWithPath:@"foo/bar/baz"].collectionID, @"baz");
|
|
}
|
|
|
|
- (void)testDocumentID {
|
|
XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar"].documentID, @"bar");
|
|
XCTAssertEqualObjects([self.db documentWithPath:@"foo/bar/baz/qux"].documentID, @"qux");
|
|
}
|
|
|
|
- (void)testCanQueueWritesWhileOffline {
|
|
XCTestExpectation *writeEpectation = [self expectationWithDescription:@"successfull write"];
|
|
XCTestExpectation *networkExpectation = [self expectationWithDescription:@"enable network"];
|
|
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
FIRFirestore *firestore = doc.firestore;
|
|
NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
|
|
|
|
[firestore disableNetworkWithCompletion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
|
|
[doc setData:data
|
|
completion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[writeEpectation fulfill];
|
|
}];
|
|
|
|
[firestore enableNetworkWithCompletion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[networkExpectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
|
|
XCTestExpectation *getExpectation = [self expectationWithDescription:@"successfull get"];
|
|
[doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
XCTAssertEqualObjects(snapshot.data, data);
|
|
XCTAssertFalse(snapshot.metadata.isFromCache);
|
|
|
|
[getExpectation fulfill];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)testCantGetDocumentsWhileOffline {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
FIRFirestore *firestore = doc.firestore;
|
|
NSDictionary<NSString *, id> *data = @{@"a" : @"b"};
|
|
|
|
XCTestExpectation *onlineExpectation = [self expectationWithDescription:@"online read"];
|
|
XCTestExpectation *networkExpectation = [self expectationWithDescription:@"network online"];
|
|
|
|
__weak FIRDocumentReference *weakDoc = doc;
|
|
|
|
[firestore disableNetworkWithCompletion:^(NSError *error) {
|
|
XCTAssertNil(error);
|
|
[doc setData:data
|
|
completion:^(NSError *_Nullable error) {
|
|
XCTAssertNil(error);
|
|
|
|
[weakDoc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
|
|
// Verify that we are not reading from cache.
|
|
XCTAssertFalse(snapshot.metadata.isFromCache);
|
|
[onlineExpectation fulfill];
|
|
}];
|
|
}];
|
|
|
|
[doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) {
|
|
XCTAssertNil(error);
|
|
|
|
// Verify that we are reading from cache.
|
|
XCTAssertTrue(snapshot.metadata.fromCache);
|
|
XCTAssertEqualObjects(snapshot.data, data);
|
|
[firestore enableNetworkWithCompletion:^(NSError *error) {
|
|
[networkExpectation fulfill];
|
|
}];
|
|
}];
|
|
}];
|
|
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
- (void)testWriteStreamReconnectsAfterIdle {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
FIRFirestore *firestore = doc.firestore;
|
|
|
|
[self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
|
|
[self waitForIdleFirestore:firestore];
|
|
[self writeDocumentRef:doc data:@{@"foo" : @"bar"}];
|
|
}
|
|
|
|
- (void)testWatchStreamReconnectsAfterIdle {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
FIRFirestore *firestore = doc.firestore;
|
|
|
|
[self readSnapshotForRef:[self documentRef] requireOnline:YES];
|
|
[self waitForIdleFirestore:firestore];
|
|
[self readSnapshotForRef:[self documentRef] requireOnline:YES];
|
|
}
|
|
|
|
- (void)testCanDisableNetwork {
|
|
FIRDocumentReference *doc = [self documentRef];
|
|
FIRFirestore *firestore = doc.firestore;
|
|
|
|
[firestore enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network"]];
|
|
[self awaitExpectations];
|
|
[firestore
|
|
enableNetworkWithCompletion:[self completionForExpectationWithName:@"Enable network again"]];
|
|
[self awaitExpectations];
|
|
[firestore
|
|
disableNetworkWithCompletion:[self completionForExpectationWithName:@"Disable network"]];
|
|
[self awaitExpectations];
|
|
[firestore
|
|
disableNetworkWithCompletion:[self
|
|
completionForExpectationWithName:@"Disable network again"]];
|
|
[self awaitExpectations];
|
|
[firestore
|
|
enableNetworkWithCompletion:[self completionForExpectationWithName:@"Final enable network"]];
|
|
[self awaitExpectations];
|
|
}
|
|
|
|
@end
|