diff --git a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSRESTOperationTests.swift b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSRESTOperationTests.swift index 771e7e60f4..e53fb5f38a 100644 --- a/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSRESTOperationTests.swift +++ b/AmplifyPlugins/API/Tests/AWSAPIPluginTests/Operation/AWSRESTOperationTests.swift @@ -52,7 +52,7 @@ class AWSRESTOperationTests: OperationTestBase { await fulfillment(of: [listenerWasInvoked], timeout: 3) } - func testGetFailsWithBadAPIName() throws { + func testGetFailsWithBadAPIName() async throws { let sentData = Data([0x00, 0x01, 0x02, 0x03]) try setUpPluginForSingleResponse(sending: sentData, for: .rest) @@ -69,14 +69,13 @@ class AWSRESTOperationTests: OperationTestBase { receivedFailure.fulfill() } } - - waitForExpectations(timeout: 1.00) + await fulfillment(of: [receivedSuccess, receivedFailure], timeout: 1) } /// - Given: A configured plugin /// - When: I invoke `APICategory.get(apiName:path:listener:)` /// - Then: The listener is invoked with the successful value - func testGetReturnsValue() throws { + func testGetReturnsValue() async throws { let sentData = Data([0x00, 0x01, 0x02, 0x03]) try setUpPluginForSingleResponse(sending: sentData, for: .rest) @@ -92,10 +91,10 @@ class AWSRESTOperationTests: OperationTestBase { callbackInvoked.fulfill() } - wait(for: [callbackInvoked], timeout: 1.0) + await fulfillment(of: [callbackInvoked], timeout: 1.0) } - func testRESTOperation_withCustomHeader_shouldOverrideDefaultAmplifyHeaders() throws { + func testRESTOperation_withCustomHeader_shouldOverrideDefaultAmplifyHeaders() async throws { let expectedHeaderValue = "text/plain" let sentData = Data([0x00, 0x01, 0x02, 0x03]) try setUpPluginForSingleResponse(sending: sentData, for: .rest) @@ -117,7 +116,7 @@ class AWSRESTOperationTests: OperationTestBase { } callbackInvoked.fulfill() } - wait(for: [callbackInvoked, validated], timeout: 1.0) + await fulfillment(of: [callbackInvoked, validated], timeout: 1.0) } } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueTests.swift index 610a6b0ffa..8802e832ff 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/MutationQueue/OutgoingMutationQueueTests.swift @@ -24,67 +24,57 @@ class OutgoingMutationQueueTests: SyncEngineTestBase { await tryOrFail { try setUpStorageAdapter() - try setUpDataStore(mutationQueue: OutgoingMutationQueue(storageAdapter: storageAdapter, - dataStoreConfiguration: .testDefault(), - authModeStrategy: AWSDefaultAuthModeStrategy())) - } - - let post = Post(title: "Post title", - content: "Post content", - createdAt: .now()) - - apiPlugin.responders[.mutateRequestResponse] = MutateRequestResponder { request in - let anyModel = try! post.eraseToAnyModel() - let remoteSyncMetadata = MutationSyncMetadata(modelId: post.id, - modelName: Post.modelName, - deleted: false, - lastChangedAt: Date().unixSeconds, - version: 2) - let remoteMutationSync = MutationSync(model: anyModel, syncMetadata: remoteSyncMetadata) - return .success(remoteMutationSync) + try setUpDataStore( + mutationQueue: OutgoingMutationQueue( + storageAdapter: storageAdapter, + dataStoreConfiguration: .testDefault(), + authModeStrategy: AWSDefaultAuthModeStrategy() + ) + ) } + let post = Post(title: "Post title", content: "Post content", createdAt: .now()) let outboxStatusReceivedCurrentCount = AtomicValue(initialValue: 0) let outboxStatusOnStart = expectation(description: "On DataStore start, outboxStatus received") let outboxStatusOnMutationEnqueued = expectation(description: "Mutation enqueued, outboxStatus received") let outboxMutationEnqueued = expectation(description: "Mutation enqueued, outboxMutationEnqueued received") - let outboxStatusFilter = HubFilters.forEventName(HubPayload.EventName.DataStore.outboxStatus) - let outboxMutationEnqueuedFilter = HubFilters.forEventName(HubPayload.EventName.DataStore.outboxMutationEnqueued) - let filters = HubFilters.any(filters: outboxStatusFilter, outboxMutationEnqueuedFilter) - let hubListener = Amplify.Hub.listen(to: .dataStore, isIncluded: filters) { payload in - if payload.eventName == HubPayload.EventName.DataStore.outboxStatus { - _ = outboxStatusReceivedCurrentCount.increment(by: 1) - guard let outboxStatusEvent = payload.data as? OutboxStatusEvent else { - XCTFail("Failed to cast payload data as OutboxStatusEvent") - return - } + let hubListener0 = Amplify.Hub.listen(to: .dataStore, eventName: HubPayload.EventName.DataStore.outboxStatus) { payload in + defer { _ = outboxStatusReceivedCurrentCount.increment(by: 1) } + guard let outboxStatusEvent = payload.data as? OutboxStatusEvent else { + XCTFail("Failed to cast payload data as OutboxStatusEvent") + return + } - switch outboxStatusReceivedCurrentCount.get() { - case 1: - XCTAssertTrue(outboxStatusEvent.isEmpty) - outboxStatusOnStart.fulfill() - case 2: - XCTAssertFalse(outboxStatusEvent.isEmpty) - outboxStatusOnMutationEnqueued.fulfill() - case 3: - XCTAssertTrue(outboxStatusEvent.isEmpty) - default: - XCTFail("Should not trigger outbox status event") - } + switch outboxStatusReceivedCurrentCount.get() { + case 0: + XCTAssertTrue(outboxStatusEvent.isEmpty) + outboxStatusOnStart.fulfill() + case 1: + XCTAssertFalse(outboxStatusEvent.isEmpty) + outboxStatusOnMutationEnqueued.fulfill() + case 2: + XCTAssertTrue(outboxStatusEvent.isEmpty) + default: + XCTFail("Should not trigger outbox status event") } + } - if payload.eventName == HubPayload.EventName.DataStore.outboxMutationEnqueued { - guard let outboxStatusEvent = payload.data as? OutboxMutationEvent else { - XCTFail("Failed to cast payload data as OutboxMutationEvent") - return - } - XCTAssertEqual(outboxStatusEvent.modelName, "Post") - outboxMutationEnqueued.fulfill() + let hubListener1 = Amplify.Hub.listen(to: .dataStore, eventName: HubPayload.EventName.DataStore.outboxMutationEnqueued) { payload in + guard let outboxStatusEvent = payload.data as? OutboxMutationEvent else { + XCTFail("Failed to cast payload data as OutboxMutationEvent") + return } + XCTAssertEqual(outboxStatusEvent.modelName, "Post") + outboxMutationEnqueued.fulfill() } - guard try await HubListenerTestUtilities.waitForListener(with: hubListener, timeout: 5.0) else { + guard try await HubListenerTestUtilities.waitForListener(with: hubListener0, timeout: 5.0) else { + XCTFail("Listener not registered for hub") + return + } + + guard try await HubListenerTestUtilities.waitForListener(with: hubListener1, timeout: 5.0) else { XCTFail("Listener not registered for hub") return } @@ -96,6 +86,19 @@ class OutgoingMutationQueueTests: SyncEngineTestBase { } } + apiPlugin.responders[.mutateRequestResponse] = MutateRequestResponder { request in + let anyModel = try! post.eraseToAnyModel() + let remoteSyncMetadata = MutationSyncMetadata( + modelId: post.id, + modelName: Post.modelName, + deleted: false, + lastChangedAt: Date().unixSeconds, + version: 2 + ) + let remoteMutationSync = MutationSync(model: anyModel, syncMetadata: remoteSyncMetadata) + return .success(remoteMutationSync) + } + try await startAmplifyAndWaitForSync() let saveSuccess = expectation(description: "save success") @@ -103,10 +106,10 @@ class OutgoingMutationQueueTests: SyncEngineTestBase { _ = try await Amplify.DataStore.save(post) saveSuccess.fulfill() } - await fulfillment(of: [saveSuccess], timeout: 1.0) await fulfillment( of: [ + saveSuccess, outboxStatusOnStart, outboxStatusOnMutationEnqueued, outboxMutationEnqueued, @@ -114,7 +117,8 @@ class OutgoingMutationQueueTests: SyncEngineTestBase { ], timeout: 5.0 ) - Amplify.Hub.removeListener(hubListener) + Amplify.Hub.removeListener(hubListener0) + Amplify.Hub.removeListener(hubListener1) } /// - Given: A sync-configured DataStore diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventExtensionsTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventExtensionsTests.swift index ab8f7640c1..0add6aebd1 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventExtensionsTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/Support/MutationEventExtensionsTests.swift @@ -21,7 +21,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { /// event model that matches the received mutation sync model. The received mutation sync has version 1. /// - When: The sent model matches the received model and the first pending mutation event version is `nil`. /// - Then: The pending mutation event version should be updated to the received model version of 1. - func testSentModelWithNilVersion_Reconciled() throws { + func testSentModelWithNilVersion_Reconciled() async throws { let modelId = UUID().uuidString let post = Post(id: modelId, title: "title", content: "content", createdAt: .now()) let requestMutationEvent = try createMutationEvent(model: post, @@ -57,7 +57,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { updatingVersionExpectation.fulfill() } } - wait(for: [updatingVersionExpectation], timeout: 1) + await fulfillment(of: [updatingVersionExpectation], timeout: 1) // query for head of mutation event table for given model id and check if it has the updated version MutationEvent.pendingMutationEvents(forModel: post, @@ -75,7 +75,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { queryAfterUpdatingVersionExpectation.fulfill() } } - wait(for: [queryAfterUpdatingVersionExpectation], timeout: 1) + await fulfillment(of: [queryAfterUpdatingVersionExpectation], timeout: 1) } /// - Given: A pending mutation events queue with two events(update and delete) containing `nil` version, @@ -85,7 +85,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { /// the second pending mutation event(delete) version is `nil`. /// - Then: The first pending mutation event(update) version should be updated to the received model version of 1 /// and the second pending mutation event version(delete) should not be updated. - func testSentModelWithNilVersion_SecondPendingEventNotReconciled() throws { + func testSentModelWithNilVersion_SecondPendingEventNotReconciled() async throws { let modelId = UUID().uuidString let post = Post(id: modelId, title: "title", content: "content", createdAt: .now()) let requestMutationEvent = try createMutationEvent(model: post, @@ -127,7 +127,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { updatingVersionExpectation.fulfill() } } - wait(for: [updatingVersionExpectation], timeout: 1) + await fulfillment(of: [updatingVersionExpectation], timeout: 1) // query for head of mutation event table for given model id and check if it has the updated version MutationEvent.pendingMutationEvents(forModel: post, @@ -146,7 +146,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { queryAfterUpdatingVersionExpectation.fulfill() } } - wait(for: [queryAfterUpdatingVersionExpectation], timeout: 1) + await fulfillment(of: [queryAfterUpdatingVersionExpectation], timeout: 1) } /// - Given: A pending mutation events queue with event containing version 2, a sent mutation event model @@ -154,7 +154,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { /// version 1. /// - When: The sent model matches the received model and the first pending mutation event version is 2. /// - Then: The first pending mutation event version should NOT be updated. - func testSentModelVersionNewerThanResponseVersion_PendingEventNotReconciled() throws { + func testSentModelVersionNewerThanResponseVersion_PendingEventNotReconciled() async throws { let modelId = UUID().uuidString let post1 = Post(id: modelId, title: "title1", content: "content1", createdAt: .now()) let post2 = Post(id: modelId, title: "title2", content: "content2", createdAt: .now()) @@ -190,7 +190,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { updatingVersionExpectation.fulfill() } } - wait(for: [updatingVersionExpectation], timeout: 1) + await fulfillment(of: [updatingVersionExpectation], timeout: 1) // query for head of mutation event table for given model id and check if it has the correct version MutationEvent.pendingMutationEvents(forModel: post1, @@ -208,7 +208,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { queryAfterUpdatingVersionExpectation.fulfill() } } - wait(for: [queryAfterUpdatingVersionExpectation], timeout: 1) + await fulfillment(of: [queryAfterUpdatingVersionExpectation], timeout: 1) } /// - Given: A pending mutation events queue with event containing version 1, a sent mutation event model @@ -216,7 +216,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { /// sync has version 2. /// - When: The sent model doesn't match the received model and the first pending mutation event version is 1. /// - Then: The first pending mutation event version should NOT be updated. - func testSentModelNotEqualToResponseModel_PendingEventNotReconciled() throws { + func testSentModelNotEqualToResponseModel_PendingEventNotReconciled() async throws { let modelId = UUID().uuidString let post1 = Post(id: modelId, title: "title1", content: "content1", createdAt: .now()) let post2 = Post(id: modelId, title: "title2", content: "content2", createdAt: .now()) @@ -253,7 +253,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { updatingVersionExpectation.fulfill() } } - wait(for: [updatingVersionExpectation], timeout: 1) + await fulfillment(of: [updatingVersionExpectation], timeout: 1) // query for head of mutation event table for given model id and check if it has the correct version MutationEvent.pendingMutationEvents(forModel: post1, @@ -271,7 +271,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { queryAfterUpdatingVersionExpectation.fulfill() } } - wait(for: [queryAfterUpdatingVersionExpectation], timeout: 1) + await fulfillment(of: [queryAfterUpdatingVersionExpectation], timeout: 1) } /// - Given: A pending mutation events queue with event containing version 1, a sent mutation event model @@ -279,7 +279,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { /// has version 2. /// - When: The sent model matches the received model and the first pending mutation event version is 1. /// - Then: The first pending mutation event version should be updated to received mutation sync version i.e. 2. - func testPendingVersionReconciledSuccess() throws { + func testPendingVersionReconciledSuccess() async throws { let modelId = UUID().uuidString let post1 = Post(id: modelId, title: "title1", content: "content1", createdAt: .now()) let post2 = Post(id: modelId, title: "title2", content: "content2", createdAt: .now()) @@ -315,7 +315,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { updatingVersionExpectation.fulfill() } } - wait(for: [updatingVersionExpectation], timeout: 1) + await fulfillment(of: [updatingVersionExpectation], timeout: 1) // query for head of mutation event table for given model id and check if it has the correct version MutationEvent.pendingMutationEvents(forModel: post1, @@ -333,7 +333,7 @@ class MutationEventExtensionsTest: BaseDataStoreTests { queryAfterUpdatingVersionExpectation.fulfill() } } - wait(for: [queryAfterUpdatingVersionExpectation], timeout: 1) + await fulfillment(of: [queryAfterUpdatingVersionExpectation], timeout: 1) } private func createMutationEvent(model: Model, diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/TestSupport/SyncEngineTestBase.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/TestSupport/SyncEngineTestBase.swift index 5d69207902..d86b8ba8a4 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/TestSupport/SyncEngineTestBase.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/TestSupport/SyncEngineTestBase.swift @@ -75,7 +75,6 @@ class SyncEngineTestBase: XCTestCase { authPlugin = MockAuthCategoryPlugin() try Amplify.add(plugin: apiPlugin) try Amplify.add(plugin: authPlugin) - Amplify.Logging.logLevel = .verbose } override func tearDown() async throws {