diff options
author | Michael Lehenbauer <mikelehen@gmail.com> | 2018-04-17 15:55:29 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-04-17 15:55:29 -0700 |
commit | e36cc9610b11dfd2581ba5e3fda1917e0d5e697a (patch) | |
tree | 1a2861ce32463a225f1960c20e2f25b195d39183 /Firestore/Example/Tests/Integration | |
parent | 2f86a297446b7b2e4832bc2868fe63d54211144f (diff) |
Integration tests, changelog, and minor fixes for array transforms. (#1108)
Diffstat (limited to 'Firestore/Example/Tests/Integration')
-rw-r--r-- | Firestore/Example/Tests/Integration/API/FIRArrayTransformTests.mm | 289 | ||||
-rw-r--r-- | Firestore/Example/Tests/Integration/API/FIRValidationTests.mm | 45 |
2 files changed, 334 insertions, 0 deletions
diff --git a/Firestore/Example/Tests/Integration/API/FIRArrayTransformTests.mm b/Firestore/Example/Tests/Integration/API/FIRArrayTransformTests.mm new file mode 100644 index 0000000..5f0cf92 --- /dev/null +++ b/Firestore/Example/Tests/Integration/API/FIRArrayTransformTests.mm @@ -0,0 +1,289 @@ +/* + * 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/FSTEventAccumulator.h" +#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h" + +/** + * Note: Transforms are tested pretty thoroughly in FIRServerTimestampTests (via set, update, + * transactions, nested in documents, multiple transforms together, etc.) and so these tests + * mostly focus on the array transform semantics. + */ +@interface FIRArrayTransformTests : FSTIntegrationTestCase +@end + +@implementation FIRArrayTransformTests { + // A document reference to read and write to. + FIRDocumentReference *_docRef; + + // Accumulator used to capture events during the test. + FSTEventAccumulator *_accumulator; + + // Listener registration for a listener maintained during the course of the test. + id<FIRListenerRegistration> _listenerRegistration; +} + +- (void)setUp { + [super setUp]; + + _docRef = [self documentRef]; + _accumulator = [FSTEventAccumulator accumulatorForTest:self]; + _listenerRegistration = + [_docRef addSnapshotListenerWithIncludeMetadataChanges:YES + listener:_accumulator.valueEventHandler]; + + // Wait for initial nil snapshot to avoid potential races. + FIRDocumentSnapshot *initialSnapshot = [_accumulator awaitEventWithName:@"initial event"]; + XCTAssertFalse(initialSnapshot.exists); +} + +- (void)tearDown { + [_listenerRegistration remove]; + + [super tearDown]; +} + +#pragma mark - Test Helpers + +/** Waits for a snapshot with local writes. */ +- (FIRDocumentSnapshot *)waitForLocalEvent { + FIRDocumentSnapshot *snapshot; + do { + snapshot = [_accumulator awaitEventWithName:@"Local event."]; + } while (!snapshot.metadata.hasPendingWrites); + return snapshot; +} + +/** Waits for a snapshot that has no pending writes */ +- (FIRDocumentSnapshot *)waitForRemoteEvent { + FIRDocumentSnapshot *snapshot; + do { + snapshot = [_accumulator awaitEventWithName:@"Remote event."]; + } while (snapshot.metadata.hasPendingWrites); + return snapshot; +} + +/** Writes some initial data and consumes the events generated. */ +- (void)writeInitialData:(NSDictionary<NSString *, id> *)data { + [self writeDocumentRef:_docRef data:data]; + XCTAssertEqualObjects([self waitForLocalEvent].data, data); + XCTAssertEqualObjects([self waitForRemoteEvent].data, data); +} + +#pragma mark - Test Cases + +- (void)testCreateDocumentWithArrayUnion { + [self writeDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] + }]; + id expected = @{ @"array" : @[ @1, @2 ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testAppendToArrayViaUpdate { + [self writeInitialData:@{ @"array" : @[ @1, @3 ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @1, @4 ]] + }]; + + id expected = @{ @"array" : @[ @1, @3, @2, @4 ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testAppendToArrayViaMergeSet { + [self writeInitialData:@{ @"array" : @[ @1, @3 ] }]; + + [self mergeDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @2, @1, @4 ]] + }]; + + id expected = @{ @"array" : @[ @1, @3, @2, @4 ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testAppendObjectToArrayViaUpdate { + [self writeInitialData:@{ @"array" : @[ @{@"a" : @"hi"} ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue + fieldValueForArrayUnion:@[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ]] + }]; + + id expected = @{ @"array" : @[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testRemoveFromArrayViaUpdate { + [self writeInitialData:@{ @"array" : @[ @1, @3, @1, @3 ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @4 ]] + }]; + + id expected = @{ @"array" : @[ @3, @3 ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testRemoveFromArrayViaMergeSet { + [self writeInitialData:@{ @"array" : @[ @1, @3, @1, @3 ] }]; + + [self mergeDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @4 ]] + }]; + + id expected = @{ @"array" : @[ @3, @3 ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +- (void)testRemoveObjectFromArrayViaUpdate { + [self writeInitialData:@{ @"array" : @[ @{@"a" : @"hi"}, @{@"a" : @"bye"} ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @{@"a" : @"hi"} ]] + }]; + + id expected = @{ @"array" : @[ @{@"a" : @"bye"} ] }; + XCTAssertEqualObjects([self waitForLocalEvent].data, expected); + XCTAssertEqualObjects([self waitForRemoteEvent].data, expected); +} + +@end + +/** + * Unlike the FIRArrayTransformTests above, these tests intentionally avoid having any ongoing + * listeners so that we can test what gets stored in the offline cache based purely on the write + * acknowledgement (without receiving an updated document via watch). As such they also rely on + * persistence being enabled so documents remain in the cache after the write. + */ +@interface FIRArrayTransformServerApplicationTests : FSTIntegrationTestCase +@end + +@implementation FIRArrayTransformServerApplicationTests { + // A document reference to read and write to. + FIRDocumentReference *_docRef; +} + +- (void)setUp { + [super setUp]; + + _docRef = [self documentRef]; +} + +/** + * Helper that uses a temporary listener to read from cache (returning nil if no document seems + * to be in cache). Can probably be replaced with get(source=cache) in the future. + */ +- (FIRDocumentSnapshot *_Nullable)getFromCache { + FSTEventAccumulator *accumulator = [FSTEventAccumulator accumulatorForTest:self]; + id<FIRListenerRegistration> listenerRegistration = + [_docRef addSnapshotListener:accumulator.valueEventHandler]; + FIRDocumentSnapshot *snapshot = [accumulator awaitEventWithName:@"listenForOneEvent"]; + [listenerRegistration remove]; + if (snapshot.metadata.fromCache) { + return snapshot; + } else { + return nil; + } +} + +- (void)testServerApplicationOfSetWithNoCachedBaseDoc { + [self writeDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] + }]; + id expected = @{ @"array" : @[ @1, @2 ] }; + XCTAssertEqualObjects([self getFromCache].data, expected); +} + +- (void)testServerApplicationOfUpdateWithNoCachedBaseDoc { + // Write an initial document out-of-band so it's not in our cache + [self writeDocumentRef:[[self firestore] documentWithPath:_docRef.path] + data:@{ + @"array" : @[ @42 ] + }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] + }]; + + // Nothing should be cached since it was an update and we had no base doc. + XCTAssertNil([self getFromCache]); +} + +- (void)testServerApplicationOfMergeSetWithNoCachedBaseDoc { + // Write an initial document out-of-band so it's not in our cache + [self writeDocumentRef:[[self firestore] documentWithPath:_docRef.path] + data:@{ + @"array" : @[ @42 ] + }]; + + [self mergeDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] + }]; + + // Document will be cached but we'll be missing 42. + id expected = @{ @"array" : @[ @1, @2 ] }; + XCTAssertEqualObjects([self getFromCache].data, expected); +} + +- (void)testServerApplicationOfArrayUnionUpdateWithCachedBaseDoc { + // Cache a document with an array. + [self writeDocumentRef:_docRef data:@{ @"array" : @[ @42 ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @2 ]] + }]; + + // Should have merged the update with the cached doc. + id expected = @{ @"array" : @[ @42, @1, @2 ] }; + XCTAssertEqualObjects([self getFromCache].data, expected); +} + +- (void)testServerApplicationOfArrayRemoveUpdateWithCachedBaseDoc { + // Cache a document with an array. + [self writeDocumentRef:_docRef data:@{ @"array" : @[ @42, @1, @2 ] }]; + + [self updateDocumentRef:_docRef + data:@{ + @"array" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @2 ]] + }]; + + // Should have merged the update with the cached doc. + id expected = @{ @"array" : @[ @42 ] }; + XCTAssertEqualObjects([self getFromCache].data, expected); +} +@end diff --git a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm index 2c6a2a8..2361fd0 100644 --- a/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm +++ b/Firestore/Example/Tests/Integration/API/FIRValidationTests.mm @@ -363,6 +363,51 @@ } } +#pragma mark - ArrayUnion / ArrayRemove Validation + +- (void)testArrayTransformsInQueriesFail { + FSTAssertThrows( + [[self collectionRef] queryWhereField:@"test" + isEqualTo:@{ + @"test" : [FIRFieldValue fieldValueForArrayUnion:@[ @1 ]] + }], + @"FieldValue.arrayUnion() can only be used with updateData() and setData() (found in field " + "test)"); + + FSTAssertThrows( + [[self collectionRef] queryWhereField:@"test" + isEqualTo:@{ + @"test" : [FIRFieldValue fieldValueForArrayRemove:@[ @1 ]] + }], + @"FieldValue.arrayRemove() can only be used with updateData() and setData() (found in field " + @"test)"); +} + +- (void)testInvalidArrayTransformElementFails { + [self expectWrite:@{ + @"foo" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, self ]] + } + toFailWithReason:@"Unsupported type: FIRValidationTests"]; + + [self expectWrite:@{ + @"foo" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, self ]] + } + toFailWithReason:@"Unsupported type: FIRValidationTests"]; +} + +- (void)testArraysInArrayTransformsFail { + // This would result in a directly nested array which is not supported. + [self expectWrite:@{ + @"foo" : [FIRFieldValue fieldValueForArrayUnion:@[ @1, @[ @"nested" ] ]] + } + toFailWithReason:@"Nested arrays are not supported"]; + + [self expectWrite:@{ + @"foo" : [FIRFieldValue fieldValueForArrayRemove:@[ @1, @[ @"nested" ] ]] + } + toFailWithReason:@"Nested arrays are not supported"]; +} + #pragma mark - Query Validation - (void)testQueryWithNonPositiveLimitFails { |