diff --git a/Test/Test Utilities/TestProxyTransportFactory.swift b/Test/Test Utilities/TestProxyTransportFactory.swift index 5f454270e..d115ec6ef 100644 --- a/Test/Test Utilities/TestProxyTransportFactory.swift +++ b/Test/Test Utilities/TestProxyTransportFactory.swift @@ -7,6 +7,8 @@ class TestProxyTransportFactory: RealtimeTransportFactory { // This value will be used by all TestProxyTransportFactory instances created by this factory (including those created before this property is updated). var networkConnectEvent: ((ARTRealtimeTransport, URL) -> Void)? + var transportCreatedEvent: ((ARTRealtimeTransport) -> Void)? + func transport(withRest rest: ARTRestInternal, options: ARTClientOptions, resumeKey: String?, logger: InternalLog) -> ARTRealtimeTransport { let webSocketFactory = WebSocketFactory() @@ -21,6 +23,8 @@ class TestProxyTransportFactory: RealtimeTransportFactory { webSocketFactory.testProxyTransport = testProxyTransport + transportCreatedEvent?(testProxyTransport) + return testProxyTransport } diff --git a/Test/Test Utilities/TestUtilities.swift b/Test/Test Utilities/TestUtilities.swift index 3a0a8d5a8..1ab8f0431 100644 --- a/Test/Test Utilities/TestUtilities.swift +++ b/Test/Test Utilities/TestUtilities.swift @@ -183,12 +183,13 @@ class AblyTests { let transportFactory: TestProxyTransportFactory } - class func newRealtime(_ options: ARTClientOptions) -> RealtimeTestEnvironment { + class func newRealtime(_ options: ARTClientOptions, onTransportCreated event: ((ARTRealtimeTransport) -> Void)? = nil) -> RealtimeTestEnvironment { let modifiedOptions = options.copy() as! ARTClientOptions let autoConnect = modifiedOptions.autoConnect modifiedOptions.autoConnect = false let transportFactory = TestProxyTransportFactory() + transportFactory.transportCreatedEvent = event modifiedOptions.testOptions.transportFactory = transportFactory let realtime = ARTRealtime(options: modifiedOptions) realtime.internal.setReachabilityClass(TestReachability.self) @@ -1215,6 +1216,16 @@ class TestProxyTransport: ARTWebSocketTransport { self.replacingAcksWithNacks = nil } } + + func emulateTokenRevokationBeforeConnected() { + setBeforeIncomingMessageModifier { protocolMessage in + if protocolMessage.action == .connected { + protocolMessage.action = .disconnected + protocolMessage.error = .create(withCode: ARTErrorCode.tokenRevoked.intValue, status: 401, message: "Test token revokation") + } + return protocolMessage + } + } // MARK: ARTWebSocket diff --git a/Test/Tests/RealtimeClientConnectionTests.swift b/Test/Tests/RealtimeClientConnectionTests.swift index 27783f93b..3cb8cd410 100644 --- a/Test/Tests/RealtimeClientConnectionTests.swift +++ b/Test/Tests/RealtimeClientConnectionTests.swift @@ -3562,8 +3562,8 @@ class RealtimeClientConnectionTests: XCTestCase { } } - // RTN16l - func test__200__Connection__Connection_recovery__failures_system_response_to_unrecoverable_token_error() throws { + // RTN16l for (RTN15c5 + RTN15h1) + func test__200a__Connection__Connection_recovery__failures_system_response_to_unrecoverable_token_error() throws { let test = Test() let options = try AblyTests.commonAppSetup(for: test) let client = AblyTests.newRealtime(options).client @@ -3571,23 +3571,93 @@ class RealtimeClientConnectionTests: XCTestCase { let recoverOptions = try AblyTests.commonAppSetup(for: test) recoverOptions.recover = client.connection.createRecoveryKey() - recoverOptions.autoConnect = false + let key = recoverOptions.key // set the key to nil so that the client can't sign further token requests recoverOptions.key = nil - let tokenTtl = 3.0 - let tokenDetails = try getTestTokenDetails(for: test, key: key, ttl: tokenTtl) + let tokenDetails = try getTestTokenDetails(for: test, key: key, ttl: 30.0) recoverOptions.token = tokenDetails.token - let recoverClient = ARTRealtime(options: recoverOptions) + + let recoverClient = AblyTests.newRealtime(recoverOptions).client defer { recoverClient.dispose(); recoverClient.close() } + let transport = recoverClient.internal.transport as! TestProxyTransport + transport.emulateTokenRevokationBeforeConnected() + waitUntil(timeout: testTimeout) { done in recoverClient.connection.once(.failed) { stateChange in - expect(stateChange.previous).to(equal(ARTRealtimeConnectionState.connected)) - expect(stateChange.reason?.code).to(equal(ARTErrorCode.tokenExpired.intValue)) + XCTAssertEqual(stateChange.previous, ARTRealtimeConnectionState.connecting) + XCTAssertEqual(recoverClient.connection.errorReason?.code, ARTErrorCode.tokenRevoked.intValue) done() } - recoverClient.connect() + recoverClient.connection.once(.connecting) { stateChange in + XCTFail("Should not attempt to connect") + } + } + } + + // RTN16l for (RTN15c5 + RTN15h2 success) + func test__200b__Connection__Connection_recovery__failures_system_response_to_unrecoverable_token_error() throws { + let test = Test() + let options = try AblyTests.commonAppSetup(for: test) + let client = AblyTests.newRealtime(options).client + expect(client.internal.connection.state).toEventually(equal(.connected), timeout: testTimeout) + + let recoverOptions = try AblyTests.commonAppSetup(for: test) + recoverOptions.recover = client.connection.createRecoveryKey() + + let tokenDetails = try getTestTokenDetails(for: test, key: recoverOptions.key, ttl: 30.0) + recoverOptions.token = tokenDetails.token + + let recoverClient = AblyTests.newRealtime(recoverOptions).client + defer { recoverClient.dispose(); recoverClient.close() } + + let transport = recoverClient.internal.transport as! TestProxyTransport + transport.emulateTokenRevokationBeforeConnected() + + waitUntil(timeout: testTimeout) { done in + let partialDone = AblyTests.splitDone(2, done: done) + recoverClient.connection.once(.disconnected) { stateChange in + XCTAssertEqual(stateChange.previous, ARTRealtimeConnectionState.connecting) + partialDone() + } + recoverClient.connection.once(.connected) { stateChange in + partialDone() + } + } + } + + // RTN16l for (RTN15c5 + RTN15h2 failure) + func test__200c__Connection__Connection_recovery__failures_system_response_to_unrecoverable_token_error() throws { + let test = Test() + let options = try AblyTests.commonAppSetup(for: test) + let client = AblyTests.newRealtime(options).client + expect(client.internal.connection.state).toEventually(equal(.connected), timeout: testTimeout) + + let recoverOptions = try AblyTests.commonAppSetup(for: test) + recoverOptions.recover = client.connection.createRecoveryKey() + + let tokenDetails = try getTestTokenDetails(for: test, key: recoverOptions.key, ttl: 30.0) + recoverOptions.token = tokenDetails.token + + let recoverClient = AblyTests.newRealtime(recoverOptions, onTransportCreated: { transport in + (transport as! TestProxyTransport).emulateTokenRevokationBeforeConnected() + }).client + defer { recoverClient.dispose(); recoverClient.close() } + + waitUntil(timeout: testTimeout) { done in + let partialDone = AblyTests.splitDone(2, done: done) + recoverClient.connection.once(.disconnected) { stateChange in + partialDone() + XCTAssertNil(recoverClient.connection.errorReason) + recoverClient.connection.once(.disconnected) { stateChange in + XCTAssertEqual(recoverClient.connection.errorReason?.code, ARTErrorCode.tokenRevoked.intValue) + partialDone() + } + } + recoverClient.connection.on(.connected) { _ in + XCTFail("Should not be connected") + } } }