diff options
Diffstat (limited to 'Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm')
-rw-r--r-- | Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm b/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm new file mode 100644 index 0000000..9a2fef1 --- /dev/null +++ b/Firestore/Example/Tests/Integration/API/FIRWriteBatchTests.mm @@ -0,0 +1,336 @@ +/* + * 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/FirebaseFirestore.h> + +#import <XCTest/XCTest.h> + +#import "Firestore/Example/Tests/Util/FSTEventAccumulator.h" +#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" + +@interface FIRWriteBatchTests : FSTIntegrationTestCase +@end + +@implementation FIRWriteBatchTests + +- (void)testSupportEmptyBatches { + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + [[[self firestore] batch] commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + [self awaitExpectations]; +} + +- (void)testCommitWithoutCompletionHandler { + FIRDocumentReference *doc = [self documentRef]; + FIRWriteBatch *batch1 = [doc.firestore batch]; + [batch1 setData:@{@"aa" : @"bb"} forDocument:doc]; + [batch1 commitWithCompletion:nil]; + FIRDocumentSnapshot *snapshot1 = [self readDocumentForRef:doc]; + XCTAssertTrue(snapshot1.exists); + XCTAssertEqualObjects(snapshot1.data, @{@"aa" : @"bb"}); + + FIRWriteBatch *batch2 = [doc.firestore batch]; + [batch2 setData:@{@"cc" : @"dd"} forDocument:doc]; + [batch2 commit]; + + // TODO(b/70631617): There's currently a backend bug that prevents us from using a resume token + // right away (against hexa at least). So we sleep. :-( :-( Anything over ~10ms seems to be + // sufficient. + [NSThread sleepForTimeInterval:0.2f]; + + FIRDocumentSnapshot *snapshot2 = [self readDocumentForRef:doc]; + XCTAssertTrue(snapshot2.exists); + XCTAssertEqualObjects(snapshot2.data, @{@"cc" : @"dd"}); +} + +- (void)testSetDocuments { + FIRDocumentReference *doc = [self documentRef]; + XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch setData:@{@"a" : @"b"} forDocument:doc]; + [batch setData:@{@"c" : @"d"} forDocument:doc]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [batchExpectation fulfill]; + }]; + [self awaitExpectations]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertTrue(snapshot.exists); + XCTAssertEqualObjects(snapshot.data, @{@"c" : @"d"}); +} + +- (void)testSetDocumentWithMerge { + FIRDocumentReference *doc = [self documentRef]; + XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch setData:@{ @"a" : @"b", @"nested" : @{@"a" : @"b"} } forDocument:doc]; + [batch setData:@{ + @"c" : @"d", + @"nested" : @{@"c" : @"d"} + } + forDocument:doc + options:[FIRSetOptions merge]]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [batchExpectation fulfill]; + }]; + [self awaitExpectations]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertTrue(snapshot.exists); + XCTAssertEqualObjects( + snapshot.data, ( + @{ @"a" : @"b", + @"c" : @"d", + @"nested" : @{@"a" : @"b", @"c" : @"d"} })); +} + +- (void)testUpdateDocuments { + FIRDocumentReference *doc = [self documentRef]; + [self writeDocumentRef:doc data:@{@"foo" : @"bar"}]; + XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch updateData:@{ @"baz" : @42 } forDocument:doc]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [batchExpectation fulfill]; + }]; + [self awaitExpectations]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + XCTAssertTrue(snapshot.exists); + XCTAssertEqualObjects(snapshot.data, (@{ @"foo" : @"bar", @"baz" : @42 })); +} + +- (void)testCannotUpdateNonexistentDocuments { + FIRDocumentReference *doc = [self documentRef]; + XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch updateData:@{ @"baz" : @42 } forDocument:doc]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNotNil(error); + [batchExpectation fulfill]; + }]; + [self awaitExpectations]; + FIRDocumentSnapshot *result = [self readDocumentForRef:doc]; + XCTAssertFalse(result.exists); +} + +- (void)testDeleteDocuments { + FIRDocumentReference *doc = [self documentRef]; + [self writeDocumentRef:doc data:@{@"foo" : @"bar"}]; + FIRDocumentSnapshot *snapshot = [self readDocumentForRef:doc]; + + XCTAssertTrue(snapshot.exists); + XCTestExpectation *batchExpectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch deleteDocument:doc]; + [batch commitWithCompletion:^(NSError *error) { + XCTAssertNil(error); + [batchExpectation fulfill]; + }]; + [self awaitExpectations]; + snapshot = [self readDocumentForRef:doc]; + XCTAssertFalse(snapshot.exists); +} + +- (void)testBatchesCommitAtomicallyRaisingCorrectEvents { + FIRCollectionReference *collection = [self collectionRef]; + FIRDocumentReference *docA = [collection documentWithPath:@"a"]; + FIRDocumentReference *docB = [collection documentWithPath:@"b"]; + FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self]; + [collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options] + includeQueryMetadataChanges:YES] + listener:accumulator.valueEventHandler]; + FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"]; + XCTAssertEqual(initialSnap.count, 0); + + // Atomically write two documents. + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [collection.firestore batch]; + [batch setData:@{ @"a" : @1 } forDocument:docA]; + [batch setData:@{ @"b" : @2 } forDocument:docB]; + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + + FIRQuerySnapshot *localSnap = [accumulator awaitEventWithName:@"local event"]; + XCTAssertTrue(localSnap.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(localSnap), (@[ @{ @"a" : @1 }, @{ @"b" : @2 } ])); + + FIRQuerySnapshot *serverSnap = [accumulator awaitEventWithName:@"server event"]; + XCTAssertFalse(serverSnap.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(serverSnap), (@[ @{ @"a" : @1 }, @{ @"b" : @2 } ])); +} + +- (void)testBatchesFailAtomicallyRaisingCorrectEvents { + FIRCollectionReference *collection = [self collectionRef]; + FIRDocumentReference *docA = [collection documentWithPath:@"a"]; + FIRDocumentReference *docB = [collection documentWithPath:@"b"]; + FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self]; + [collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options] + includeQueryMetadataChanges:YES] + listener:accumulator.valueEventHandler]; + FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"]; + XCTAssertEqual(initialSnap.count, 0); + + // Atomically write 1 document and update a nonexistent document. + XCTestExpectation *expectation = [self expectationWithDescription:@"batch failed"]; + FIRWriteBatch *batch = [collection.firestore batch]; + [batch setData:@{ @"a" : @1 } forDocument:docA]; + [batch updateData:@{ @"b" : @2 } forDocument:docB]; + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNotNil(error); + XCTAssertEqualObjects(error.domain, FIRFirestoreErrorDomain); + XCTAssertEqual(error.code, FIRFirestoreErrorCodeNotFound); + [expectation fulfill]; + }]; + + // Local event with the set document. + FIRQuerySnapshot *localSnap = [accumulator awaitEventWithName:@"local event"]; + XCTAssertTrue(localSnap.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(localSnap), (@[ @{ @"a" : @1 } ])); + + // Server event with the set reverted. + FIRQuerySnapshot *serverSnap = [accumulator awaitEventWithName:@"server event"]; + XCTAssertFalse(serverSnap.metadata.hasPendingWrites); + XCTAssertEqual(serverSnap.count, 0); +} + +- (void)testWriteTheSameServerTimestampAcrossWrites { + FIRCollectionReference *collection = [self collectionRef]; + FIRDocumentReference *docA = [collection documentWithPath:@"a"]; + FIRDocumentReference *docB = [collection documentWithPath:@"b"]; + FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self]; + [collection addSnapshotListenerWithOptions:[[FIRQueryListenOptions options] + includeQueryMetadataChanges:YES] + listener:accumulator.valueEventHandler]; + FIRQuerySnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"]; + XCTAssertEqual(initialSnap.count, 0); + + // Atomically write 2 documents with server timestamps. + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [collection.firestore batch]; + [batch setData:@{@"when" : [FIRFieldValue fieldValueForServerTimestamp]} forDocument:docA]; + [batch setData:@{@"when" : [FIRFieldValue fieldValueForServerTimestamp]} forDocument:docB]; + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + + FIRQuerySnapshot *localSnap = [accumulator awaitEventWithName:@"local event"]; + XCTAssertTrue(localSnap.metadata.hasPendingWrites); + XCTAssertEqualObjects(FIRQuerySnapshotGetData(localSnap), + (@[ @{@"when" : [NSNull null]}, @{@"when" : [NSNull null]} ])); + + FIRQuerySnapshot *serverSnap = [accumulator awaitEventWithName:@"server event"]; + XCTAssertFalse(serverSnap.metadata.hasPendingWrites); + XCTAssertEqual(serverSnap.count, 2); + NSDate *when = serverSnap.documents[0][@"when"]; + XCTAssertEqualObjects(FIRQuerySnapshotGetData(serverSnap), + (@[ @{@"when" : when}, @{@"when" : when} ])); +} + +- (void)testCanWriteTheSameDocumentMultipleTimes { + FIRDocumentReference *doc = [self documentRef]; + FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self]; + [doc + addSnapshotListenerWithOptions:[[FIRDocumentListenOptions options] includeMetadataChanges:YES] + listener:accumulator.valueEventHandler]; + FIRDocumentSnapshot *initialSnap = [accumulator awaitEventWithName:@"initial event"]; + XCTAssertFalse(initialSnap.exists); + + XCTestExpectation *expectation = [self expectationWithDescription:@"batch written"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch deleteDocument:doc]; + [batch setData:@{ @"a" : @1, @"b" : @1, @"when" : @"when" } forDocument:doc]; + [batch updateData:@{ + @"b" : @2, + @"when" : [FIRFieldValue fieldValueForServerTimestamp] + } + forDocument:doc]; + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [expectation fulfill]; + }]; + + FIRDocumentSnapshot *localSnap = [accumulator awaitEventWithName:@"local event"]; + XCTAssertTrue(localSnap.metadata.hasPendingWrites); + XCTAssertEqualObjects(localSnap.data, (@{ @"a" : @1, @"b" : @2, @"when" : [NSNull null] })); + + FIRDocumentSnapshot *serverSnap = [accumulator awaitEventWithName:@"server event"]; + XCTAssertFalse(serverSnap.metadata.hasPendingWrites); + NSDate *when = serverSnap[@"when"]; + XCTAssertEqualObjects(serverSnap.data, (@{ @"a" : @1, @"b" : @2, @"when" : when })); +} + +- (void)testUpdateFieldsWithDots { + FIRDocumentReference *doc = [self documentRef]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateFieldsWithDots"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch setData:@{@"a.b" : @"old", @"c.d" : @"old"} forDocument:doc]; + [batch updateData:@{ + [[FIRFieldPath alloc] initWithFields:@[ @"a.b" ]] : @"new" + } + forDocument:doc]; + + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [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]; + + XCTestExpectation *expectation = [self expectationWithDescription:@"testUpdateNestedFields"]; + FIRWriteBatch *batch = [doc.firestore batch]; + [batch setData:@{ + @"a" : @{@"b" : @"old"}, + @"c" : @{@"d" : @"old"}, + @"e" : @{@"f" : @"old"} + } + forDocument:doc]; + [batch updateData:@{ + @"a.b" : @"new", + [[FIRFieldPath alloc] initWithFields:@[ @"c", @"d" ]] : @"new" + } + forDocument:doc]; + [batch commitWithCompletion:^(NSError *_Nullable error) { + XCTAssertNil(error); + [doc getDocumentWithCompletion:^(FIRDocumentSnapshot *snapshot, NSError *error) { + XCTAssertNil(error); + XCTAssertEqualObjects(snapshot.data, (@{ + @"a" : @{@"b" : @"new"}, + @"c" : @{@"d" : @"new"}, + @"e" : @{@"f" : @"old"} + })); + }]; + [expectation fulfill]; + }]; + + [self awaitExpectations]; +} + +@end |