| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- /*
- * 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 "Firestore/Source/Remote/FSTRemoteEvent.h"
- #import <XCTest/XCTest.h>
- #import "Firestore/Source/Local/FSTQueryData.h"
- #import "Firestore/Source/Model/FSTDocument.h"
- #import "Firestore/Source/Model/FSTDocumentKey.h"
- #import "Firestore/Source/Remote/FSTExistenceFilter.h"
- #import "Firestore/Source/Remote/FSTWatchChange.h"
- #import "Firestore/Example/Tests/Remote/FSTWatchChange+Testing.h"
- #import "Firestore/Example/Tests/Util/FSTHelpers.h"
- NS_ASSUME_NONNULL_BEGIN
- @interface FSTRemoteEventTests : XCTestCase
- @end
- @implementation FSTRemoteEventTests {
- NSData *_resumeToken1;
- NSMutableDictionary<NSNumber *, NSNumber *> *_noPendingResponses;
- }
- - (void)setUp {
- _resumeToken1 = [@"resume1" dataUsingEncoding:NSUTF8StringEncoding];
- _noPendingResponses = [NSMutableDictionary dictionary];
- }
- - (FSTWatchChangeAggregator *)aggregatorWithTargets:(NSArray<NSNumber *> *)targets
- outstanding:
- (NSDictionary<NSNumber *, NSNumber *> *)outstanding
- changes:(NSArray<FSTWatchChange *> *)watchChanges {
- NSMutableDictionary<NSNumber *, FSTQueryData *> *listens = [NSMutableDictionary dictionary];
- FSTQueryData *dummyQueryData = [FSTQueryData alloc];
- for (NSNumber *targetID in targets) {
- listens[targetID] = dummyQueryData;
- }
- FSTWatchChangeAggregator *aggregator =
- [[FSTWatchChangeAggregator alloc] initWithSnapshotVersion:FSTTestVersion(3)
- listenTargets:listens
- pendingTargetResponses:outstanding];
- [aggregator addWatchChanges:watchChanges];
- return aggregator;
- }
- - (void)testWillAccumulateDocumentAddedAndRemovedEvents {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @2, @3 ]
- removedTargetIDs:@[ @4, @5, @6 ]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @4 ]
- removedTargetIDs:@[ @2, @6 ]
- documentKey:doc2.key
- document:doc2];
- FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2, @3, @4, @5, @6 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 2);
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- XCTAssertEqual(event.targetChanges.count, 6);
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- FSTUpdateMapping *mapping2 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
- XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
- FSTUpdateMapping *mapping3 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
- FSTUpdateMapping *mapping4 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[ doc1 ]];
- XCTAssertEqualObjects(event.targetChanges[@4].mapping, mapping4);
- FSTUpdateMapping *mapping5 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1 ]];
- XCTAssertEqualObjects(event.targetChanges[@5].mapping, mapping5);
- FSTUpdateMapping *mapping6 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1, doc2 ]];
- XCTAssertEqualObjects(event.targetChanges[@6].mapping, mapping6);
- }
- - (void)testWillIgnoreEventsForPendingTargets {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
- targetIDs:@[ @1 ]
- cause:nil];
- FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
- targetIDs:@[ @1 ]
- cause:nil];
- FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc2.key
- document:doc2];
- // We're waiting for the unwatch and watch ack
- NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2 };
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ]
- outstanding:pendingResponses
- changes:@[ change1, change2, change3, change4 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- // doc1 is ignored because it was part of an inactive target, but doc2 is in the changes
- // because it become active.
- XCTAssertEqual(event.documentUpdates.count, 1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- XCTAssertEqual(event.targetChanges.count, 1);
- }
- - (void)testWillIgnoreEventsForRemovedTargets {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
- targetIDs:@[ @1 ]
- cause:nil];
- // We're waiting for the unwatch ack
- NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @1 };
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[] outstanding:pendingResponses changes:@[ change1, change2 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- // doc1 is ignored because it was part of an inactive target
- XCTAssertEqual(event.documentUpdates.count, 0);
- // Target 1 is ignored because it was removed
- XCTAssertEqual(event.targetChanges.count, 0);
- }
- - (void)testWillKeepResetMappingEvenWithUpdates {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTDocument *doc3 = FSTTestDoc(@"docs/3", 3, @{ @"value" : @3 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc1.key
- document:doc1];
- // Reset stream, ignoring doc1
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
- targetIDs:@[ @1 ]
- cause:nil];
- // Add doc2, doc3
- FSTWatchChange *change3 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc2.key
- document:doc2];
- FSTWatchChange *change4 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc3.key
- document:doc3];
- // Remove doc2 again, should not show up in reset mapping
- FSTWatchChange *change5 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[]
- removedTargetIDs:@[ @1 ]
- documentKey:doc2.key
- document:doc2];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2, change3, change4, change5 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 3);
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- XCTAssertEqualObjects(event.documentUpdates[doc3.key], doc3);
- XCTAssertEqual(event.targetChanges.count, 1);
- // Only doc3 is part of the new mapping
- FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[ doc3 ]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
- }
- - (void)testWillHandleSingleReset {
- // Reset target
- FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
- targetIDs:@[ @1 ]
- cause:nil];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 0);
- XCTAssertEqual(event.targetChanges.count, 1);
- // Reset mapping is empty
- FSTResetMapping *expectedMapping = [FSTResetMapping mappingWithDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, expectedMapping);
- }
- - (void)testWillHandleTargetAddAndRemovalInSameBatch {
- FSTDocument *doc1a = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc1b = FSTTestDoc(@"docs/1", 1, @{ @"value" : @2 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[ @2 ]
- documentKey:doc1a.key
- document:doc1a];
- FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @2 ]
- removedTargetIDs:@[ @1 ]
- documentKey:doc1b.key
- document:doc1b];
- FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 1);
- XCTAssertEqualObjects(event.documentUpdates[doc1b.key], doc1b);
- XCTAssertEqual(event.targetChanges.count, 2);
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[ doc1b ]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- FSTUpdateMapping *mapping2 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1b ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping2);
- }
- - (void)testTargetCurrentChangeWillMarkTheTargetCurrent {
- FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @1 ]
- resumeToken:_resumeToken1];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 0);
- XCTAssertEqual(event.targetChanges.count, 1);
- FSTTargetChange *targetChange = event.targetChanges[@1];
- XCTAssertEqualObjects(targetChange.mapping, [[FSTUpdateMapping alloc] init]);
- XCTAssertEqual(targetChange.currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(targetChange.resumeToken, _resumeToken1);
- }
- - (void)testTargetAddedChangeWillResetPreviousState {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1, @3 ]
- removedTargetIDs:@[ @2 ]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @1, @2, @3 ]
- resumeToken:_resumeToken1];
- FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
- targetIDs:@[ @1 ]
- cause:nil];
- FSTWatchChange *change4 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateRemoved
- targetIDs:@[ @2 ]
- cause:nil];
- FSTWatchChange *change5 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateAdded
- targetIDs:@[ @1 ]
- cause:nil];
- FSTWatchChange *change6 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[ @3 ]
- documentKey:doc2.key
- document:doc2];
- NSDictionary<NSNumber *, NSNumber *> *pendingResponses = @{ @1 : @2, @2 : @1 };
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1, @3 ]
- outstanding:pendingResponses
- changes:@[ change1, change2, change3, change4, change5, change6 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 2);
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- // target 1 and 3 are affected (1 because of re-add), target 2 is not because of remove
- XCTAssertEqual(event.targetChanges.count, 2);
- // doc1 was before the remove, so it does not show up in the mapping
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc2 ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- // Current was before the remove
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
- // Doc1 was before the remove
- FSTUpdateMapping *mapping3 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1 ] removedDocuments:@[ doc2 ]];
- XCTAssertEqualObjects(event.targetChanges[@3].mapping, mapping3);
- // Current was before the remove
- XCTAssertEqual(event.targetChanges[@3].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(event.targetChanges[@3].resumeToken, _resumeToken1);
- }
- - (void)testNoChangeWillStillMarkTheAffectedTargets {
- FSTWatchChange *change = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateNoChange
- targetIDs:@[ @1 ]
- resumeToken:_resumeToken1];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ] outstanding:_noPendingResponses changes:@[ change ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 0);
- XCTAssertEqual(event.targetChanges.count, 1);
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTUpdateMapping alloc] init]);
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateNone);
- XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
- }
- - (void)testExistenceFiltersWillReplacePreviousExistenceFilters {
- FSTExistenceFilter *filter1 = [FSTExistenceFilter filterWithCount:1];
- FSTExistenceFilter *filter2 = [FSTExistenceFilter filterWithCount:2];
- FSTWatchChange *change1 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:1];
- FSTWatchChange *change2 = [FSTExistenceFilterWatchChange changeWithFilter:filter1 targetID:2];
- // replace filter1 for target 2
- FSTWatchChange *change3 = [FSTExistenceFilterWatchChange changeWithFilter:filter2 targetID:2];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1, @2 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2, change3 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 0);
- XCTAssertEqual(event.targetChanges.count, 0);
- XCTAssertEqual(aggregator.existenceFilters.count, 2);
- XCTAssertEqual(aggregator.existenceFilters[@1], filter1);
- XCTAssertEqual(aggregator.existenceFilters[@2], filter2);
- }
- - (void)testExistenceFilterMismatchResetsTarget {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc2.key
- document:doc2];
- FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @1 ]
- resumeToken:_resumeToken1];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2, change3 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 2);
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- XCTAssertEqual(event.targetChanges.count, 1);
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
- [event handleExistenceFilterMismatchForTargetID:@1];
- // Mapping is reset
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, [[FSTResetMapping alloc] init]);
- // Reset the resume snapshot
- XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(0));
- // Target needs to be set to not current
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkNotCurrent);
- XCTAssertEqual(event.targetChanges[@1].resumeToken.length, 0);
- }
- - (void)testDocumentUpdate {
- FSTDocument *doc1 = FSTTestDoc(@"docs/1", 1, @{ @"value" : @1 }, NO);
- FSTDeletedDocument *deletedDoc1 =
- [FSTDeletedDocument documentWithKey:doc1.key version:FSTTestVersion(3)];
- FSTDocument *doc2 = FSTTestDoc(@"docs/2", 2, @{ @"value" : @2 }, NO);
- FSTDocument *doc3 = FSTTestDoc(@"docs/3", 3, @{ @"value" : @3 }, NO);
- FSTWatchChange *change1 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc1.key
- document:doc1];
- FSTWatchChange *change2 = [[FSTDocumentWatchChange alloc] initWithUpdatedTargetIDs:@[ @1 ]
- removedTargetIDs:@[]
- documentKey:doc2.key
- document:doc2];
- FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 2);
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], doc1);
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- // Update doc1
- [event addDocumentUpdate:deletedDoc1];
- [event addDocumentUpdate:doc3];
- XCTAssertEqualObjects(event.snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.documentUpdates.count, 3);
- // doc1 is replaced
- XCTAssertEqualObjects(event.documentUpdates[doc1.key], deletedDoc1);
- // doc2 is untouched
- XCTAssertEqualObjects(event.documentUpdates[doc2.key], doc2);
- // doc3 is new
- XCTAssertEqualObjects(event.documentUpdates[doc3.key], doc3);
- // Target is unchanged
- XCTAssertEqual(event.targetChanges.count, 1);
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[ doc1, doc2 ] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- }
- - (void)testResumeTokensHandledPerTarget {
- NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
- FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @1 ]
- resumeToken:_resumeToken1];
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @2 ]
- resumeToken:resumeToken2];
- FSTWatchChangeAggregator *aggregator = [self aggregatorWithTargets:@[ @1, @2 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqual(event.targetChanges.count, 2);
- FSTUpdateMapping *mapping1 =
- [FSTUpdateMapping mappingWithAddedDocuments:@[] removedDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, _resumeToken1);
- XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
- XCTAssertEqualObjects(event.targetChanges[@2].snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken2);
- }
- - (void)testLastResumeTokenWins {
- NSData *resumeToken2 = [@"resume2" dataUsingEncoding:NSUTF8StringEncoding];
- NSData *resumeToken3 = [@"resume3" dataUsingEncoding:NSUTF8StringEncoding];
- FSTWatchChange *change1 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateCurrent
- targetIDs:@[ @1 ]
- resumeToken:_resumeToken1];
- FSTWatchChange *change2 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
- targetIDs:@[ @1 ]
- resumeToken:resumeToken2];
- FSTWatchChange *change3 = [FSTWatchTargetChange changeWithState:FSTWatchTargetChangeStateReset
- targetIDs:@[ @2 ]
- resumeToken:resumeToken3];
- FSTWatchChangeAggregator *aggregator =
- [self aggregatorWithTargets:@[ @1, @2 ]
- outstanding:_noPendingResponses
- changes:@[ change1, change2, change3 ]];
- FSTRemoteEvent *event = [aggregator remoteEvent];
- XCTAssertEqual(event.targetChanges.count, 2);
- FSTResetMapping *mapping1 = [FSTResetMapping mappingWithDocuments:@[]];
- XCTAssertEqualObjects(event.targetChanges[@1].mapping, mapping1);
- XCTAssertEqualObjects(event.targetChanges[@1].snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.targetChanges[@1].currentStatusUpdate, FSTCurrentStatusUpdateMarkCurrent);
- XCTAssertEqualObjects(event.targetChanges[@1].resumeToken, resumeToken2);
- XCTAssertEqualObjects(event.targetChanges[@2].mapping, mapping1);
- XCTAssertEqualObjects(event.targetChanges[@2].snapshotVersion, FSTTestVersion(3));
- XCTAssertEqual(event.targetChanges[@2].currentStatusUpdate, FSTCurrentStatusUpdateNone);
- XCTAssertEqualObjects(event.targetChanges[@2].resumeToken, resumeToken3);
- }
- @end
- NS_ASSUME_NONNULL_END
|