280 lines
12 KiB
Swift
280 lines
12 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import Combine
|
|
import XCTest
|
|
|
|
import Amplify
|
|
@testable import AmplifyTestCommon
|
|
@testable import AWSAPIPlugin
|
|
@_implementationOnly import AmplifyAsyncTesting
|
|
import AppSyncRealTimeClient
|
|
|
|
class GraphQLSubscribeCombineTests: OperationTestBase {
|
|
|
|
var sink: AnyCancellable?
|
|
|
|
// Setup expectations
|
|
var onSubscribeInvoked: AsyncExpectation!
|
|
var receivedCompletionSuccess: AsyncExpectation!
|
|
var receivedCompletionFailure: AsyncExpectation!
|
|
|
|
// Subscription state expectations
|
|
var receivedStateValueConnecting: AsyncExpectation!
|
|
var receivedStateValueConnected: AsyncExpectation!
|
|
var receivedStateValueDisconnected: AsyncExpectation!
|
|
|
|
// Subscription item expectations
|
|
var receivedDataValueSuccess: AsyncExpectation!
|
|
var receivedDataValueError: AsyncExpectation!
|
|
|
|
// Handles to the subscription item and event handler used to make mock calls into the
|
|
// subscription system
|
|
var subscriptionItem: SubscriptionItem!
|
|
var subscriptionEventHandler: SubscriptionEventHandler!
|
|
|
|
var connectionStateSink: AnyCancellable?
|
|
var subscriptionDataSink: AnyCancellable?
|
|
|
|
override func setUp() async throws {
|
|
try await super.setUp()
|
|
|
|
onSubscribeInvoked = asyncExpectation(description: "onSubscribeInvoked")
|
|
|
|
receivedCompletionSuccess = asyncExpectation(description: "receivedStateCompletionSuccess")
|
|
receivedCompletionFailure = asyncExpectation(description: "receivedStateCompletionFailure")
|
|
receivedStateValueConnecting = asyncExpectation(description: "receivedStateValueConnecting")
|
|
receivedStateValueConnected = asyncExpectation(description: "receivedStateValueConnected")
|
|
receivedStateValueDisconnected = asyncExpectation(description: "receivedStateValueDisconnected")
|
|
|
|
receivedDataValueSuccess = asyncExpectation(description: "receivedDataValueSuccess")
|
|
receivedDataValueError = asyncExpectation(description: "receivedDataValueError")
|
|
|
|
try setUpMocksAndSubscriptionItems()
|
|
}
|
|
|
|
func waitForSubscriptionExpectations() async {
|
|
await waitForExpectations([receivedCompletionSuccess,
|
|
receivedCompletionFailure,
|
|
receivedStateValueConnecting,
|
|
receivedStateValueConnected,
|
|
receivedStateValueDisconnected,
|
|
receivedDataValueSuccess,
|
|
receivedDataValueError], timeout: 0.05)
|
|
}
|
|
|
|
func testHappyPath() async throws {
|
|
await receivedCompletionSuccess.setShouldTrigger(true)
|
|
await receivedCompletionFailure.setShouldTrigger(false)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(true)
|
|
await receivedStateValueDisconnected.setShouldTrigger(true)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(true)
|
|
await receivedDataValueError.setShouldTrigger(false)
|
|
|
|
let testJSON: JSONValue = ["foo": true]
|
|
let testData = #"{"data": {"foo": true}}"# .data(using: .utf8)!
|
|
|
|
try await subscribe(expecting: testJSON)
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.connected), subscriptionItem)
|
|
subscriptionEventHandler(.data(testData), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.disconnected), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
func testConnectionWithNoData() async throws {
|
|
await receivedCompletionSuccess.setShouldTrigger(true)
|
|
await receivedCompletionFailure.setShouldTrigger(false)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(true)
|
|
await receivedStateValueDisconnected.setShouldTrigger(true)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(false)
|
|
await receivedDataValueError.setShouldTrigger(false)
|
|
|
|
try await subscribe()
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.connected), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.disconnected), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
func testConnectionError() async throws {
|
|
await receivedCompletionSuccess.setShouldTrigger(false)
|
|
await receivedCompletionFailure.setShouldTrigger(true)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(false)
|
|
await receivedStateValueDisconnected.setShouldTrigger(false)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(false)
|
|
await receivedDataValueError.setShouldTrigger(false)
|
|
|
|
try await subscribe()
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.failed("Error"), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
func testDecodingError() async throws {
|
|
let testData = #"{"data": {"foo": true}, "errors": []}"# .data(using: .utf8)!
|
|
await receivedCompletionSuccess.setShouldTrigger(true)
|
|
await receivedCompletionFailure.setShouldTrigger(false)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(true)
|
|
await receivedStateValueDisconnected.setShouldTrigger(true)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(false)
|
|
await receivedDataValueError.setShouldTrigger(true)
|
|
|
|
try await subscribe()
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.connected), subscriptionItem)
|
|
subscriptionEventHandler(.data(testData), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.disconnected), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
func testMultipleSuccessValues() async throws {
|
|
let testJSON: JSONValue = ["foo": true]
|
|
let testData = #"{"data": {"foo": true}}"# .data(using: .utf8)!
|
|
await receivedCompletionSuccess.setShouldTrigger(true)
|
|
await receivedCompletionFailure.setShouldTrigger(false)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(true)
|
|
await receivedStateValueDisconnected.setShouldTrigger(true)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(true)
|
|
await receivedDataValueSuccess.setExpectedFulfillmentCount(2)
|
|
await receivedDataValueError.setShouldTrigger(false)
|
|
|
|
try await subscribe(expecting: testJSON)
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.connected), subscriptionItem)
|
|
subscriptionEventHandler(.data(testData), subscriptionItem)
|
|
subscriptionEventHandler(.data(testData), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.disconnected), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
func testMixedSuccessAndErrorValues() async throws {
|
|
let successfulTestData = #"{"data": {"foo": true}}"# .data(using: .utf8)!
|
|
let invalidTestData = #"{"data": {"foo": true}, "errors": []}"# .data(using: .utf8)!
|
|
await receivedCompletionSuccess.setShouldTrigger(true)
|
|
await receivedCompletionFailure.setShouldTrigger(false)
|
|
await receivedStateValueConnecting.setShouldTrigger(true)
|
|
await receivedStateValueConnected.setShouldTrigger(true)
|
|
await receivedStateValueDisconnected.setShouldTrigger(true)
|
|
|
|
await receivedDataValueSuccess.setShouldTrigger(true)
|
|
await receivedDataValueSuccess.setExpectedFulfillmentCount(2)
|
|
await receivedDataValueError.setShouldTrigger(true)
|
|
|
|
try await subscribe()
|
|
await waitForExpectations([onSubscribeInvoked], timeout: 0.05)
|
|
|
|
subscriptionEventHandler(.connection(.connecting), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.connected), subscriptionItem)
|
|
subscriptionEventHandler(.data(successfulTestData), subscriptionItem)
|
|
subscriptionEventHandler(.data(invalidTestData), subscriptionItem)
|
|
subscriptionEventHandler(.data(successfulTestData), subscriptionItem)
|
|
subscriptionEventHandler(.connection(.disconnected), subscriptionItem)
|
|
|
|
await waitForSubscriptionExpectations()
|
|
}
|
|
|
|
// MARK: - Utilities
|
|
|
|
/// Sets up test with a mock subscription connection handler that populates
|
|
/// self.subscriptionItem and self.subscriptionEventHandler, then fulfills
|
|
/// self.onSubscribeInvoked
|
|
func setUpMocksAndSubscriptionItems() throws {
|
|
let onSubscribe: MockSubscriptionConnection.OnSubscribe = {
|
|
requestString, variables, eventHandler in
|
|
let item = SubscriptionItem(
|
|
requestString: requestString,
|
|
variables: variables,
|
|
eventHandler: eventHandler
|
|
)
|
|
|
|
self.subscriptionItem = item
|
|
self.subscriptionEventHandler = eventHandler
|
|
Task { await self.onSubscribeInvoked.fulfill() }
|
|
return item
|
|
}
|
|
|
|
let onGetOrCreateConnection: MockSubscriptionConnectionFactory.OnGetOrCreateConnection = { _, _, _, _ in
|
|
MockSubscriptionConnection(onSubscribe: onSubscribe, onUnsubscribe: { _ in })
|
|
}
|
|
|
|
try setUpPluginForSubscriptionResponse(onGetOrCreateConnection: onGetOrCreateConnection)
|
|
}
|
|
|
|
/// Calls `Amplify.API.subscribe` with a request made from a generic document, and returns
|
|
/// the operation created from that subscription. If `expectedValue` is not nil, also asserts
|
|
/// that the received value is equal to the expected value
|
|
func subscribe(
|
|
expecting expectedValue: JSONValue? = nil
|
|
) async throws {
|
|
let testDocument = "subscribe { subscribeTodos { id name description }}"
|
|
|
|
let request = GraphQLRequest(
|
|
document: testDocument,
|
|
variables: nil,
|
|
responseType: JSONValue.self
|
|
)
|
|
let subscription = apiPlugin.subscribe(request: request)
|
|
sink = Amplify.Publisher.create(subscription).sink { completion in
|
|
switch completion {
|
|
case .failure:
|
|
Task { await self.receivedCompletionFailure.fulfill() }
|
|
case .finished:
|
|
Task { await self.receivedCompletionSuccess.fulfill() }
|
|
}
|
|
} receiveValue: { subscriptionEvent in
|
|
switch subscriptionEvent {
|
|
case .connection(let connectionState):
|
|
switch connectionState {
|
|
case .connecting:
|
|
Task { await self.receivedStateValueConnecting.fulfill() }
|
|
case .connected:
|
|
Task { await self.receivedStateValueConnected.fulfill() }
|
|
case .disconnected:
|
|
Task { await self.receivedStateValueDisconnected.fulfill() }
|
|
}
|
|
case .data(let result):
|
|
switch result {
|
|
case .success(let actualValue):
|
|
if let expectedValue = expectedValue {
|
|
XCTAssertEqual(actualValue, expectedValue)
|
|
}
|
|
Task { await self.receivedDataValueSuccess.fulfill() }
|
|
case .failure:
|
|
Task { await self.receivedDataValueError.fulfill() }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|