mirror of
https://github.com/firebase/firebase-ios-sdk
synced 2026-11-09 22:36:01 +05:45
687 lines
24 KiB
Objective-C
687 lines
24 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 "FRealtime.h"
|
|
#import "FIRDatabaseConfig_Private.h"
|
|
#import "FParsedUrl.h"
|
|
#import "FRepoManager.h"
|
|
#import "FTestHelpers.h"
|
|
#import "FTupleFirebase.h"
|
|
#import "FUtilities.h"
|
|
|
|
@implementation FRealtime
|
|
|
|
- (void)testUrlParsing {
|
|
FParsedUrl *parsed = [FUtilities parseUrl:@"http://www.example.com:9000"];
|
|
XCTAssertTrue([[parsed.path description] isEqualToString:@"/"], @"Got correct path");
|
|
XCTAssertTrue([parsed.repoInfo.host isEqualToString:@"www.example.com:9000"],
|
|
@"Got correct host");
|
|
XCTAssertTrue([parsed.repoInfo.internalHost isEqualToString:@"www.example.com:9000"],
|
|
@"Got correct host");
|
|
XCTAssertFalse(parsed.repoInfo.secure, @"Should not be secure, there's a port");
|
|
|
|
parsed = [FUtilities parseUrl:@"http://www.firebaseio.com/foo/bar"];
|
|
XCTAssertTrue([[parsed.path description] isEqualToString:@"/foo/bar"], @"Got correct path");
|
|
XCTAssertTrue([parsed.repoInfo.host isEqualToString:@"www.firebaseio.com"], @"Got correct host");
|
|
XCTAssertTrue([parsed.repoInfo.internalHost isEqualToString:@"www.firebaseio.com"],
|
|
@"Got correct host");
|
|
XCTAssertTrue(parsed.repoInfo.secure, @"Should be secure, there's no port");
|
|
}
|
|
|
|
- (void)testCachingRedirects {
|
|
NSString *host = @"host.example.com";
|
|
NSString *host2 = @"host2.example.com";
|
|
NSString *internalHost = @"internal.example.com";
|
|
NSString *internalHost2 = @"internal2.example.com";
|
|
|
|
// Set host on first repo info
|
|
FRepoInfo *repoInfo = [[FRepoInfo alloc] initWithHost:host isSecure:YES withNamespace:host];
|
|
XCTAssertTrue([repoInfo.host isEqualToString:host], @"Got correct host");
|
|
XCTAssertTrue([repoInfo.internalHost isEqualToString:host], @"Got correct host");
|
|
|
|
// Set internal host on first repo info
|
|
repoInfo.internalHost = internalHost;
|
|
XCTAssertTrue([repoInfo.host isEqualToString:host], @"Got correct host");
|
|
XCTAssertTrue([repoInfo.internalHost isEqualToString:internalHost], @"Got correct host");
|
|
|
|
// Set up a second unrelated repo info to make sure caching is keyspaced properly
|
|
FRepoInfo *repoInfo2 = [[FRepoInfo alloc] initWithHost:host2 isSecure:YES withNamespace:host2];
|
|
XCTAssertTrue([repoInfo2.host isEqualToString:host2], @"Got correct host");
|
|
XCTAssertTrue([repoInfo2.internalHost isEqualToString:host2], @"Got correct host");
|
|
|
|
repoInfo2.internalHost = internalHost2;
|
|
XCTAssertTrue([repoInfo2.internalHost isEqualToString:internalHost2], @"Got correct host");
|
|
|
|
// Setting host on this repo info should also set the right internal host
|
|
FRepoInfo *repoInfoCached = [[FRepoInfo alloc] initWithHost:host isSecure:YES withNamespace:host];
|
|
XCTAssertTrue([repoInfoCached.host isEqualToString:host], @"Got correct host");
|
|
XCTAssertTrue([repoInfoCached.internalHost isEqualToString:internalHost], @"Got correct host");
|
|
|
|
[repoInfo clearInternalHostCache];
|
|
[repoInfo2 clearInternalHostCache];
|
|
[repoInfoCached clearInternalHostCache];
|
|
|
|
XCTAssertTrue([repoInfo.internalHost isEqualToString:host], @"Got correct host");
|
|
XCTAssertTrue([repoInfo2.internalHost isEqualToString:host2], @"Got correct host");
|
|
XCTAssertTrue([repoInfoCached.internalHost isEqualToString:host], @"Got correct host");
|
|
}
|
|
|
|
- (void)testOnDisconnectSetWorks {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseConfig *readerCfg = [FTestHelpers configForName:@"reader"];
|
|
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
FIRDatabaseReference *reader =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:readerCfg] child:writer.key];
|
|
|
|
__block NSNumber *readValue = @0;
|
|
__block NSNumber *writeValue = @0;
|
|
[[reader child:@"disconnected"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
NSNumber *val = [snapshot value];
|
|
if (![val isEqual:[NSNull null]]) {
|
|
readValue = val;
|
|
}
|
|
}];
|
|
|
|
[[writer child:@"disconnected"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
id val = [snapshot value];
|
|
if (val != [NSNull null]) {
|
|
writeValue = val;
|
|
}
|
|
}];
|
|
|
|
[writer child:@"hello"];
|
|
|
|
__block BOOL ready = NO;
|
|
[[writer child:@"disconnected"]
|
|
onDisconnectSetValue:@1
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
[writer child:@"s"];
|
|
|
|
ready = NO;
|
|
[[writer child:@"disconnected"]
|
|
onDisconnectSetValue:@2
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return [@2 isEqualToNumber:readValue] && [@2 isEqualToNumber:writeValue];
|
|
}];
|
|
|
|
[FRepoManager interrupt:readerCfg];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
[FRepoManager disposeRepos:readerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectSetWithPriorityWorks {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseConfig *readerCfg = [FTestHelpers configForName:@"reader"];
|
|
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
FIRDatabaseReference *reader =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:readerCfg] child:writer.key];
|
|
|
|
__block BOOL sawNewValue = NO;
|
|
__block BOOL writerSawNewValue = NO;
|
|
[[reader child:@"disconnected"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
id val = snapshot.value;
|
|
id pri = snapshot.priority;
|
|
if (val != [NSNull null] && pri != [NSNull null]) {
|
|
sawNewValue = [(NSNumber *)val boolValue] &&
|
|
[pri isEqualToString:@"abcd"];
|
|
}
|
|
}];
|
|
|
|
[[writer child:@"disconnected"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
id val = [snapshot value];
|
|
id pri = snapshot.priority;
|
|
if (val != [NSNull null] && pri != [NSNull null]) {
|
|
writerSawNewValue = [(NSNumber *)val boolValue] &&
|
|
[pri isEqualToString:@"abcd"];
|
|
}
|
|
}];
|
|
|
|
__block BOOL ready = NO;
|
|
[[writer child:@"disconnected"]
|
|
onDisconnectSetValue:@YES
|
|
andPriority:@"abcd"
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return sawNewValue && writerSawNewValue;
|
|
}];
|
|
|
|
[FRepoManager interrupt:readerCfg];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
[FRepoManager disposeRepos:readerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectRemoveWorks {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseConfig *readerCfg = [FTestHelpers configForName:@"reader"];
|
|
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
FIRDatabaseReference *reader =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:readerCfg] child:writer.key];
|
|
|
|
__block BOOL ready = NO;
|
|
[[writer child:@"foo"] setValue:@"bar"
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
__block BOOL sawRemove = NO;
|
|
__block BOOL writerSawRemove = NO;
|
|
[[reader child:@"foo"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
sawRemove = [[NSNull null] isEqual:snapshot.value];
|
|
}];
|
|
|
|
[[writer child:@"foo"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
writerSawRemove = [[NSNull null] isEqual:snapshot.value];
|
|
}];
|
|
|
|
ready = NO;
|
|
[[writer child:@"foo"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return sawRemove && writerSawRemove;
|
|
}];
|
|
|
|
[FRepoManager interrupt:readerCfg];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
[FRepoManager disposeRepos:readerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectUpdateWorks {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseConfig *readerCfg = [FTestHelpers configForName:@"reader"];
|
|
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
FIRDatabaseReference *reader =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:readerCfg] child:writer.key];
|
|
|
|
[self waitForCompletionOf:[writer child:@"foo"] setValue:@{@"bar" : @"a", @"baz" : @"b"}];
|
|
|
|
__block BOOL sawNewValue = NO;
|
|
__block BOOL writerSawNewValue = NO;
|
|
[[reader child:@"foo"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
NSDictionary *val = [snapshot value];
|
|
if (val) {
|
|
sawNewValue = [@{@"bar" : @"a", @"baz" : @"c", @"bat" : @"d"}
|
|
isEqualToDictionary:val];
|
|
}
|
|
}];
|
|
|
|
[[writer child:@"foo"]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
NSDictionary *val = [snapshot value];
|
|
if (val) {
|
|
writerSawNewValue =
|
|
[@{@"bar" : @"a", @"baz" : @"c", @"bat" : @"d"} isEqualToDictionary:val];
|
|
}
|
|
}];
|
|
|
|
__block BOOL ready = NO;
|
|
[[writer child:@"foo"]
|
|
onDisconnectUpdateChildValues:@{@"baz" : @"c", @"bat" : @"d"}
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return sawNewValue && writerSawNewValue;
|
|
}];
|
|
|
|
[FRepoManager interrupt:readerCfg];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
[FRepoManager disposeRepos:readerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectTriggersSingleLocalValueEventForWriter {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
|
|
__block int calls = 0;
|
|
[writer observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
calls++;
|
|
if (calls == 2) {
|
|
// second call, verify the data
|
|
NSDictionary *val = [snapshot value];
|
|
NSDictionary *expected = @{@"foo" : @{@"bar" : @"a", @"bam" : @"c"}};
|
|
XCTAssertTrue([val isEqualToDictionary:expected],
|
|
@"Got all of the updates in one");
|
|
} else if (calls > 2) {
|
|
XCTFail(@"Extra calls");
|
|
}
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 1;
|
|
}];
|
|
|
|
__block BOOL done = NO;
|
|
FIRDatabaseReference *child = [writer child:@"foo"];
|
|
[child onDisconnectSetValue:@{@"bar" : @"a", @"baz" : @"b"}];
|
|
[child onDisconnectUpdateChildValues:@{@"bam" : @"c"}];
|
|
[[child child:@"baz"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 2;
|
|
}];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectTriggersSingleLocalValueEventForReader {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *reader = [FTestHelpers getRandomNode];
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] child:reader.key];
|
|
|
|
__block int calls = 0;
|
|
[reader observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
calls++;
|
|
if (calls == 2) {
|
|
// second call, verify the data
|
|
NSDictionary *val = [snapshot value];
|
|
NSDictionary *expected = @{@"foo" : @{@"bar" : @"a", @"bam" : @"c"}};
|
|
XCTAssertTrue([val isEqualToDictionary:expected],
|
|
@"Got all of the updates in one");
|
|
} else if (calls > 2) {
|
|
XCTFail(@"Extra calls");
|
|
}
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 1;
|
|
}];
|
|
|
|
__block BOOL done = NO;
|
|
FIRDatabaseReference *child = [writer child:@"foo"];
|
|
[child onDisconnectSetValue:@{@"bar" : @"a", @"baz" : @"b"}];
|
|
[child onDisconnectUpdateChildValues:@{@"bam" : @"c"}];
|
|
[[child child:@"baz"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 2;
|
|
}];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectTriggersSingleLocalValueEventForWriterWithQuery {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
|
|
__block int calls = 0;
|
|
[[[writer child:@"foo"] queryLimitedToLast:2]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
calls++;
|
|
if (calls == 2) {
|
|
// second call, verify the data
|
|
NSDictionary *val = [snapshot value];
|
|
NSDictionary *expected = @{@"bar" : @"a", @"bam" : @"c"};
|
|
XCTAssertTrue([val isEqualToDictionary:expected],
|
|
@"Got all of the updates in one");
|
|
} else if (calls > 2) {
|
|
XCTFail(@"Extra calls");
|
|
}
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 1;
|
|
}];
|
|
|
|
__block BOOL done = NO;
|
|
FIRDatabaseReference *child = [writer child:@"foo"];
|
|
[child onDisconnectSetValue:@{@"bar" : @"a", @"baz" : @"b"}];
|
|
[child onDisconnectUpdateChildValues:@{@"bam" : @"c"}];
|
|
[[child child:@"baz"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 2;
|
|
}];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectTriggersSingleLocalValueEventForReaderWithQuery {
|
|
FIRDatabaseReference *reader = [FTestHelpers getRandomNode];
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] child:reader.key];
|
|
|
|
__block int calls = 0;
|
|
[[[reader child:@"foo"] queryLimitedToLast:2]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
calls++;
|
|
XCTAssertTrue([snapshot.key isEqualToString:@"foo"], @"Got the right snapshot");
|
|
if (calls == 2) {
|
|
// second call, verify the data
|
|
NSDictionary *val = [snapshot value];
|
|
NSDictionary *expected = @{@"bar" : @"a", @"bam" : @"c"};
|
|
XCTAssertTrue([val isEqualToDictionary:expected],
|
|
@"Got all of the updates in one");
|
|
} else if (calls > 2) {
|
|
XCTFail(@"Extra calls");
|
|
}
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 1;
|
|
}];
|
|
|
|
__block BOOL done = NO;
|
|
FIRDatabaseReference *child = [writer child:@"foo"];
|
|
[child onDisconnectSetValue:@{@"bar" : @"a", @"baz" : @"b"}];
|
|
[child onDisconnectUpdateChildValues:@{@"bam" : @"c"}];
|
|
[[child child:@"baz"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return calls == 2;
|
|
}];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectDeepMergeTriggersOnlyOneValueEventForReaderWithQuery {
|
|
FIRDatabaseReference *reader = [FTestHelpers getRandomNode];
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
|
|
__block BOOL done = NO;
|
|
NSDictionary *toSet =
|
|
@{@"a" : @1, @"b" : @{@"c" : @YES, @"d" : @"scalar", @"e" : @{@"f" : @"hooray"}}};
|
|
[writer setValue:toSet];
|
|
[[writer child:@"a"] onDisconnectSetValue:@2];
|
|
[[writer child:@"b/d"]
|
|
onDisconnectRemoveValueWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
WAIT_FOR(done);
|
|
|
|
__block int count = 2;
|
|
[[reader queryLimitedToLast:3]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
count++;
|
|
if (count == 1) {
|
|
// Loaded the data, kill the writer connection
|
|
[FRepoManager interrupt:writerCfg];
|
|
} else if (count == 2) {
|
|
NSDictionary *expected =
|
|
@{@"a" : @2,
|
|
@"b" : @{@"c" : @YES, @"e" : @{@"f" : @"hooray"}}};
|
|
XCTAssertTrue([snapshot.value isEqualToDictionary:expected],
|
|
@"Should see complete new snapshot");
|
|
} else {
|
|
XCTFail(@"Too many calls");
|
|
}
|
|
}];
|
|
|
|
WAIT_FOR(count == 2);
|
|
|
|
// cleanup
|
|
[reader removeAllObservers];
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectCancelWorks {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseConfig *readerCfg = [FTestHelpers configForName:@"reader"];
|
|
|
|
FIRDatabaseReference *writer =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
FIRDatabaseReference *reader =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:readerCfg] child:writer.key];
|
|
|
|
__block BOOL ready = NO;
|
|
[[writer child:@"foo"] setValue:@{@"bar" : @"a", @"baz" : @"b"}
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
__block BOOL sawNewValue = NO;
|
|
__block BOOL writerSawNewValue = NO;
|
|
[[reader child:@"foo"] observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
NSDictionary *val = [snapshot value];
|
|
if (val) {
|
|
sawNewValue = [@{@"bar" : @"a", @"baz" : @"b", @"bat" : @"d"}
|
|
isEqualToDictionary:val];
|
|
}
|
|
}];
|
|
|
|
[[writer child:@"foo"]
|
|
observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
NSDictionary *val = [snapshot value];
|
|
if (val) {
|
|
writerSawNewValue =
|
|
[@{@"bar" : @"a", @"baz" : @"b", @"bat" : @"d"} isEqualToDictionary:val];
|
|
}
|
|
}];
|
|
|
|
ready = NO;
|
|
[[writer child:@"foo"] onDisconnectUpdateChildValues:@{@"baz" : @"c", @"bat" : @"d"}];
|
|
[[writer child:@"foo/baz"]
|
|
cancelDisconnectOperationsWithCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
ready = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return ready;
|
|
}];
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return sawNewValue && writerSawNewValue;
|
|
}];
|
|
|
|
[FRepoManager interrupt:readerCfg];
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
[FRepoManager disposeRepos:readerCfg];
|
|
}
|
|
|
|
- (void)testOnDisconnectWithServerValuesWithLocalEvents {
|
|
FIRDatabaseConfig *writerCfg = [FTestHelpers configForName:@"writer"];
|
|
FIRDatabaseReference *node =
|
|
[[[FIRDatabaseReference alloc] initWithConfig:writerCfg] childByAutoId];
|
|
|
|
__block FIRDataSnapshot *snap = nil;
|
|
[node observeEventType:FIRDataEventTypeValue
|
|
withBlock:^(FIRDataSnapshot *snapshot) {
|
|
snap = snapshot;
|
|
}];
|
|
|
|
NSDictionary *data = @{
|
|
@"a" : @1,
|
|
@"b" : @{@".value" : [FIRServerValue timestamp], @".priority" : [FIRServerValue timestamp]}
|
|
};
|
|
|
|
__block BOOL done = NO;
|
|
[node onDisconnectSetValue:data
|
|
andPriority:[FIRServerValue timestamp]
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
done = NO;
|
|
|
|
[node onDisconnectUpdateChildValues:@{
|
|
@"a" : [FIRServerValue timestamp],
|
|
@"c" : [FIRServerValue timestamp]
|
|
}
|
|
withCompletionBlock:^(NSError *error, FIRDatabaseReference *ref) {
|
|
done = YES;
|
|
}];
|
|
|
|
[self waitUntil:^BOOL {
|
|
return done;
|
|
}];
|
|
|
|
done = NO;
|
|
|
|
[FRepoManager interrupt:writerCfg];
|
|
|
|
[self waitUntil:^BOOL {
|
|
if ([snap value] != [NSNull null]) {
|
|
NSDictionary *val = [snap value];
|
|
done = (val[@"a"] && val[@"b"] && val[@"c"]);
|
|
}
|
|
return done;
|
|
}];
|
|
|
|
NSDictionary *value = [snap value];
|
|
NSNumber *now = [NSNumber numberWithDouble:round([[NSDate date] timeIntervalSince1970] * 1000)];
|
|
NSNumber *timestamp = [snap priority];
|
|
XCTAssertTrue([[snap priority] isKindOfClass:[NSNumber class]], @"Should get back number");
|
|
XCTAssertTrue([now doubleValue] - [timestamp doubleValue] < 2000,
|
|
@"Number should be no more than 2 seconds ago");
|
|
XCTAssertEqualObjects([snap priority], [value objectForKey:@"a"],
|
|
@"Should get back matching ServerValue.TIMESTAMP");
|
|
XCTAssertEqualObjects([snap priority], [value objectForKey:@"b"],
|
|
@"Should get back matching ServerValue.TIMESTAMP");
|
|
XCTAssertEqualObjects([snap priority], [[snap childSnapshotForPath:@"b"] priority],
|
|
@"Should get back matching ServerValue.TIMESTAMP");
|
|
XCTAssertEqualObjects([NSNull null], [[snap childSnapshotForPath:@"d"] value],
|
|
@"Should get null for cancelled child");
|
|
|
|
// cleanup
|
|
[FRepoManager disposeRepos:writerCfg];
|
|
}
|
|
|
|
@end
|