From f4b723c68d0d88ca7fc00feb3727f4e808a70d7e Mon Sep 17 00:00:00 2001 From: Di Wu Date: Fri, 20 Sep 2024 10:31:26 -0700 Subject: [PATCH] fix: inject auth info to appsync handshake headers --- .../Utilities/Base64Encoder.swift | 18 ---------- .../Utilities/EndpointHelper.swift | 14 ++------ .../Websocket/AppSyncWebSocketClient.swift | 33 +++++++------------ .../Utilities/Base64EncoderTests.swift | 20 ----------- .../Utilities/EndpointHelperTests.swift | 4 +-- .../project.pbxproj | 8 ++--- .../xcshareddata/swiftpm/Package.resolved | 16 ++++----- .../IntegrationTestAppTests/APIKeyTests.swift | 33 +++++++++++++------ .../AuthTokenTests.swift | 33 ++++++++++++++----- .../IntegrationTestAppTests/IAMTests.swift | 32 +++++++++++++----- 10 files changed, 98 insertions(+), 113 deletions(-) delete mode 100644 Sources/AWSAppSyncApolloExtensions/Utilities/Base64Encoder.swift delete mode 100644 Tests/AWSAppSyncApolloExtensionsTests/Utilities/Base64EncoderTests.swift diff --git a/Sources/AWSAppSyncApolloExtensions/Utilities/Base64Encoder.swift b/Sources/AWSAppSyncApolloExtensions/Utilities/Base64Encoder.swift deleted file mode 100644 index 86bf9b6..0000000 --- a/Sources/AWSAppSyncApolloExtensions/Utilities/Base64Encoder.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - -import Foundation - -extension StringProtocol { - func base64EncodedString() throws -> String { - let utf8Encoded = data(using: .utf8) - guard let base64String = utf8Encoded?.base64EncodedString() else { - return "" - } - return base64String - } -} diff --git a/Sources/AWSAppSyncApolloExtensions/Utilities/EndpointHelper.swift b/Sources/AWSAppSyncApolloExtensions/Utilities/EndpointHelper.swift index 2e5c96e..1259499 100644 --- a/Sources/AWSAppSyncApolloExtensions/Utilities/EndpointHelper.swift +++ b/Sources/AWSAppSyncApolloExtensions/Utilities/EndpointHelper.swift @@ -24,6 +24,7 @@ func appSyncApiEndpoint(_ url: URL) -> URL { } urlComponents.host = host.replacingOccurrences(of: "appsync-realtime-api", with: "appsync-api") + urlComponents.scheme = "https" guard let apiUrl = urlComponents.url else { return url } @@ -44,20 +45,9 @@ func appSyncRealTimeEndpoint(_ url: URL) -> URL { } urlComponents.host = host.replacingOccurrences(of: "appsync-api", with: "appsync-realtime-api") + urlComponents.scheme = "wss" guard let realTimeUrl = urlComponents.url else { return url } - return realTimeUrl } - -func useWebSocketProtocolScheme(url: URL) -> URL { - guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { - return url - } - if urlComponents.scheme == "ws" || urlComponents.scheme == "wss" { - return url - } - urlComponents.scheme = urlComponents.scheme == "http" ? "ws" : "wss" - return urlComponents.url ?? url -} diff --git a/Sources/AWSAppSyncApolloExtensions/Websocket/AppSyncWebSocketClient.swift b/Sources/AWSAppSyncApolloExtensions/Websocket/AppSyncWebSocketClient.swift index fe3d827..f279349 100644 --- a/Sources/AWSAppSyncApolloExtensions/Websocket/AppSyncWebSocketClient.swift +++ b/Sources/AWSAppSyncApolloExtensions/Websocket/AppSyncWebSocketClient.swift @@ -13,6 +13,10 @@ import Foundation public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient, URLSessionDelegate { + public enum Error: Swift.Error { + case emptyEndpoint + } + private static let jsonEncoder = JSONEncoder() // MARK: - ApolloWebSocket.WebSocketClient @@ -30,7 +34,6 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient, // MARK: - Internal private let taskQueue: TaskQueue - private let endpointURL: URL /// The underlying URLSessionWebSocketTask private var connection: URLSessionWebSocketTask? { @@ -72,13 +75,11 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient, callbackQueue: DispatchQueue, authorizer: AppSyncAuthorizer ) { - self.endpointURL = useWebSocketProtocolScheme(url: appSyncRealTimeEndpoint(endpointURL)) - self.request = URLRequest(url: self.endpointURL) + self.request = URLRequest(url: appSyncRealTimeEndpoint(endpointURL)) self.delegate = delegate self.callbackQueue = callbackQueue self.authorizer = authorizer self.taskQueue = TaskQueue() - request.setValue("graphql-ws", forHTTPHeaderField: "Sec-WebSocket-Protocol") } public func connect() { @@ -160,27 +161,17 @@ public class AppSyncWebSocketClient: NSObject, ApolloWebSocket.WebSocketClient, // MARK: - Connect Internals private func createWebSocketConnection() async throws -> URLSessionWebSocketTask { - let host = appSyncApiEndpoint(endpointURL).host! - var headers = ["host": host] + guard let url = request.url else { + throw Error.emptyEndpoint + } - let authHeaders = try await authorizer.getWebsocketConnectionHeaders(endpoint: endpointURL) + request.setValue("graphql-ws", forHTTPHeaderField: "Sec-WebSocket-Protocol") + request.setValue(appSyncApiEndpoint(url).host, forHTTPHeaderField: "host") + let authHeaders = try await authorizer.getWebsocketConnectionHeaders(endpoint: url) for authHeader in authHeaders { - headers[authHeader.key] = authHeader.value + request.setValue(authHeader.value, forHTTPHeaderField: authHeader.key) } - let payload = "{}" - - let headerJsonData = try Self.jsonEncoder.encode(headers) - var urlComponents = URLComponents(url: endpointURL, resolvingAgainstBaseURL: false) - - urlComponents?.queryItems = [ - URLQueryItem(name: "header", value: headerJsonData.base64EncodedString()), - URLQueryItem(name: "payload", value: try? payload.base64EncodedString()) - ] - - let decoratedURL = urlComponents?.url ?? endpointURL - request.url = decoratedURL - AppSyncApolloLogger.debug("[AppSyncWebSocketClient] connecting to server \(decoratedURL)") let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: nil) return urlSession.webSocketTask(with: request) } diff --git a/Tests/AWSAppSyncApolloExtensionsTests/Utilities/Base64EncoderTests.swift b/Tests/AWSAppSyncApolloExtensionsTests/Utilities/Base64EncoderTests.swift deleted file mode 100644 index cd46123..0000000 --- a/Tests/AWSAppSyncApolloExtensionsTests/Utilities/Base64EncoderTests.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright Amazon.com Inc. or its affiliates. -// All Rights Reserved. -// -// SPDX-License-Identifier: Apache-2.0 -// - - -import XCTest -@testable import AWSAppSyncApolloExtensions - -class Base64EncoderTests: XCTestCase { - - func testBase64EncodedString_generateCorrectResult() throws { - XCTAssertEqual( - try "aws-appsync-apollo-extensions-swift".base64EncodedString(), - "YXdzLWFwcHN5bmMtYXBvbGxvLWV4dGVuc2lvbnMtc3dpZnQ=" - ) - } -} diff --git a/Tests/AWSAppSyncApolloExtensionsTests/Utilities/EndpointHelperTests.swift b/Tests/AWSAppSyncApolloExtensionsTests/Utilities/EndpointHelperTests.swift index fb42aa8..47f458d 100644 --- a/Tests/AWSAppSyncApolloExtensionsTests/Utilities/EndpointHelperTests.swift +++ b/Tests/AWSAppSyncApolloExtensionsTests/Utilities/EndpointHelperTests.swift @@ -14,7 +14,7 @@ final class EndpointHelperTests: XCTestCase { let appSyncEndpoint = URL(string: "https://abc.appsync-api.amazonaws.com/graphql")! XCTAssertEqual( appSyncRealTimeEndpoint(appSyncEndpoint), - URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql") + URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql") ) } @@ -22,7 +22,7 @@ final class EndpointHelperTests: XCTestCase { let appSyncEndpoint = URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql")! XCTAssertEqual( appSyncRealTimeEndpoint(appSyncEndpoint), - URL(string: "https://abc.appsync-realtime-api.amazonaws.com/graphql") + URL(string: "wss://abc.appsync-realtime-api.amazonaws.com/graphql") ) } diff --git a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj index d0b0c62..392e0e7 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj +++ b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj @@ -16,7 +16,6 @@ 218CFDC02C5A8714009D70B9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 218CFDBF2C5A8714009D70B9 /* Preview Assets.xcassets */; }; 218CFDF62C5AD23D009D70B9 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDF52C5AD23D009D70B9 /* AWSCognitoAuthPlugin */; }; 218CFDF82C5AD23D009D70B9 /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDF72C5AD23D009D70B9 /* Amplify */; }; - 218CFDFA2C5AD408009D70B9 /* amplify_outputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 218CFDF92C5AD408009D70B9 /* amplify_outputs.json */; }; 218CFDFD2C5AD4BB009D70B9 /* Authenticator in Frameworks */ = {isa = PBXBuildFile; productRef = 218CFDFC2C5AD4BB009D70B9 /* Authenticator */; }; 218CFE152C5AD66D009D70B9 /* TodoList.graphql in Resources */ = {isa = PBXBuildFile; fileRef = 218CFE112C5AD66D009D70B9 /* TodoList.graphql */; }; 218CFE162C5AD66D009D70B9 /* schema.json in Resources */ = {isa = PBXBuildFile; fileRef = 218CFE122C5AD66D009D70B9 /* schema.json */; }; @@ -30,6 +29,7 @@ 21CFD7C92C76641E0071C70F /* AWSAppSyncApolloExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 21CFD7C82C76641E0071C70F /* AWSAppSyncApolloExtensions */; }; 21FDF39C2C62B20200481EA0 /* APIKeyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FDF39B2C62B20200481EA0 /* APIKeyTests.swift */; }; 21FDF39E2C62B3E500481EA0 /* IntegrationTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21FDF39D2C62B3E500481EA0 /* IntegrationTestBase.swift */; }; + 60CE34112C9CDC1700DEAB45 /* amplify_outputs.json in Resources */ = {isa = PBXBuildFile; fileRef = 60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -50,7 +50,6 @@ 218CFDBF2C5A8714009D70B9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 218CFDC52C5A8714009D70B9 /* IntegrationTestAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = IntegrationTestAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 218CFDF32C5AD177009D70B9 /* aws-appsync-apollo-interceptors-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "aws-appsync-apollo-interceptors-swift"; path = ../..; sourceTree = ""; }; - 218CFDF92C5AD408009D70B9 /* amplify_outputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = amplify_outputs.json; sourceTree = ""; }; 218CFE112C5AD66D009D70B9 /* TodoList.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; name = TodoList.graphql; path = IntegrationTestApp/graphql/TodoList.graphql; sourceTree = SOURCE_ROOT; }; 218CFE122C5AD66D009D70B9 /* schema.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = schema.json; path = IntegrationTestApp/graphql/schema.json; sourceTree = SOURCE_ROOT; }; 218CFE132C5AD66D009D70B9 /* SubscribeTodo.graphql */ = {isa = PBXFileReference; lastKnownFileType = text; name = SubscribeTodo.graphql; path = IntegrationTestApp/graphql/SubscribeTodo.graphql; sourceTree = SOURCE_ROOT; }; @@ -61,6 +60,7 @@ 21B8F2D32C6298580042981F /* IAMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IAMTests.swift; sourceTree = ""; }; 21FDF39B2C62B20200481EA0 /* APIKeyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIKeyTests.swift; sourceTree = ""; }; 21FDF39D2C62B3E500481EA0 /* IntegrationTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTestBase.swift; sourceTree = ""; }; + 60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = amplify_outputs.json; path = "../../../../tmp/apollo-extension-integ-test/amplify_outputs.json"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -113,7 +113,7 @@ isa = PBXGroup; children = ( 21B8F2D02C5D8ACF0042981F /* graphql */, - 218CFDF92C5AD408009D70B9 /* amplify_outputs.json */, + 60CE34102C9CDC1700DEAB45 /* amplify_outputs.json */, 218CFDB82C5A8711009D70B9 /* IntegrationTestAppApp.swift */, 218CFDBA2C5A8711009D70B9 /* ContentView.swift */, 218CFE192C5AD6B1009D70B9 /* Network.swift */, @@ -259,7 +259,7 @@ buildActionMask = 2147483647; files = ( 218CFE172C5AD66D009D70B9 /* SubscribeTodo.graphql in Resources */, - 218CFDFA2C5AD408009D70B9 /* amplify_outputs.json in Resources */, + 60CE34112C9CDC1700DEAB45 /* amplify_outputs.json in Resources */, 218CFE162C5AD66D009D70B9 /* schema.json in Resources */, 218CFDC02C5A8714009D70B9 /* Preview Assets.xcassets in Resources */, 218CFDBD2C5A8714009D70B9 /* Assets.xcassets in Resources */, diff --git a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 85ff059..c768c89 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/amplify-swift.git", "state" : { - "branch" : "lawmicha.apollo", - "revision" : "22ae18b521a1d67deced0d06dcacce0bfabd2165" + "revision" : "36e5e92a5c8e1df92b69c5575de1c1f02ed1611e", + "version" : "2.41.1" } }, { @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-crt-swift", "state" : { - "revision" : "0d0a0cf2e2cb780ceeceac190b4ede94f4f96902", - "version" : "0.26.0" + "revision" : "7b42e0343f28b3451aab20840dc670abd12790bd", + "version" : "0.36.0" } }, { @@ -50,8 +50,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-sdk-swift.git", "state" : { - "revision" : "47922c05dd66be717c7bce424651a534456717b7", - "version" : "0.36.2" + "revision" : "828358a2c39d138325b0f87a2d813f4b972e5f4f", + "version" : "1.0.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/smithy-lang/smithy-swift", "state" : { - "revision" : "8a5b0105c1b8a1d26a9435fb0af3959a7f5de578", - "version" : "0.41.1" + "revision" : "0ed3440f8c41e27a0937364d5035d2d4fefb8aa3", + "version" : "0.71.0" } }, { diff --git a/Tests/IntegrationTestApp/IntegrationTestAppTests/APIKeyTests.swift b/Tests/IntegrationTestApp/IntegrationTestAppTests/APIKeyTests.swift index 23c2be1..5449809 100644 --- a/Tests/IntegrationTestApp/IntegrationTestAppTests/APIKeyTests.swift +++ b/Tests/IntegrationTestApp/IntegrationTestAppTests/APIKeyTests.swift @@ -24,18 +24,23 @@ final class APIKeyTests: IntegrationTestBase { func testAPIKeyApolloClientMutation() throws { let completed = expectation(description: "mutation completed") - - Network.shared.apolloAPIKey.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + let todoId = UUID().uuidString + Network.shared.apolloAPIKey.perform( + mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -84,18 +89,21 @@ final class APIKeyTests: IntegrationTestBase { func testSubscriptionReceivesMutation() async throws { AppSyncApolloLogger.logLevel = .verbose let receivedMutation = expectation(description: "received mutation") - + let todoId = UUID().uuidString let activeSubscription = Network.shared.apolloAPIKey.subscribe(subscription: OnCreateSubscription()) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.onCreateTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.onCreateTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - receivedMutation.fulfill() + + if newlyCreatedTodo.id == todoId { + receivedMutation.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -104,17 +112,22 @@ final class APIKeyTests: IntegrationTestBase { try await Task.sleep(nanoseconds: 5 * 1_000_000_000) // 5 seconds let completed = expectation(description: "mutation completed") - Network.shared.apolloAPIKey.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + Network.shared.apolloAPIKey.perform( + mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } diff --git a/Tests/IntegrationTestApp/IntegrationTestAppTests/AuthTokenTests.swift b/Tests/IntegrationTestApp/IntegrationTestAppTests/AuthTokenTests.swift index 3f2e8bd..986b278 100644 --- a/Tests/IntegrationTestApp/IntegrationTestAppTests/AuthTokenTests.swift +++ b/Tests/IntegrationTestApp/IntegrationTestAppTests/AuthTokenTests.swift @@ -21,17 +21,23 @@ final class AuthTokenTests: IntegrationTestBase { func testAuthTokenApolloClientMutation() async throws { try await signIn() let completed = expectation(description: "mutation completed") - Network.shared.apolloCUP.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + let todoId = UUID().uuidString + Network.shared.apolloCUP.perform( + mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -102,18 +108,21 @@ final class AuthTokenTests: IntegrationTestBase { try await signIn() let receivedMutation = expectation(description: "received mutation") - + let todoId = UUID().uuidString let activeSubscription = Network.shared.apolloCUP.subscribe(subscription: OnCreateSubscription()) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.onCreateTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.onCreateTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - receivedMutation.fulfill() + + if newlyCreatedTodo.id == todoId { + receivedMutation.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -122,17 +131,23 @@ final class AuthTokenTests: IntegrationTestBase { try await Task.sleep(nanoseconds: 5 * 1_000_000_000) // 5 seconds let completed = expectation(description: "mutation completed") - Network.shared.apolloCUP.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + Network.shared.apolloCUP.perform( + mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } + if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } diff --git a/Tests/IntegrationTestApp/IntegrationTestAppTests/IAMTests.swift b/Tests/IntegrationTestApp/IntegrationTestAppTests/IAMTests.swift index eabc251..268542f 100644 --- a/Tests/IntegrationTestApp/IntegrationTestAppTests/IAMTests.swift +++ b/Tests/IntegrationTestApp/IntegrationTestAppTests/IAMTests.swift @@ -15,17 +15,22 @@ final class IAMTests: IntegrationTestBase { func testAuthTokenApolloClientMutation() async throws { try await signIn() let completed = expectation(description: "mutation completed") - Network.shared.apolloIAM.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + let todoId = UUID().uuidString + Network.shared.apolloIAM.perform( + mutation: CreateTodoMutation(createTodoInput: .init(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -38,18 +43,21 @@ final class IAMTests: IntegrationTestBase { try await signIn() AppSyncApolloLogger.logLevel = .verbose let receivedMutation = expectation(description: "received mutation") - + let todoId = UUID().uuidString let activeSubscription = Network.shared.apolloIAM.subscribe(subscription: OnCreateSubscription()) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.onCreateTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.onCreateTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - receivedMutation.fulfill() + + if newlyCreatedTodo.id == todoId { + receivedMutation.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") } @@ -58,17 +66,23 @@ final class IAMTests: IntegrationTestBase { try await Task.sleep(nanoseconds: 5 * 1_000_000_000) // 5 seconds let completed = expectation(description: "mutation completed") - Network.shared.apolloIAM.perform(mutation: CreateTodoMutation(createTodoInput: .init())) { result in + + Network.shared.apolloIAM.perform( + mutation: CreateTodoMutation(createTodoInput: CreateTodoInput(id: .some(todoId))) + ) { result in switch result { case .success(let graphQLResult): - guard (graphQLResult.data?.createTodo) != nil else { + guard let newlyCreatedTodo = graphQLResult.data?.createTodo else { XCTFail("Missing created Todo") return } if let errors = graphQLResult.errors { XCTFail("Failed with errors \(errors)") } - completed.fulfill() + + if newlyCreatedTodo.id == todoId { + completed.fulfill() + } case .failure(let error): XCTFail("Could not create todo \(error)") }