diff --git a/Source/ARTAuth.m b/Source/ARTAuth.m index 16ab57a08..78a254f93 100644 --- a/Source/ARTAuth.m +++ b/Source/ARTAuth.m @@ -21,6 +21,8 @@ #import "ARTPushActivationState.h" #import "ARTFormEncode.h" #import "ARTInternalLog.h" +#import "ARTLocalDeviceStorage.h" +#import "ARTLocalDevice+Private.h" @implementation ARTAuth { ARTQueuedDealloc *_dealloc; @@ -824,6 +826,7 @@ - (void)setLocalDeviceClientId_nosync:(NSString *)clientId { return; } [_rest.device_nosync setClientId:clientId]; + [_rest.storage setObject:clientId forKey:ARTClientIdKey]; [_rest.push getActivationMachine:^(ARTPushActivationStateMachine *stateMachine) { if (![stateMachine.current_nosync isKindOfClass:[ARTPushActivationStateNotActivated class]]) { [stateMachine sendEvent:[[ARTPushActivationEventGotPushDeviceDetails alloc] init]]; diff --git a/Source/ARTLocalDevice.m b/Source/ARTLocalDevice.m index 873b751c3..5a73a80ec 100644 --- a/Source/ARTLocalDevice.m +++ b/Source/ARTLocalDevice.m @@ -30,6 +30,7 @@ NSString *const ARTDeviceSecretKey = @"ARTDeviceSecret"; NSString *const ARTDeviceIdentityTokenKey = @"ARTDeviceIdentityToken"; NSString *const ARTAPNSDeviceTokenKey = @"ARTAPNSDeviceToken"; +NSString *const ARTClientIdKey = @"ARTClientId"; NSString *const ARTAPNSDeviceDefaultTokenType = @"default"; NSString *const ARTAPNSDeviceLocationTokenType = @"location"; @@ -46,17 +47,24 @@ @interface ARTLocalDevice () @implementation ARTLocalDevice -- (instancetype)initWithClientId:(NSString *)clientId storage:(id)storage logger:(nullable ARTInternalLog *)logger { +- (instancetype)initWithStorage:(id)storage logger:(nullable ARTInternalLog *)logger { if (self = [super init]) { - self.clientId = clientId; self.storage = storage; _logger = logger; } return self; } -+ (ARTLocalDevice *)load:(NSString *)clientId storage:(id)storage logger:(nullable ARTInternalLog *)logger { - ARTLocalDevice *device = [[ARTLocalDevice alloc] initWithClientId:clientId storage:storage logger:logger]; +- (void)generateAndPersistPairOfDeviceIdAndSecret { + self.id = [self.class generateId]; + self.secret = [self.class generateSecret]; + + [_storage setObject:self.id forKey:ARTDeviceIdKey]; + [_storage setSecret:self.secret forDevice:self.id]; +} + ++ (instancetype)deviceWithStorage:(id)storage logger:(nullable ARTInternalLog *)logger { + ARTLocalDevice *device = [[ARTLocalDevice alloc] initWithStorage:storage logger:logger]; device.platform = ARTDevicePlatform; #if TARGET_OS_IOS switch ([[UIDevice currentDevice] userInterfaceIdiom]) { @@ -75,21 +83,25 @@ + (ARTLocalDevice *)load:(NSString *)clientId storage:(id)stor NSString *deviceId = [storage objectForKey:ARTDeviceIdKey]; NSString *deviceSecret = deviceId == nil ? nil : [storage secretForDevice:deviceId]; - if (deviceId == nil || deviceSecret == nil) { // generate both at the same time - deviceId = [self generateId]; - deviceSecret = [self generateSecret]; - - [storage setObject:deviceId forKey:ARTDeviceIdKey]; - [storage setSecret:deviceSecret forDevice:deviceId]; + if (deviceId == nil || deviceSecret == nil) { + [device generateAndPersistPairOfDeviceIdAndSecret]; // Should be removed later once spec issue #180 resolved. + } + else { + device.id = deviceId; + device.secret = deviceSecret; } - - device.id = deviceId; - device.secret = deviceSecret; id identityTokenDetailsInfo = [storage objectForKey:ARTDeviceIdentityTokenKey]; ARTDeviceIdentityTokenDetails *identityTokenDetails = [ARTDeviceIdentityTokenDetails unarchive:identityTokenDetailsInfo withLogger:logger]; device->_identityTokenDetails = identityTokenDetails; + NSString *clientId = [storage objectForKey:ARTClientIdKey]; + if (clientId == nil && identityTokenDetails.clientId != nil) { + clientId = identityTokenDetails.clientId; // Older versions of the SDK did not persist clientId, so as a fallback when loading data persisted by these versions we use the clientId of the stored identity token + [storage setObject:clientId forKey:ARTClientIdKey]; + } + device.clientId = clientId; + NSArray *supportedTokenTypes = @[ ARTAPNSDeviceDefaultTokenType, ARTAPNSDeviceLocationTokenType @@ -102,6 +114,34 @@ + (ARTLocalDevice *)load:(NSString *)clientId storage:(id)stor return device; } +- (void)setupDetailsWithClientId:(NSString *)clientId { + NSString *deviceId = self.id; + NSString *deviceSecret = self.secret; + + if (deviceId == nil || deviceSecret == nil) { + [self generateAndPersistPairOfDeviceIdAndSecret]; + } + + self.clientId = clientId; + [_storage setObject:clientId forKey:ARTClientIdKey]; +} + +- (void)resetDetails { + // Should be replaced later to resetting device's id/secret once spec issue #180 resolved. + [self generateAndPersistPairOfDeviceIdAndSecret]; + + self.clientId = nil; + [_storage setObject:nil forKey:ARTClientIdKey]; + [self setAndPersistIdentityTokenDetails:nil]; + NSArray *supportedTokenTypes = @[ + ARTAPNSDeviceDefaultTokenType, + ARTAPNSDeviceLocationTokenType + ]; + for (NSString *tokenType in supportedTokenTypes) { + [self setAndPersistAPNSDeviceToken:nil tokenType:tokenType]; + } +} + + (NSString *)generateId { return [NSUUID new].UUIDString; } @@ -146,6 +186,7 @@ - (void)setAndPersistIdentityTokenDetails:(ARTDeviceIdentityTokenDetails *)token _identityTokenDetails = tokenDetails; if (self.clientId == nil) { self.clientId = tokenDetails.clientId; + [self.storage setObject:tokenDetails.clientId forKey:ARTClientIdKey]; } } @@ -153,9 +194,4 @@ - (BOOL)isRegistered { return _identityTokenDetails != nil; } -- (void)clearIdentityTokenDetailsAndClientId { - [self setAndPersistIdentityTokenDetails:nil]; - self.clientId = nil; -} - @end diff --git a/Source/ARTPushActivationState.m b/Source/ARTPushActivationState.m index b8d28649e..5b7ed7f26 100644 --- a/Source/ARTPushActivationState.m +++ b/Source/ARTPushActivationState.m @@ -102,6 +102,8 @@ @implementation ARTPushActivationPersistentState } else if ([local apnsDeviceToken]) { [machine sendEvent:[ARTPushActivationEventGotPushDeviceDetails new]]; } + [machine.rest setupLocalDevice_nosync]; + [machine registerForAPNS]; #endif return [ARTPushActivationStateWaitingForPushDeviceDetails newWithMachine:machine logger:logger]; @@ -116,7 +118,6 @@ - (ARTPushActivationState *)transition:(ARTPushActivationEvent *)event { return self; } else if ([event isKindOfClass:[ARTPushActivationEventCalledActivate class]]) { - [self.machine registerForAPNS]; return validateAndSync(self.machine, event, self.logger); } return nil; @@ -274,8 +275,7 @@ - (ARTPushActivationState *)transition:(ARTPushActivationEvent *)event { } else if ([event isKindOfClass:[ARTPushActivationEventDeregistered class]]) { #if TARGET_OS_IOS - ARTLocalDevice *local = self.machine.rest.device_nosync; - [local clearIdentityTokenDetailsAndClientId]; + [self.machine.rest resetLocalDevice_nosync]; #endif [self.machine callDeactivatedCallback:nil]; return [ARTPushActivationStateNotActivated newWithMachine:self.machine logger:self.logger]; diff --git a/Source/ARTRest.m b/Source/ARTRest.m index 731fe3745..36df56b52 100644 --- a/Source/ARTRest.m +++ b/Source/ARTRest.m @@ -744,10 +744,9 @@ - (ARTLocalDevice *)device { } - (ARTLocalDevice *)device_nosync { - NSString *clientId = self.auth.clientId_nosync; __block ARTLocalDevice *ret; - dispatch_sync(ARTRestInternal.deviceAccessQueue, ^{ - ret = [self deviceWithClientId_onlyCallOnDeviceAccessQueue:clientId]; + dispatch_sync([ARTRestInternal deviceAccessQueue], ^{ + ret = [self sharedDevice_onlyCallOnDeviceAccessQueue]; }); return ret; } @@ -765,7 +764,7 @@ + (dispatch_queue_t)deviceAccessQueue { static BOOL sharedDeviceNeedsLoading_onlyAccessOnDeviceAccessQueue = YES; -- (ARTLocalDevice *)deviceWithClientId_onlyCallOnDeviceAccessQueue:(NSString *)clientId { +- (ARTLocalDevice *)sharedDevice_onlyCallOnDeviceAccessQueue { // The device is shared in a static variable because it's a reflection // of what's persisted. Having a device instance per ARTRest instance // could leave some instances in a stale state, if, through another @@ -776,12 +775,27 @@ - (ARTLocalDevice *)deviceWithClientId_onlyCallOnDeviceAccessQueue:(NSString *)c static id device; if (sharedDeviceNeedsLoading_onlyAccessOnDeviceAccessQueue) { - device = [ARTLocalDevice load:clientId storage:self.storage logger:self.logger]; + device = [ARTLocalDevice deviceWithStorage:self.storage logger:self.logger]; sharedDeviceNeedsLoading_onlyAccessOnDeviceAccessQueue = NO; } return device; } +- (void)setupLocalDevice_nosync { + ARTLocalDevice *device = [self device_nosync]; + NSString *clientId = self.auth.clientId_nosync; + dispatch_sync([ARTRestInternal deviceAccessQueue], ^{ + [device setupDetailsWithClientId:clientId]; + }); +} + +- (void)resetLocalDevice_nosync { + ARTLocalDevice *device = [self device_nosync]; + dispatch_sync([ARTRestInternal deviceAccessQueue], ^{ + [device resetDetails]; + }); +} + - (void)resetDeviceSingleton { dispatch_sync([ARTRestInternal deviceAccessQueue], ^{ sharedDeviceNeedsLoading_onlyAccessOnDeviceAccessQueue = YES; diff --git a/Source/PrivateHeaders/Ably/ARTLocalDevice+Private.h b/Source/PrivateHeaders/Ably/ARTLocalDevice+Private.h index 5c9518b41..de9f0925c 100644 --- a/Source/PrivateHeaders/Ably/ARTLocalDevice+Private.h +++ b/Source/PrivateHeaders/Ably/ARTLocalDevice+Private.h @@ -9,6 +9,7 @@ extern NSString *const ARTDeviceIdKey; extern NSString *const ARTDeviceSecretKey; extern NSString *const ARTDeviceIdentityTokenKey; extern NSString *const ARTAPNSDeviceTokenKey; +extern NSString *const ARTClientIdKey; extern NSString *const ARTAPNSDeviceDefaultTokenType; extern NSString *const ARTAPNSDeviceLocationTokenType; @@ -19,18 +20,19 @@ NSString* ARTAPNSDeviceTokenKeyOfType(NSString * _Nullable tokenType); @property (nonatomic) id storage; -+ (ARTLocalDevice *)load:(NSString *)clientId storage:(id)storage logger:(nullable ARTInternalLog *)logger; ++ (instancetype)deviceWithStorage:(id)storage logger:(nullable ARTInternalLog *)logger; - (nullable NSString *)apnsDeviceToken; - (void)setAndPersistAPNSDeviceToken:(nullable NSString *)deviceToken tokenType:(NSString *)tokenType; - (void)setAndPersistAPNSDeviceToken:(nullable NSString *)deviceToken; - (void)setAndPersistIdentityTokenDetails:(nullable ARTDeviceIdentityTokenDetails *)tokenDetails; - (BOOL)isRegistered; -- (void)clearIdentityTokenDetailsAndClientId; +- (void)resetDetails; +- (void)setupDetailsWithClientId:(nullable NSString *)clientId; + (NSString *)generateId; + (NSString *)generateSecret; -+ (NSString *)apnsDeviceTokenOfType:(nullable NSString *)tokenType fromStorage:(id)storage; ++ (nullable NSString *)apnsDeviceTokenOfType:(nullable NSString *)tokenType fromStorage:(id)storage; @end diff --git a/Source/PrivateHeaders/Ably/ARTRest+Private.h b/Source/PrivateHeaders/Ably/ARTRest+Private.h index 1ffbff5ab..62ad582f9 100644 --- a/Source/PrivateHeaders/Ably/ARTRest+Private.h +++ b/Source/PrivateHeaders/Ably/ARTRest+Private.h @@ -76,6 +76,9 @@ NS_ASSUME_NONNULL_BEGIN - (nullable NSObject *)internetIsUp:(void (^)(BOOL isUp))cb; #if TARGET_OS_IOS +- (void)setupLocalDevice_nosync; +- (void)resetLocalDevice_nosync; + // This is only intended to be called from test code. - (void)resetDeviceSingleton; diff --git a/Test/Tests/PushActivationStateMachineTests.swift b/Test/Tests/PushActivationStateMachineTests.swift index 6c6c2a924..b47be792c 100644 --- a/Test/Tests/PushActivationStateMachineTests.swift +++ b/Test/Tests/PushActivationStateMachineTests.swift @@ -118,14 +118,27 @@ class PushActivationStateMachineTests: XCTestCase { func test__014__Activation_state_machine__State_NotActivated__on_Event_CalledActivate__local_device__should_have_a_generated_id() { beforeEach__Activation_state_machine__State_NotActivated() - - rest.internal.resetDeviceSingleton() + + let options = ARTClientOptions(key: "xxxx:xxxx") + let rest = ARTRest(options: options) + rest.internal.storage = storage + let stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + + stateMachine.send(ARTPushActivationEventCalledActivate()) + XCTAssertEqual(rest.device.id.count, 36) } func test__015__Activation_state_machine__State_NotActivated__on_Event_CalledActivate__local_device__should_have_a_generated_secret() throws { beforeEach__Activation_state_machine__State_NotActivated() - + + let options = ARTClientOptions(key: "xxxx:xxxx") + let rest = ARTRest(options: options) + rest.internal.storage = storage + let stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + + stateMachine.send(ARTPushActivationEventCalledActivate()) + let secret = try XCTUnwrap(rest.device.secret, "Device Secret should be available in storage") let data = try XCTUnwrap(Data(base64Encoded: secret), "Device Secret should be encoded with Base64") @@ -140,6 +153,15 @@ class PushActivationStateMachineTests: XCTestCase { options.clientId = "deviceClient" let rest = ARTRest(options: options) rest.internal.storage = storage + + let stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + + XCTAssertNil(rest.device.clientId) + + stateMachine.send(ARTPushActivationEventCalledActivate()) + + XCTAssertNotNil(rest.device.id) + XCTAssertNotNil(rest.device.secret) XCTAssertEqual(rest.device.clientId, "deviceClient") } @@ -459,6 +481,7 @@ class PushActivationStateMachineTests: XCTestCase { storage = MockDeviceStorage(startWith: ARTPushActivationStateWaitingForDeviceRegistration(machine: initialStateMachine, logger: .init(core: MockInternalLogCore()))) rest.internal.storage = storage stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + rest.internal.setupLocalDevice_nosync() } // RSH3c1 @@ -529,6 +552,7 @@ class PushActivationStateMachineTests: XCTestCase { storage = MockDeviceStorage(startWith: ARTPushActivationStateWaitingForNewPushDeviceDetails(machine: initialStateMachine, logger: .init(core: MockInternalLogCore()))) rest.internal.storage = storage stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + rest.internal.setupLocalDevice_nosync() } // RSH3d1 @@ -830,6 +854,8 @@ class PushActivationStateMachineTests: XCTestCase { rest.internal.storage = storage stateMachine = ARTPushActivationStateMachine(rest: rest.internal, delegate: StateMachineDelegate(), logger: .init(core: MockInternalLogCore())) + rest.internal.setupLocalDevice_nosync() + XCTAssertEqual(stateMachine.rest.device.clientId, "client1") var deactivatedCallbackCalled = false @@ -838,19 +864,31 @@ class PushActivationStateMachineTests: XCTestCase { } defer { hook.remove() } - var clearIdentityTokenDetailsAndClientIdCalled = false - let hookDevice = stateMachine.rest.device.testSuite_injectIntoMethod(after: NSSelectorFromString("clearIdentityTokenDetailsAndClientId")) { - clearIdentityTokenDetailsAndClientIdCalled = true + var resetDetailsCalled = false + let hookDevice = stateMachine.rest.device.testSuite_injectIntoMethod(after: NSSelectorFromString("resetDetails")) { + resetDetailsCalled = true } defer { hookDevice.remove() } stateMachine.send(ARTPushActivationEventDeregistered()) expect(stateMachine.current).to(beAKindOf(ARTPushActivationStateNotActivated.self)) XCTAssertTrue(deactivatedCallbackCalled) - XCTAssertTrue(clearIdentityTokenDetailsAndClientIdCalled) + XCTAssertTrue(resetDetailsCalled) + // RSH3g2a XCTAssertNil(stateMachine.rest.device.identityTokenDetails) XCTAssertNil(stateMachine.rest.device.clientId) + XCTAssertNil(stateMachine.rest.device.push.recipient["push"]) + + XCTAssertNil(storage.object(forKey: ARTDeviceIdentityTokenKey)) + XCTAssertNil(ARTLocalDevice.apnsDeviceToken(ofType: ARTAPNSDeviceDefaultTokenType, from: storage)) + XCTAssertNil(ARTLocalDevice.apnsDeviceToken(ofType: ARTAPNSDeviceLocationTokenType, from: storage)) + + // Should be replaced with `nil` checks after issue https://github.com/ably/specification/issues/180 resolved + XCTAssertNotNil(stateMachine.rest.device.id) + XCTAssertNotNil(stateMachine.rest.device.secret) + XCTAssertEqual(storage.keysWritten[ARTDeviceIdKey] as? String, stateMachine.rest.device.id) + XCTAssertEqual(storage.keysWritten[ARTDeviceSecretKey] as? String, stateMachine.rest.device.secret) } // RSH3g3 @@ -873,7 +911,60 @@ class PushActivationStateMachineTests: XCTestCase { expect(stateMachine.current).to(beAKindOf(ARTPushActivationStateWaitingForDeregistration.self)) XCTAssertTrue(deactivatedCallbackCalled) } + + // RSH8b, RSH3a2b, RSH3g2a + func test__056__Activation_state_machine__should_be_possible_to_activate_and_deactivate_and_then_activate_again_with_different_clientId() { + beforeEach__Activation_state_machine__State_NotActivated() + + let options1 = ARTClientOptions(key: "xxxx:xxxx") + options1.clientId = "client1" + let rest1 = ARTRest(options: options1) + httpExecutor = MockHTTPExecutor() + rest1.internal.httpExecutor = httpExecutor + rest1.internal.storage = storage + + let stateMachineDelegate = StateMachineDelegate() + let stateMachine1 = ARTPushActivationStateMachine(rest: rest1.internal, delegate: stateMachineDelegate, logger: .init(core: MockInternalLogCore())) + + let testDeviceToken = "xxxx-xxxx-xxxx-xxxx-xxxx" + stateMachine1.rest.device.setAndPersistAPNSDeviceToken(testDeviceToken) + defer { stateMachine1.rest.device.setAndPersistAPNSDeviceToken(nil) } + waitUntil(timeout: testTimeout) { done in + let partialDone = AblyTests.splitDone(3, done: done) + stateMachine1.transitions = { event, _, _ in + if event is ARTPushActivationEventCalledActivate { + XCTAssertEqual(rest1.internal.device_nosync.clientId, "client1") + partialDone() + } + if event is ARTPushActivationEventGotPushDeviceDetails { + partialDone() + stateMachine1.send(ARTPushActivationEventCalledDeactivate()) + } + if event is ARTPushActivationEventCalledDeactivate { + partialDone() + } + } + stateMachine1.send(ARTPushActivationEventCalledActivate()) + } + + XCTAssertNil(rest1.device.clientId) // after deactivation, RSH3g2a + + let options2 = ARTClientOptions(key: "xxxx:xxxx") + options2.clientId = "client2" + let rest2 = ARTRest(options: options2) + rest2.internal.storage = storage + rest2.internal.httpExecutor = httpExecutor + + XCTAssertNil(rest2.device.clientId) + + let stateMachine2 = ARTPushActivationStateMachine(rest: rest2.internal, delegate: stateMachineDelegate, logger: .init(core: MockInternalLogCore())) + stateMachine2.send(ARTPushActivationEventCalledActivate()) + + XCTAssertEqual(rest2.device.clientId, "client2") + XCTAssertTrue(rest1.device === rest2.device) + } + // RSH4 func test__005__Activation_state_machine__should_queue_event_that_has_no_transition_defined_for_it() throws { // Start with WaitingForDeregistration state @@ -951,6 +1042,8 @@ class PushActivationStateMachineTests: XCTestCase { options.clientId = "deviceClient" let rest = ARTRest(options: options) rest.internal.storage = storage + rest.internal.setupLocalDevice_nosync() + XCTAssertEqual(rest.device.clientId, "deviceClient") let newOptions = ARTClientOptions(key: "xxxx:xxxx") @@ -1156,10 +1249,15 @@ class PushActivationStateMachineTests: XCTestCase { // RSH3d2b, RSH3d2c, RSH3d2d func test__should_fire_Deregistered_event_and_include_DeviceSecret_HTTP_header() throws { contextBeforeEach?() - + + rest.internal.setupLocalDevice_nosync() + let delegate = StateMachineDelegate() stateMachine.delegate = delegate - + + let deviceId = rest.device.id + let deviceSecret = rest.device.secret + waitUntil(timeout: testTimeout) { done in let partialDone = AblyTests.splitDone(2, done: done) stateMachine.transitions = { event, _, currentState in @@ -1177,7 +1275,7 @@ class PushActivationStateMachineTests: XCTestCase { expect(stateMachine.current).to(beAKindOf(ARTPushActivationStateNotActivated.self)) XCTAssertEqual(httpExecutor.requests.count, 1) - let requests = httpExecutor.requests.compactMap { $0.url?.path }.filter { $0 == "/push/deviceRegistrations/\(rest.device.id)" } + let requests = httpExecutor.requests.compactMap { $0.url?.path }.filter { $0 == "/push/deviceRegistrations/\(deviceId)" } XCTAssertEqual(requests.count, 1) let request = try XCTUnwrap(httpExecutor.requests.first, "Should have a \"/push/deviceRegistrations\" request") @@ -1187,7 +1285,7 @@ class PushActivationStateMachineTests: XCTestCase { XCTAssertEqual(request.httpMethod, "DELETE") XCTAssertNotNil(request.allHTTPHeaderFields?["Authorization"]) let deviceAuthorization = request.allHTTPHeaderFields?["X-Ably-DeviceSecret"] - XCTAssertEqual(deviceAuthorization, rest.device.secret) + XCTAssertEqual(deviceAuthorization, deviceSecret) contextAfterEach?() } @@ -1195,7 +1293,9 @@ class PushActivationStateMachineTests: XCTestCase { // RSH3d2b, RSH3d2c, RSH3d2d func test__should_fire_Deregistered_event_and_include_DeviceIdentityToken_HTTP_header() throws { contextBeforeEach?() - + + rest.internal.setupLocalDevice_nosync() + let delegate = StateMachineDelegate() stateMachine.delegate = delegate @@ -1211,7 +1311,9 @@ class PushActivationStateMachineTests: XCTestCase { rest.device.setAndPersistIdentityTokenDetails(testIdentityTokenDetails) defer { rest.device.setAndPersistIdentityTokenDetails(nil) } XCTAssertNotNil(rest.device.identityTokenDetails) - + + let deviceId = rest.device.id + waitUntil(timeout: testTimeout) { done in let partialDone = AblyTests.splitDone(2, done: done) stateMachine.transitions = { event, _, currentState in @@ -1229,7 +1331,7 @@ class PushActivationStateMachineTests: XCTestCase { expect(stateMachine.current).to(beAKindOf(ARTPushActivationStateNotActivated.self)) XCTAssertEqual(httpExecutor.requests.count, 1) - let requests = httpExecutor.requests.compactMap { $0.url?.path }.filter { $0 == "/push/deviceRegistrations/\(rest.device.id)" } + let requests = httpExecutor.requests.compactMap { $0.url?.path }.filter { $0 == "/push/deviceRegistrations/\(deviceId)" } XCTAssertEqual(requests.count, 1) let request = try XCTUnwrap(httpExecutor.requests.first, "Should have a \"/push/deviceRegistrations\" request") @@ -1248,7 +1350,9 @@ class PushActivationStateMachineTests: XCTestCase { // RSH3d2c func test__should_fire_DeregistrationFailed_event() throws { contextBeforeEach?() - + + rest.internal.setupLocalDevice_nosync() + let delegate = StateMachineDelegate() stateMachine.delegate = delegate diff --git a/Test/Tests/PushAdminTests.swift b/Test/Tests/PushAdminTests.swift index 7fc3bdba8..852b45221 100644 --- a/Test/Tests/PushAdminTests.swift +++ b/Test/Tests/PushAdminTests.swift @@ -188,6 +188,7 @@ class PushAdminTests: XCTestCase { rest.internal.httpExecutor = mockHttpExecutor storage = MockDeviceStorage() rest.internal.storage = storage + rest.internal.setupLocalDevice_nosync() localDevice = rest.device } diff --git a/Test/Tests/PushChannelTests.swift b/Test/Tests/PushChannelTests.swift index 59312ba43..6bfc04eca 100644 --- a/Test/Tests/PushChannelTests.swift +++ b/Test/Tests/PushChannelTests.swift @@ -341,6 +341,7 @@ class PushChannelTests: XCTestCase { options.testOptions.channelNamePrefix = nil let rest = ARTRest(options: options) rest.internal.storage = MockDeviceStorage() + rest.internal.setupLocalDevice_nosync() // Activate device let testIdentityTokenDetails = ARTDeviceIdentityTokenDetails(token: "xxxx-xxxx-xxx", issued: Date(), expires: Date.distantFuture, capability: "", clientId: "") diff --git a/Test/Tests/PushTests.swift b/Test/Tests/PushTests.swift index a98488ab4..8470e28be 100644 --- a/Test/Tests/PushTests.swift +++ b/Test/Tests/PushTests.swift @@ -229,16 +229,22 @@ class PushTests: XCTestCase { issued: Date(), expires: Date.distantFuture, capability: "", - clientId: "" + clientId: "client1" ) let rest = ARTRest(key: "fake:key") rest.internal.storage = storage + + storage.simulateOnNextRead(string: "testId", for: ARTDeviceIdKey) + storage.simulateOnNextRead(string: "testSecret", for: ARTDeviceSecretKey) storage.simulateOnNextRead(string: testToken, for: ARTAPNSDeviceTokenKey) storage.simulateOnNextRead(data: testIdentity.archive(withLogger: nil), for: ARTDeviceIdentityTokenKey) let device = rest.device + XCTAssertEqual(device.id, "testId") + XCTAssertEqual(device.secret, "testSecret") + XCTAssertEqual(device.clientId, "client1") XCTAssertEqual(device.apnsDeviceToken(), testToken) XCTAssertEqual(device.identityTokenDetails?.token, testIdentity.token) } @@ -255,7 +261,11 @@ class PushTests: XCTestCase { } let realtime = ARTRealtime(options: options) + let storage = MockDeviceStorage() + realtime.internal.rest.storage = storage + XCTAssertNil(realtime.device.clientId) + XCTAssertNil(storage.keysWritten[ARTClientIdKey] as? String) waitUntil(timeout: testTimeout) { done in realtime.auth.authorize { _, _ in @@ -264,6 +274,7 @@ class PushTests: XCTestCase { } XCTAssertEqual(realtime.device.clientId, "testClient") + XCTAssertEqual(storage.keysWritten[ARTClientIdKey] as? String, "testClient") } // RSH8d @@ -274,8 +285,12 @@ class PushTests: XCTestCase { options.testOptions.transportFactory = TestProxyTransportFactory() let realtime = ARTRealtime(options: options) - XCTAssertNil(realtime.device.clientId) + let storage = MockDeviceStorage() + realtime.internal.rest.storage = storage + XCTAssertNil(realtime.device.clientId) + XCTAssertNil(storage.keysWritten[ARTClientIdKey] as? String) + waitUntil(timeout: testTimeout) { done in realtime.connection.once(.connected) { _ in done() @@ -288,6 +303,7 @@ class PushTests: XCTestCase { } XCTAssertEqual(realtime.device.clientId, "testClient") + XCTAssertEqual(storage.keysWritten[ARTClientIdKey] as? String, "testClient") } // RSH8e @@ -336,7 +352,7 @@ class PushTests: XCTestCase { storage.simulateOnNextRead(string: testDeviceToken, for: ARTAPNSDeviceTokenKey) storage.simulateOnNextRead(data: testDeviceIdentity.archive(withLogger: nil), for: ARTDeviceIdentityTokenKey) - XCTAssertNil(realtime.device.clientId) + XCTAssertEqual(realtime.device.clientId, testDeviceIdentity.clientId) waitUntil(timeout: testTimeout) { done in stateMachine.transitions = { event, _, _ in @@ -377,9 +393,12 @@ class PushTests: XCTestCase { clientId: expectedClientId ) } + let storage = MockDeviceStorage() + rest.internal.storage = storage rest.push.internal.activationMachine.delegate = stateMachineDelegate XCTAssertNil(rest.device.clientId) + XCTAssertNil(storage.keysWritten[ARTClientIdKey] as? String) waitUntil(timeout: testTimeout) { done in stateMachineDelegate.onDidActivateAblyPush = { _ in @@ -392,6 +411,7 @@ class PushTests: XCTestCase { } XCTAssertEqual(rest.device.clientId, expectedClientId) + XCTAssertEqual(storage.keysWritten[ARTClientIdKey] as? String, expectedClientId) } func test__014__Registerer_Delegate_option__a_successful_activation_should_call_the_correct_registerer_delegate_method() throws {