diff --git a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLSubscriptionTaskRunner.swift b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLSubscriptionTaskRunner.swift index 382baa0775..09dfa9d3c7 100644 --- a/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLSubscriptionTaskRunner.swift +++ b/AmplifyPlugins/API/Sources/AWSAPIPlugin/Operation/AWSGraphQLSubscriptionTaskRunner.swift @@ -178,7 +178,6 @@ public class AWSGraphQLSubscriptionTaskRunner: InternalTaskRunner, } } - internal static func decodeAppSyncRealTimeResponseError(_ data: JSONValue?) -> [Error] { let knownAppSyncRealTimeRequestErorrs = decodeAppSyncRealTimeRequestError(data) diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerCancelTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerTests.swift similarity index 66% rename from AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerCancelTests.swift rename to AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerTests.swift index a06001515f..f96b8fe5bd 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerCancelTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSGraphQLSubscriptionTaskRunnerTests.swift @@ -14,7 +14,7 @@ import XCTest @testable import AWSPluginsTestCommon // swiftlint:disable:next type_name -class AWSGraphQLSubscriptionTaskRunnerCancelTests: XCTestCase { +class AWSGraphQLSubscriptionTaskRunnerTests: XCTestCase { var apiPlugin: AWSAPIPlugin! var authService: MockAWSAuthService! var pluginConfig: AWSAPICategoryPluginConfiguration! @@ -183,4 +183,102 @@ class AWSGraphQLSubscriptionTaskRunnerCancelTests: XCTestCase { subscriptionEvents.cancel() await fulfillment(of: [receivedFailure, receivedCompletion], timeout: 5) } + + func testDecodeAppSyncRealTimeResponseError_withEmptyJsonValue_failedToDecode() { + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeResponseError(nil) + XCTAssertEqual(errors.count, 1) + if case .some(.operationError(let description, _, _)) = errors.first as? APIError { + XCTAssertTrue(description.contains("Failed to decode AppSync error response")) + } else { + XCTFail("Should be failed with APIError") + } + } + + func testDecodeAppSYncRealTimeResponseError_withKnownAppSyncRealTimeRequestError_returnKnownErrors() { + let errorJson: JSONValue = [ + "errors": [[ + "message": "test1", + "errorType": "MaxSubscriptionsReachedError" + ]] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeResponseError(errorJson) + XCTAssertEqual(errors.count, 1) + guard case .maxSubscriptionsReached = errors.first as? AppSyncRealTimeRequest.Error else { + XCTFail("Should be AppSyncRealTimeRequestError") + return + } + } + + func testDecodeAppSYncRealTimeResponseError_withUnknownError_returnParsedGraphQLError() { + let errorJson: JSONValue = [ + "errors": [[ + "message": "test1", + "errorType": "Unknown" + ]] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeResponseError(errorJson) + XCTAssertEqual(errors.count, 1) + XCTAssertTrue(errors.first is GraphQLError) + } + + func testDecodeAppSyncRealTimeRequestError_withoutErrorsField_returnEmptyErrors() { + let errorJson: JSONValue = [ + "noErrors": [[ + "message": "test1", + "errorType": "Unknown" + ]] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeRequestError(errorJson) + XCTAssertEqual(errors.count, 0) + } + + func testDecodeAppSyncRealTimeRequestError_withWellFormatErrors_parseErrors() { + let errorJson: JSONValue = [ + "errors": [[ + "message": "test1", + "errorType": "MaxSubscriptionsReachedError" + ], [ + "message": "test2", + "errorType": "LimitExceededError" + ], [ + "message": "test3", + "errorType": "Unauthorized" + ]] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeRequestError(errorJson) + XCTAssertEqual(errors.count, 3) + } + + func testDecodeAppSyncRealTimeRequestError_withSomeWellFormatErrors_parseErrors() { + let errorJson: JSONValue = [ + "errors": [[ + "message": "test1", + "errorType": "MaxSubscriptionsReachedError" + ], [ + "message": "test2", + "errorType": "LimitExceededError" + ], [ + "random": "123" + ]] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeRequestError(errorJson) + XCTAssertEqual(errors.count, 2) + } + + func testDecodeAppSyncRealTimeRequestError_withSingletonErrors_parseErrors() { + let errorJson: JSONValue = [ + "errors": [ + "message": "test1", + "errorType": "MaxSubscriptionsReachedError" + ] + ] + + let errors = AWSGraphQLSubscriptionTaskRunner.decodeAppSyncRealTimeRequestError(errorJson) + XCTAssertEqual(errors.count, 1) + } } diff --git a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/LocalWebSocketServer.swift b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/LocalWebSocketServer.swift index 1dc0fbd948..0b8c47f16a 100644 --- a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/LocalWebSocketServer.swift +++ b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/LocalWebSocketServer.swift @@ -37,7 +37,7 @@ class LocalWebSocketServer { stack.applicationProtocols.insert(ws, at: 0) let port = NWEndpoint.Port(rawValue: portNumber)! guard let listener = try? NWListener(using: params, on: port) else { - throw "unable to start the listener at: localhost:\(port)" + throw LocalWebSocketServerError.error("unable to start the listener at: localhost:\(port)") } listener.newConnectionHandler = { [weak self] conn in @@ -93,7 +93,7 @@ class LocalWebSocketServer { func sendTransientFailureToConnections() { self.connections.forEach { - var metadata = NWProtocolWebSocket.Metadata(opcode: .close) + let metadata = NWProtocolWebSocket.Metadata(opcode: .close) metadata.closeCode = .protocolCode(NWProtocolWebSocket.CloseCode.Defined.internalServerError) $0.send( content: nil, @@ -103,3 +103,7 @@ class LocalWebSocketServer { } } } + +enum LocalWebSocketServerError: Error { + case error(String) +} diff --git a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/RetryWithJitterTests.swift b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/RetryWithJitterTests.swift index 15a6aed34e..b7a023be44 100644 --- a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/RetryWithJitterTests.swift +++ b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/RetryWithJitterTests.swift @@ -8,6 +8,7 @@ import XCTest @testable import AWSPluginsCore +@testable @_spi(RetryWithJitter) import AmplifyNetwork class RetryWithJitterTests: XCTestCase { struct TestError: Error { diff --git a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/WebSocketClientTests.swift b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/WebSocketClientTests.swift index a9dc80a155..be05e55443 100644 --- a/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/WebSocketClientTests.swift +++ b/AmplifyPlugins/Internal/Tests/NetworkTests/WebSocket/WebSocketClientTests.swift @@ -9,6 +9,7 @@ import XCTest import Combine @testable import AWSPluginsCore +@testable @_spi(WebSocket) @_spi(NetworkReachability) import AmplifyNetwork fileprivate let timeout: TimeInterval = 5 diff --git a/Package.swift b/Package.swift index 1b9240c19a..38de745653 100644 --- a/Package.swift +++ b/Package.swift @@ -327,6 +327,7 @@ let internalNetworkingTargets: [Target] = [ .testTarget( name: "AmplifyNetworkUnitTests", dependencies: [ + "AmplifyTestCommon", "AmplifyNetwork" ], path: "AmplifyPlugins/Internal/Tests/NetworkTests"