From c7fbe4241a0e596ee9cebd889388085713b20d08 Mon Sep 17 00:00:00 2001 From: Noah Durell Date: Fri, 22 Mar 2024 15:16:46 -0400 Subject: [PATCH 1/9] add new headers * mobile header indicating traffic coming from mobile sdk * retry attempt header indicating number of retries --- Sources/KlaviyoSwift/KlaviyoAPI.swift | 3 +- Sources/KlaviyoSwift/NetworkSession.swift | 4 ++- Sources/KlaviyoSwift/StateManagement.swift | 9 ++++-- .../APIRequestErrorHandlingTests.swift | 28 +++++++++---------- Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift | 2 +- .../KlaviyoSwiftTests/KlaviyoTestUtils.swift | 2 +- .../testSuccessfulResponseWithEvent.1.txt | 5 +++- .../testSuccessfulResponseWithProfile.1.txt | 5 +++- ...testSuccessfulResponseWithStoreToken.1.txt | 5 +++- .../testCreateEmphemeralSesionHeaders.1.txt | 5 +++- 10 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Sources/KlaviyoSwift/KlaviyoAPI.swift b/Sources/KlaviyoSwift/KlaviyoAPI.swift index 17f09ea1..30f1a0d9 100644 --- a/Sources/KlaviyoSwift/KlaviyoAPI.swift +++ b/Sources/KlaviyoSwift/KlaviyoAPI.swift @@ -39,7 +39,7 @@ struct KlaviyoAPI { static var requestRateLimited: (KlaviyoRequest) -> Void = { _ in } static var requestHttpError: (KlaviyoRequest, Int, Double) -> Void = { _, _, _ in } - var send: (KlaviyoRequest) async -> Result = { request in + var send: (KlaviyoRequest, Int) async -> Result = { request, attemptNumber in let start = Date() var urlRequest: URLRequest @@ -49,6 +49,7 @@ struct KlaviyoAPI { requestFailed(request, error, 0.0) return .failure(.internalRequestError(error)) } + urlRequest.allHTTPHeaderFields?["X-Klaviyo-Retry-Attempt"] = "\(attemptNumber)/50" requestStarted(request) diff --git a/Sources/KlaviyoSwift/NetworkSession.swift b/Sources/KlaviyoSwift/NetworkSession.swift index 0afe5a7f..d39868d4 100644 --- a/Sources/KlaviyoSwift/NetworkSession.swift +++ b/Sources/KlaviyoSwift/NetworkSession.swift @@ -14,7 +14,8 @@ func createEmphemeralSession(protocolClasses: [AnyClass] = URLProtocolOverrides. "User-Agent": NetworkSession.defaultUserAgent, "revision": NetworkSession.currentApiRevision, "content-type": NetworkSession.applicationJson, - "accept": NetworkSession.applicationJson + "accept": NetworkSession.applicationJson, + "X-Klaviyo-Mobile": NetworkSession.mobileHeader ] configuration.protocolClasses = protocolClasses return URLSession(configuration: configuration) @@ -24,6 +25,7 @@ struct NetworkSession { fileprivate static let currentApiRevision = "2023-07-15" fileprivate static let applicationJson = "application/json" fileprivate static let acceptedEncodings = ["br", "gzip", "deflate"] + fileprivate static let mobileHeader = "1" static let defaultUserAgent = { () -> String in let appContext = environment.analytics.appContextInfo() diff --git a/Sources/KlaviyoSwift/StateManagement.swift b/Sources/KlaviyoSwift/StateManagement.swift index cf576b6d..05791d2c 100644 --- a/Sources/KlaviyoSwift/StateManagement.swift +++ b/Sources/KlaviyoSwift/StateManagement.swift @@ -313,8 +313,13 @@ struct KlaviyoReducer: ReducerProtocol { return .none } let retryInfo = state.retryInfo - return .run { send in - let result = await environment.analytics.klaviyoAPI.send(request) + var numAttempts = 0 + if case let .retry(attempts) = retryInfo { + numAttempts = attempts + } + + return .run { [numAttempts] send in + let result = await environment.analytics.klaviyoAPI.send(request, numAttempts) switch result { case .success: // TODO: may want to inspect response further. diff --git a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift index dcadff38..0d161c70 100644 --- a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift +++ b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift @@ -25,7 +25,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.httpError(500, TEST_RETURN_DATA)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.httpError(500, TEST_RETURN_DATA)) } _ = await store.send(.sendRequest) @@ -41,7 +41,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.httpError(400, TEST_FAILURE_JSON_INVALID_PHONE_NUMBER.data(using: .utf8)!)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.httpError(400, TEST_FAILURE_JSON_INVALID_PHONE_NUMBER.data(using: .utf8)!)) } _ = await store.send(.sendRequest) @@ -63,7 +63,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.httpError(400, TEST_FAILURE_JSON_INVALID_EMAIL.data(using: .utf8)!)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.httpError(400, TEST_FAILURE_JSON_INVALID_EMAIL.data(using: .utf8)!)) } _ = await store.send(.sendRequest) @@ -88,7 +88,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request, request2] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } _ = await store.send(.sendRequest) @@ -108,7 +108,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request, request2] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } _ = await store.send(.sendRequest) @@ -130,7 +130,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request, request2] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.networkError(NSError(domain: "foo", code: NSURLErrorCancelled))) } _ = await store.send(.sendRequest) @@ -152,7 +152,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.internalError("internal error!")) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.internalError("internal error!")) } _ = await store.send(.sendRequest) @@ -173,7 +173,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.internalRequestError(KlaviyoAPI.KlaviyoAPIError.internalError("foo"))) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.internalRequestError(KlaviyoAPI.KlaviyoAPIError.internalError("foo"))) } _ = await store.send(.sendRequest) @@ -194,7 +194,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.unknownError(KlaviyoAPI.KlaviyoAPIError.internalError("foo"))) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.unknownError(KlaviyoAPI.KlaviyoAPIError.internalError("foo"))) } _ = await store.send(.sendRequest) @@ -214,7 +214,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.dataEncodingError(request)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.dataEncodingError(request)) } _ = await store.send(.sendRequest) @@ -234,7 +234,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.invalidData) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.invalidData) } _ = await store.send(.sendRequest) @@ -254,7 +254,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.rateLimitError) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError) } _ = await store.send(.sendRequest) @@ -273,7 +273,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.rateLimitError) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError) } _ = await store.send(.sendRequest) @@ -294,7 +294,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.missingOrInvalidResponse(nil)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.missingOrInvalidResponse(nil)) } _ = await store.send(.sendRequest) diff --git a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift index 449d76fc..934387bc 100644 --- a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift +++ b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift @@ -134,7 +134,7 @@ final class KlaviyoAPITests: XCTestCase { func sendAndAssert(with request: KlaviyoAPI.KlaviyoRequest, assertion: (Result) -> Void) async { - let result = await KlaviyoAPI().send(request) + let result = await KlaviyoAPI().send(request, 0) assertion(result) } } diff --git a/Tests/KlaviyoSwiftTests/KlaviyoTestUtils.swift b/Tests/KlaviyoSwiftTests/KlaviyoTestUtils.swift index abecb16d..251cecb4 100644 --- a/Tests/KlaviyoSwiftTests/KlaviyoTestUtils.swift +++ b/Tests/KlaviyoSwiftTests/KlaviyoTestUtils.swift @@ -156,7 +156,7 @@ extension FileClient { } extension KlaviyoAPI { - static let test = { KlaviyoAPI(send: { _ in .success(TEST_RETURN_DATA) }) } + static let test = { KlaviyoAPI(send: { _, _ in .success(TEST_RETURN_DATA) }) } } extension LoggerClient { diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt index e983bc39..4118dc3a 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt @@ -9,7 +9,10 @@ ▿ httpMethod: Optional - some: "POST" ▿ allHTTPHeaderFields: Optional> - - some: 0 key/value pairs + ▿ some: 1 key/value pair + ▿ (2 elements) + - key: "X-Klaviyo-Retry-Attempt" + - value: "0/50" ▿ httpBody: Optional - some: 0 bytes - httpBodyStream: Optional.none diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt index 60cd6017..d45fd287 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt @@ -9,7 +9,10 @@ ▿ httpMethod: Optional - some: "POST" ▿ allHTTPHeaderFields: Optional> - - some: 0 key/value pairs + ▿ some: 1 key/value pair + ▿ (2 elements) + - key: "X-Klaviyo-Retry-Attempt" + - value: "0/50" ▿ httpBody: Optional - some: 0 bytes - httpBodyStream: Optional.none diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt index 98f1c938..8423a2aa 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt @@ -9,7 +9,10 @@ ▿ httpMethod: Optional - some: "POST" ▿ allHTTPHeaderFields: Optional> - - some: 0 key/value pairs + ▿ some: 1 key/value pair + ▿ (2 elements) + - key: "X-Klaviyo-Retry-Attempt" + - value: "0/50" ▿ httpBody: Optional - some: 0 bytes - httpBodyStream: Optional.none diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/NetworkSessionTests/testCreateEmphemeralSesionHeaders.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/NetworkSessionTests/testCreateEmphemeralSesionHeaders.1.txt index 0cce6297..82436a64 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/NetworkSessionTests/testCreateEmphemeralSesionHeaders.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/NetworkSessionTests/testCreateEmphemeralSesionHeaders.1.txt @@ -1,5 +1,5 @@ ▿ Optional> - ▿ some: 5 key/value pairs + ▿ some: 6 key/value pairs ▿ (2 elements) - key: "Accept-Encoding" ▿ value: 3 elements @@ -9,6 +9,9 @@ ▿ (2 elements) - key: "User-Agent" - value: "FooApp/1.2.3 (com.klaviyo.fooapp; build:1; iOS 1.1.1) klaviyo-ios/3.0.4" + ▿ (2 elements) + - key: "X-Klaviyo-Mobile" + - value: "1" ▿ (2 elements) - key: "accept" - value: "application/json" From 3dfced90cfd86681cfd3fd125842005f90733743 Mon Sep 17 00:00:00 2001 From: Noah Durell Date: Fri, 22 Mar 2024 17:02:24 -0400 Subject: [PATCH 2/9] fix a bunch of warnings --- Sources/KlaviyoSwift/KlaviyoState.swift | 2 +- .../APIRequestErrorHandlingTests.swift | 16 +++++++++++++- .../AppLifeCycleEventsTests.swift | 3 ++- Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift | 6 +---- .../KlaviyoSwiftTests/KlaviyoStateTests.swift | 1 - .../NetworkSessionTests.swift | 2 +- .../StateChangePublisherTests.swift | 4 +++- .../StateManagementEdgeCaseTests.swift | 22 ++++++++++++++++++- .../StateManagementTests.swift | 11 +++++++++- 9 files changed, 54 insertions(+), 13 deletions(-) diff --git a/Sources/KlaviyoSwift/KlaviyoState.swift b/Sources/KlaviyoSwift/KlaviyoState.swift index 5791c23d..0eef7aa4 100644 --- a/Sources/KlaviyoSwift/KlaviyoState.swift +++ b/Sources/KlaviyoSwift/KlaviyoState.swift @@ -190,7 +190,7 @@ struct KlaviyoState: Equatable, Codable { } var attributes = profile.data.attributes var location = profile.data.attributes.location ?? .init() - var properties = profile.data.attributes.properties.value as? [String: Any] ?? [:] + let properties = profile.data.attributes.properties.value as? [String: Any] ?? [:] let updatedProfile = Profile.updateProfileWithProperties(dict: pendingProfile) if let firstName = updatedProfile.firstName { diff --git a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift index 0d161c70..53db5cae 100644 --- a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift +++ b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift @@ -11,14 +11,15 @@ import XCTest let TIMEOUT_NANOSECONDS: UInt64 = 10_000_000_000 // 10 seconds -@MainActor class APIRequestErrorHandlingTests: XCTestCase { + @MainActor override func setUp() async throws { environment = KlaviyoEnvironment.test() } // MARK: - http error + @MainActor func testSendRequestHttpFailureDequesRequest() async throws { var initialState = INITIALIZED_TEST_STATE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -35,6 +36,7 @@ class APIRequestErrorHandlingTests: XCTestCase { } } + @MainActor func testSendRequestHttpFailureForPhoneNumberResetsStateAndDequesRequest() async throws { var initialState = INITIALIZED_TEST_STATE_INVALID_PHONE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -57,6 +59,7 @@ class APIRequestErrorHandlingTests: XCTestCase { } } + @MainActor func testSendRequestHttpFailureForEmailResetsStateAndDequesRequest() async throws { var initialState = INITIALIZED_TEST_STATE_INVALID_EMAIL() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -81,6 +84,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - network error + @MainActor func testSendRequestFailureIncrementsRetryCount() async throws { var initialState = INITIALIZED_TEST_STATE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -100,6 +104,7 @@ class APIRequestErrorHandlingTests: XCTestCase { } } + @MainActor func testSendRequestFailureWithBackoff() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retryWithBackoff(requestCount: 1, totalRetryCount: 1, currentBackoff: 1) @@ -120,6 +125,7 @@ class APIRequestErrorHandlingTests: XCTestCase { } } + @MainActor func testSendRequestMaxRetries() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retry(ErrorHandlingConstants.maxRetries) @@ -144,6 +150,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - internal error + @MainActor func testSendRequestInternalError() async throws { // NOTE: should really happen but putting this in for possible future cases and test coverage var initialState = INITIALIZED_TEST_STATE() @@ -166,6 +173,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - internal request error + @MainActor func testSendRequestInternalRequestError() async throws { var initialState = INITIALIZED_TEST_STATE() @@ -187,6 +195,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - unknown error + @MainActor func testSendRequestUnknownError() async throws { var initialState = INITIALIZED_TEST_STATE() @@ -208,6 +217,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - data decoding error + @MainActor func testSendRequestDataDecodingError() async throws { var initialState = INITIALIZED_TEST_STATE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -228,6 +238,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - invalid data + @MainActor func testSendRequestInvalidData() async throws { var initialState = INITIALIZED_TEST_STATE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -248,6 +259,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - rate limit error + @MainActor func testRateLimitErrorWithExistingRetry() async throws { var initialState = INITIALIZED_TEST_STATE() let request = initialState.buildProfileRequest(apiKey: initialState.apiKey!, anonymousId: initialState.anonymousId!) @@ -266,6 +278,7 @@ class APIRequestErrorHandlingTests: XCTestCase { } } + @MainActor func testRateLimitErrorWithExistingBackoffRetry() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retryWithBackoff(requestCount: 2, totalRetryCount: 2, currentBackoff: 4) @@ -287,6 +300,7 @@ class APIRequestErrorHandlingTests: XCTestCase { // MARK: - Missing or invalid response + @MainActor func testMissingOrInvalidResponse() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retryWithBackoff(requestCount: 2, totalRetryCount: 2, currentBackoff: 4) diff --git a/Tests/KlaviyoSwiftTests/AppLifeCycleEventsTests.swift b/Tests/KlaviyoSwiftTests/AppLifeCycleEventsTests.swift index 4443181b..64e53866 100644 --- a/Tests/KlaviyoSwiftTests/AppLifeCycleEventsTests.swift +++ b/Tests/KlaviyoSwiftTests/AppLifeCycleEventsTests.swift @@ -10,7 +10,6 @@ import Combine import Foundation import XCTest -@MainActor class AppLifeCycleEventsTests: XCTestCase { let passThroughSubject = PassthroughSubject() @@ -25,12 +24,14 @@ class AppLifeCycleEventsTests: XCTestCase { } } + @MainActor override func setUp() { environment = KlaviyoEnvironment.test() } // MARK: - App Terminate + @MainActor func testAppTerminateStopsReachability() { environment.notificationCenterPublisher = getFilteredNotificaitonPublished(name: UIApplication.willTerminateNotification) let expection = XCTestExpectation(description: "Stop reachability is called.") diff --git a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift index 934387bc..769c0fd3 100644 --- a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift +++ b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift @@ -9,18 +9,14 @@ import SnapshotTesting import XCTest -@MainActor final class KlaviyoAPITests: XCTestCase { + @MainActor override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. environment = KlaviyoEnvironment.test() } - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - func testInvalidURL() async throws { environment.analytics.apiURL = "" diff --git a/Tests/KlaviyoSwiftTests/KlaviyoStateTests.swift b/Tests/KlaviyoSwiftTests/KlaviyoStateTests.swift index 8677f494..a59445c8 100644 --- a/Tests/KlaviyoSwiftTests/KlaviyoStateTests.swift +++ b/Tests/KlaviyoSwiftTests/KlaviyoStateTests.swift @@ -11,7 +11,6 @@ import Foundation import SnapshotTesting import XCTest -@MainActor final class KlaviyoStateTests: XCTestCase { let TEST_EVENT = [ "event": "$opened_push", diff --git a/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift b/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift index 86a8c04a..bd02e58e 100644 --- a/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift +++ b/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift @@ -9,8 +9,8 @@ import SnapshotTesting import XCTest -@MainActor class NetworkSessionTests: XCTestCase { + @MainActor override func setUpWithError() throws { environment = KlaviyoEnvironment.test() } diff --git a/Tests/KlaviyoSwiftTests/StateChangePublisherTests.swift b/Tests/KlaviyoSwiftTests/StateChangePublisherTests.swift index 94b01934..4cfe4dec 100644 --- a/Tests/KlaviyoSwiftTests/StateChangePublisherTests.swift +++ b/Tests/KlaviyoSwiftTests/StateChangePublisherTests.swift @@ -11,12 +11,13 @@ import Foundation import XCTest @_spi(KlaviyoPrivate) @testable import KlaviyoSwift -@MainActor final class StateChangePublisherTests: XCTestCase { + @MainActor override func setUpWithError() throws { environment = KlaviyoEnvironment.test() } + @MainActor func testStateChangePublisher() throws { let savedCalledExpectation = XCTestExpectation(description: "Save called on initialization") // Third call set email should trigger again @@ -76,6 +77,7 @@ final class StateChangePublisherTests: XCTestCase { XCTAssertEqual(count, 2) } + @MainActor func testStateChangeDuplicateAreRemoved() throws { let savedCalledExpectation = XCTestExpectation(description: "Save called on initialization") savedCalledExpectation.assertForOverFulfill = true diff --git a/Tests/KlaviyoSwiftTests/StateManagementEdgeCaseTests.swift b/Tests/KlaviyoSwiftTests/StateManagementEdgeCaseTests.swift index 137d5911..48c6e083 100644 --- a/Tests/KlaviyoSwiftTests/StateManagementEdgeCaseTests.swift +++ b/Tests/KlaviyoSwiftTests/StateManagementEdgeCaseTests.swift @@ -9,14 +9,15 @@ import Foundation import XCTest -@MainActor class StateManagementEdgeCaseTests: XCTestCase { + @MainActor override func setUp() async throws { environment = KlaviyoEnvironment.test() } // MARK: - initialization + @MainActor func testInitializeWhileInitializing() async throws { let initialState = KlaviyoState(queue: [], requestsInFlight: []) let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -39,6 +40,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.initialize(apiKey)) } + @MainActor func testInitializeAfterInitialized() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -57,6 +59,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Send Request + @MainActor func testSendRequestBeforeInitialization() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -71,6 +74,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Complete Initialization + @MainActor func testCompleteInitializationWhileAlreadyInitialized() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -87,6 +91,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.completeInitialization(initialState)) } + @MainActor func testCompleteInitializationWithExistingIdentifiers() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -110,6 +115,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Set Email + @MainActor func testSetEmailUninitialized() async throws { let expection = XCTestExpectation(description: "fatal error expected") environment.emitDeveloperWarning = { _ in @@ -130,6 +136,7 @@ class StateManagementEdgeCaseTests: XCTestCase { await fulfillment(of: [expection]) } + @MainActor func testSetEmailMissingAnonymousIdStillSetsEmail() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -146,6 +153,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Set External Id + @MainActor func testSetExternalIdUninitialized() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -159,6 +167,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.setExternalId("external-blob-id")) } + @MainActor func testSetExternalIdMissingAnonymousIdStillSetsExternalId() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -175,6 +184,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Set Phone number + @MainActor func testSetPhoneNumberUninitialized() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -188,6 +198,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.setPhoneNumber("1-800-Blobs4u")) } + @MainActor func testSetPhoneNumberMissingApiKeyStillSetsPhoneNumber() async throws { let initialState = KlaviyoState(anonymousId: environment.analytics.uuid().uuidString, queue: [], @@ -203,6 +214,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Set Push Token + @MainActor func testSetPushTokenUninitialized() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -216,6 +228,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.setPushToken("blob_token", .authorized)) } + @MainActor func testSetPushTokenWithMissingAnonymousId() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -233,6 +246,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Stop + @MainActor func testStopUninitialized() async { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -246,6 +260,7 @@ class StateManagementEdgeCaseTests: XCTestCase { _ = await store.send(.stop) } + @MainActor func testStopInitializing() async { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -261,6 +276,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Start + @MainActor func testStartUninitialized() async { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -276,6 +292,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Network Status Changed + @MainActor func testNetworkStatusChangedUninitialized() async { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -291,6 +308,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - Missing api key for token request + @MainActor func testTokenRequestMissingApiKey() async { let initialState = KlaviyoState( anonymousId: environment.analytics.uuid().uuidString, @@ -308,6 +326,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - set enqueue event uninitialized + @MainActor func testEnqueueEventUninitialized() async throws { let expection = XCTestExpectation(description: "fatal error expected") environment.emitDeveloperWarning = { _ in @@ -322,6 +341,7 @@ class StateManagementEdgeCaseTests: XCTestCase { // MARK: - set profile uninitialized + @MainActor func testSetProfileUnitialized() async throws { let expection = XCTestExpectation(description: "fatal error expected") environment.emitDeveloperWarning = { _ in diff --git a/Tests/KlaviyoSwiftTests/StateManagementTests.swift b/Tests/KlaviyoSwiftTests/StateManagementTests.swift index aa90c734..2bdcc109 100644 --- a/Tests/KlaviyoSwiftTests/StateManagementTests.swift +++ b/Tests/KlaviyoSwiftTests/StateManagementTests.swift @@ -11,14 +11,15 @@ import Combine import Foundation import XCTest -@MainActor class StateManagementTests: XCTestCase { + @MainActor override func setUp() async throws { environment = KlaviyoEnvironment.test() } // MARK: - Initialization + @MainActor func testInitialize() async throws { let initialState = KlaviyoState(queue: [], requestsInFlight: []) let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -41,6 +42,7 @@ class StateManagementTests: XCTestCase { await store.receive(.flushQueue) } + @MainActor func testInitializeSubscribesToAppropriatePublishers() async throws { let lifecycleExpectation = XCTestExpectation(description: "lifecycle is subscribed") let stateChangeIsSubscribed = XCTestExpectation(description: "state change is subscribed") @@ -73,6 +75,7 @@ class StateManagementTests: XCTestCase { // MARK: - Set Email + @MainActor func testSetEmail() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -87,6 +90,7 @@ class StateManagementTests: XCTestCase { // MARK: Set Phone Number + @MainActor func testSetPhoneNumber() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -101,6 +105,7 @@ class StateManagementTests: XCTestCase { // MARK: - Set External Id. + @MainActor func testSetExternalId() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -115,6 +120,7 @@ class StateManagementTests: XCTestCase { // MARK: - Set Push Token + @MainActor func testSetPushToken() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.pushTokenData = nil @@ -141,6 +147,7 @@ class StateManagementTests: XCTestCase { } } + @MainActor func testSetPushTokenMultipleTimes() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.pushTokenData = nil @@ -171,6 +178,7 @@ class StateManagementTests: XCTestCase { // MARK: - flush + @MainActor func testFlushUninitializedQueueDoesNotFlush() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -182,6 +190,7 @@ class StateManagementTests: XCTestCase { _ = await store.send(.flushQueue) } + @MainActor func testQueueThatIsFlushingDoesNotFlush() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, From 406ca0b8f0c4bd7b152eda82a6e75c680783595e Mon Sep 17 00:00:00 2001 From: Noah Durell Date: Fri, 22 Mar 2024 17:06:04 -0400 Subject: [PATCH 3/9] missed some --- Tests/KlaviyoSwiftTests/StateManagementTests.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Tests/KlaviyoSwiftTests/StateManagementTests.swift b/Tests/KlaviyoSwiftTests/StateManagementTests.swift index 2bdcc109..d31d504c 100644 --- a/Tests/KlaviyoSwiftTests/StateManagementTests.swift +++ b/Tests/KlaviyoSwiftTests/StateManagementTests.swift @@ -202,6 +202,7 @@ class StateManagementTests: XCTestCase { _ = await store.send(.flushQueue) } + @MainActor func testEmptyQueueDoesNotFlush() async throws { let apiKey = "fake-key" let initialState = KlaviyoState(apiKey: apiKey, @@ -213,6 +214,7 @@ class StateManagementTests: XCTestCase { _ = await store.send(.flushQueue) } + @MainActor func testFlushQueueWithMultipleRequests() async throws { var count = 0 // request uuids need to be unique :) @@ -255,6 +257,7 @@ class StateManagementTests: XCTestCase { } } + @MainActor func testFlushQueueDuringExponentialBackoff() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retryWithBackoff(requestCount: 23, totalRetryCount: 23, currentBackoff: 200) @@ -269,6 +272,7 @@ class StateManagementTests: XCTestCase { } } + @MainActor func testFlushQueueExponentialBackoffGoesToSize() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.retryInfo = .retryWithBackoff(requestCount: 23, totalRetryCount: 23, currentBackoff: Int(initialState.flushInterval) - 2) @@ -295,6 +299,7 @@ class StateManagementTests: XCTestCase { } } + @MainActor func testSendRequestWhenNotFlushing() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.flushing = false @@ -305,6 +310,7 @@ class StateManagementTests: XCTestCase { // MARK: - send request + @MainActor func testSendRequestWithNoRequestsInFlight() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -316,6 +322,7 @@ class StateManagementTests: XCTestCase { // MARK: - Network Connectivity Changed + @MainActor func testNetworkConnectivityChanges() async throws { let initialState = INITIALIZED_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) @@ -339,6 +346,7 @@ class StateManagementTests: XCTestCase { // MARK: - Stop + @MainActor func testStopWithRequestsInFlight() async throws { // This test is a little convoluted but essentially want to make when we stop // that we save our state. @@ -359,6 +367,7 @@ class StateManagementTests: XCTestCase { // MARK: - Test pending profile + @MainActor func testFlushWithPendingProfile() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.flushing = false @@ -435,6 +444,7 @@ class StateManagementTests: XCTestCase { // MARK: - Test set profile + @MainActor func testSetProfileWithExistingProperties() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.phoneNumber = "555BLOB" @@ -450,6 +460,7 @@ class StateManagementTests: XCTestCase { // MARK: - Test enqueue event + @MainActor func testEnqueueEvent() async throws { var initialState = INITIALIZED_TEST_STATE() initialState.phoneNumber = "555BLOB" @@ -461,6 +472,7 @@ class StateManagementTests: XCTestCase { } } + @MainActor func testEnqueueEventWhenInitilizingSendsEvent() async throws { let initialState = INITILIZING_TEST_STATE() let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) From 9ddcfe80f2667f6ce91c2e76208b28f3386bbbb6 Mon Sep 17 00:00:00 2001 From: Noah Durell Date: Mon, 1 Apr 2024 19:04:33 -0400 Subject: [PATCH 4/9] includ 429 in iterator --- Sources/KlaviyoSwift/SDKRequestIterator.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/KlaviyoSwift/SDKRequestIterator.swift b/Sources/KlaviyoSwift/SDKRequestIterator.swift index 555c667a..78c0f7fe 100644 --- a/Sources/KlaviyoSwift/SDKRequestIterator.swift +++ b/Sources/KlaviyoSwift/SDKRequestIterator.swift @@ -137,6 +137,7 @@ public func requestIterator() -> AsyncStream { KlaviyoAPI.requestFailed = { _, _, _ in } KlaviyoAPI.requestCompleted = { _, _, _ in } KlaviyoAPI.requestHttpError = { _, _, _ in } + KlaviyoAPI.requestRateLimited = { _ in } } KlaviyoAPI.requestStarted = { request in continuation.yield(SDKRequest.fromAPIRequest(request: request, response: .inProgress)) @@ -151,5 +152,8 @@ public func requestIterator() -> AsyncStream { KlaviyoAPI.requestHttpError = { request, statusCode, duration in continuation.yield(SDKRequest.fromAPIRequest(request: request, response: .httpError(statusCode, duration))) } + KlaviyoAPI.requestRateLimited = { request in + continuation.yield(SDKRequest.fromAPIRequest(request: request, response: .reqeustError("Rate Limited", 0.0))) + } } } From ff151d25f50d4a1422d276812b6de19e5b47c21a Mon Sep 17 00:00:00 2001 From: Ajay Subramanya Date: Mon, 8 Apr 2024 16:45:10 -0500 Subject: [PATCH 5/9] updated initial attempt to 1 and moved adding header --- Sources/KlaviyoSwift/KlaviyoAPI.swift | 6 +++--- Sources/KlaviyoSwift/KlaviyoState.swift | 2 +- Sources/KlaviyoSwift/StateManagement.swift | 7 ++++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Sources/KlaviyoSwift/KlaviyoAPI.swift b/Sources/KlaviyoSwift/KlaviyoAPI.swift index 30f1a0d9..5254a37c 100644 --- a/Sources/KlaviyoSwift/KlaviyoAPI.swift +++ b/Sources/KlaviyoSwift/KlaviyoAPI.swift @@ -44,12 +44,11 @@ struct KlaviyoAPI { var urlRequest: URLRequest do { - urlRequest = try request.urlRequest() + urlRequest = try request.urlRequest(attemptNumber) } catch { requestFailed(request, error, 0.0) return .failure(.internalRequestError(error)) } - urlRequest.allHTTPHeaderFields?["X-Klaviyo-Retry-Attempt"] = "\(attemptNumber)/50" requestStarted(request) @@ -86,7 +85,7 @@ struct KlaviyoAPI { } extension KlaviyoAPI.KlaviyoRequest { - func urlRequest() throws -> URLRequest { + func urlRequest(_ attemptNumber: Int = StateManagementConstants.initialAttempt) throws -> URLRequest { guard let url = url else { throw KlaviyoAPI.KlaviyoAPIError.internalError("Invalid url string. API URL: \(environment.analytics.apiURL)") } @@ -97,6 +96,7 @@ extension KlaviyoAPI.KlaviyoRequest { } request.httpBody = body request.httpMethod = "POST" + request.setValue("\(attemptNumber)/50", forHTTPHeaderField: "X-Klaviyo-Attempt-Count") return request } diff --git a/Sources/KlaviyoSwift/KlaviyoState.swift b/Sources/KlaviyoSwift/KlaviyoState.swift index 0eef7aa4..bec5a1a2 100644 --- a/Sources/KlaviyoSwift/KlaviyoState.swift +++ b/Sources/KlaviyoSwift/KlaviyoState.swift @@ -98,7 +98,7 @@ struct KlaviyoState: Equatable, Codable { var initalizationState = InitializationState.uninitialized var flushing = false var flushInterval = StateManagementConstants.wifiFlushInterval - var retryInfo = RetryInfo.retry(0) + var retryInfo = RetryInfo.retry(StateManagementConstants.initialAttempt) var pendingRequests: [PendingRequest] = [] var pendingProfile: [Profile.ProfileKey: AnyEncodable]? diff --git a/Sources/KlaviyoSwift/StateManagement.swift b/Sources/KlaviyoSwift/StateManagement.swift index 05791d2c..cfccd47d 100644 --- a/Sources/KlaviyoSwift/StateManagement.swift +++ b/Sources/KlaviyoSwift/StateManagement.swift @@ -21,6 +21,7 @@ enum StateManagementConstants { static let cellularFlushInterval = 30.0 static let wifiFlushInterval = 10.0 static let maxQueueSize = 200 + static let initialAttempt = 1 } enum RetryInfo: Equatable { @@ -293,7 +294,7 @@ struct KlaviyoReducer: ReducerProtocol { state.requestsInFlight.removeAll { inflightRequest in completedRequest.uuid == inflightRequest.uuid } - state.retryInfo = RetryInfo.retry(0) + state.retryInfo = RetryInfo.retry(StateManagementConstants.initialAttempt) if state.requestsInFlight.isEmpty { state.flushing = false return .none @@ -313,7 +314,7 @@ struct KlaviyoReducer: ReducerProtocol { return .none } let retryInfo = state.retryInfo - var numAttempts = 0 + var numAttempts = 1 if case let .retry(attempts) = retryInfo { numAttempts = attempts } @@ -366,7 +367,7 @@ struct KlaviyoReducer: ReducerProtocol { switch retryInfo { case let .retry(count): exceededRetries = count > ErrorHandlingConstants.maxRetries - state.retryInfo = .retry(exceededRetries ? 0 : count) + state.retryInfo = .retry(exceededRetries ? 1 : count) case let .retryWithBackoff(requestCount, totalCount, backOff): exceededRetries = requestCount > ErrorHandlingConstants.maxRetries state.retryInfo = .retryWithBackoff(requestCount: exceededRetries ? 0 : requestCount, totalRetryCount: totalCount, currentBackoff: backOff) From 2ae7ce225e544f694a5b2a2bf8dfe18528c8ba05 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya Date: Tue, 9 Apr 2024 14:19:12 -0500 Subject: [PATCH 6/9] trying to fix some tetss --- .../APIRequestErrorHandlingTests.swift | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift index 53db5cae..49c06ad9 100644 --- a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift +++ b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift @@ -55,7 +55,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -78,7 +78,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -96,11 +96,11 @@ class APIRequestErrorHandlingTests: XCTestCase { _ = await store.send(.sendRequest) - await store.receive(.requestFailed(request, .retry(1)), timeout: TIMEOUT_NANOSECONDS) { + await store.receive(.requestFailed(request, .retry(2)), timeout: TIMEOUT_NANOSECONDS) { $0.flushing = false $0.queue = [request, request2] $0.requestsInFlight = [] - $0.retryInfo = .retry(1) + $0.retryInfo = .retry(2) } } @@ -144,7 +144,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [request2] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -167,7 +167,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -189,7 +189,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -211,7 +211,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -232,7 +232,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -253,7 +253,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } @@ -270,11 +270,11 @@ class APIRequestErrorHandlingTests: XCTestCase { _ = await store.send(.sendRequest) - await store.receive(.requestFailed(request, .retryWithBackoff(requestCount: 1, totalRetryCount: 1, currentBackoff: 0)), timeout: TIMEOUT_NANOSECONDS) { + await store.receive(.requestFailed(request, .retryWithBackoff(requestCount: 2, totalRetryCount: 2, currentBackoff: 0)), timeout: TIMEOUT_NANOSECONDS) { $0.flushing = false $0.queue = [request] $0.requestsInFlight = [] - $0.retryInfo = .retryWithBackoff(requestCount: 1, totalRetryCount: 1, currentBackoff: 0) + $0.retryInfo = .retryWithBackoff(requestCount: 2, totalRetryCount: 2, currentBackoff: 0) } } @@ -316,7 +316,7 @@ class APIRequestErrorHandlingTests: XCTestCase { $0.flushing = false $0.queue = [] $0.requestsInFlight = [] - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) } } } From 352ded64a08f1c9f51f97898aeb64239effeef68 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya Date: Wed, 10 Apr 2024 09:20:01 -0500 Subject: [PATCH 7/9] Fixing tests --- Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift index 2beb192c..e29f9fff 100644 --- a/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift +++ b/Tests/KlaviyoSwiftTests/APIRequestErrorHandlingTests.swift @@ -266,7 +266,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError(nil)) } _ = await store.send(.sendRequest) @@ -286,7 +286,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError(nil)) } _ = await store.send(.sendRequest) @@ -305,7 +305,7 @@ class APIRequestErrorHandlingTests: XCTestCase { initialState.requestsInFlight = [request] let store = TestStore(initialState: initialState, reducer: KlaviyoReducer()) - environment.analytics.klaviyoAPI.send = { _ in .failure(.rateLimitError(20)) } + environment.analytics.klaviyoAPI.send = { _, _ in .failure(.rateLimitError(20)) } _ = await store.send(.sendRequest) From f627d9181cf27ce30caca02de5a320824c5983a2 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya Date: Wed, 10 Apr 2024 09:48:25 -0500 Subject: [PATCH 8/9] fixed state management tests --- .../StateManagementTests.swift | 2 +- .../testLoadNewKlaviyoState.1.txt | 18 ------------------ .../testStateFileExistsInvalidData.1.txt | 18 ------------------ .../testStateFileExistsInvalidJSON.1.txt | 18 ------------------ .../testValidStateFileExists.1.txt | 18 ------------------ 5 files changed, 1 insertion(+), 73 deletions(-) delete mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt delete mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt delete mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt delete mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt diff --git a/Tests/KlaviyoSwiftTests/StateManagementTests.swift b/Tests/KlaviyoSwiftTests/StateManagementTests.swift index d31d504c..dcdadb74 100644 --- a/Tests/KlaviyoSwiftTests/StateManagementTests.swift +++ b/Tests/KlaviyoSwiftTests/StateManagementTests.swift @@ -293,7 +293,7 @@ class StateManagementTests: XCTestCase { // didn't fake uuid since we are not testing this. await store.receive(.deQueueCompletedResults(request)) { $0.flushing = false - $0.retryInfo = .retry(0) + $0.retryInfo = .retry(1) $0.requestsInFlight = [] $0.queue = [] } diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt deleted file mode 100644 index 4cabef0e..00000000 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt +++ /dev/null @@ -1,18 +0,0 @@ -▿ KlaviyoState - ▿ anonymousId: Optional - - some: "00000000-0000-0000-0000-000000000001" - ▿ apiKey: Optional - - some: "foo" - - email: Optional.none - - externalId: Optional.none - - flushInterval: 10.0 - - flushing: false - - initalizationState: InitializationState.uninitialized - - pendingProfile: Optional>.none - - pendingRequests: 0 elements - - phoneNumber: Optional.none - - pushTokenData: Optional.none - - queue: 0 elements - - requestsInFlight: 0 elements - ▿ retryInfo: RetryInfo - - retry: 0 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt deleted file mode 100644 index 4cabef0e..00000000 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt +++ /dev/null @@ -1,18 +0,0 @@ -▿ KlaviyoState - ▿ anonymousId: Optional - - some: "00000000-0000-0000-0000-000000000001" - ▿ apiKey: Optional - - some: "foo" - - email: Optional.none - - externalId: Optional.none - - flushInterval: 10.0 - - flushing: false - - initalizationState: InitializationState.uninitialized - - pendingProfile: Optional>.none - - pendingRequests: 0 elements - - phoneNumber: Optional.none - - pushTokenData: Optional.none - - queue: 0 elements - - requestsInFlight: 0 elements - ▿ retryInfo: RetryInfo - - retry: 0 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt deleted file mode 100644 index 4cabef0e..00000000 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt +++ /dev/null @@ -1,18 +0,0 @@ -▿ KlaviyoState - ▿ anonymousId: Optional - - some: "00000000-0000-0000-0000-000000000001" - ▿ apiKey: Optional - - some: "foo" - - email: Optional.none - - externalId: Optional.none - - flushInterval: 10.0 - - flushing: false - - initalizationState: InitializationState.uninitialized - - pendingProfile: Optional>.none - - pendingRequests: 0 elements - - phoneNumber: Optional.none - - pushTokenData: Optional.none - - queue: 0 elements - - requestsInFlight: 0 elements - ▿ retryInfo: RetryInfo - - retry: 0 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt deleted file mode 100644 index 4cabef0e..00000000 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt +++ /dev/null @@ -1,18 +0,0 @@ -▿ KlaviyoState - ▿ anonymousId: Optional - - some: "00000000-0000-0000-0000-000000000001" - ▿ apiKey: Optional - - some: "foo" - - email: Optional.none - - externalId: Optional.none - - flushInterval: 10.0 - - flushing: false - - initalizationState: InitializationState.uninitialized - - pendingProfile: Optional>.none - - pendingRequests: 0 elements - - phoneNumber: Optional.none - - pushTokenData: Optional.none - - queue: 0 elements - - requestsInFlight: 0 elements - ▿ retryInfo: RetryInfo - - retry: 0 From 1c9009d275561700dfd2cd0d7b60d9966c5f72f3 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya Date: Wed, 10 Apr 2024 10:16:16 -0500 Subject: [PATCH 9/9] fixed tests I think --- Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift | 3 +-- .../NetworkSessionTests.swift | 2 +- .../testSuccessfulResponseWithEvent.1.txt | 2 +- .../testSuccessfulResponseWithProfile.1.txt | 2 +- .../testSuccessfulResponseWithStoreToken.1.txt | 2 +- .../testLoadNewKlaviyoState.1.txt | 18 ++++++++++++++++++ .../testStateFileExistsInvalidData.1.txt | 18 ++++++++++++++++++ .../testStateFileExistsInvalidJSON.1.txt | 18 ++++++++++++++++++ .../testValidStateFileExists.1.txt | 18 ++++++++++++++++++ 9 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt create mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt create mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt create mode 100644 Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt diff --git a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift index 769c0fd3..c05a58b8 100644 --- a/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift +++ b/Tests/KlaviyoSwiftTests/KlaviyoAPITests.swift @@ -9,8 +9,8 @@ import SnapshotTesting import XCTest +@MainActor final class KlaviyoAPITests: XCTestCase { - @MainActor override func setUpWithError() throws { // Put setup code here. This method is called before the invocation of each test method in the class. @@ -101,7 +101,6 @@ final class KlaviyoAPITests: XCTestCase { }) } let request = KlaviyoAPI.KlaviyoRequest(apiKey: "foo", endpoint: .createEvent(.init(data: .init(event: .test)))) await sendAndAssert(with: request) { result in - switch result { case let .success(data): assertSnapshot(matching: data, as: .dump) diff --git a/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift b/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift index bd02e58e..86a8c04a 100644 --- a/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift +++ b/Tests/KlaviyoSwiftTests/NetworkSessionTests.swift @@ -9,8 +9,8 @@ import SnapshotTesting import XCTest +@MainActor class NetworkSessionTests: XCTestCase { - @MainActor override func setUpWithError() throws { environment = KlaviyoEnvironment.test() } diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt index 4118dc3a..fafe69a5 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithEvent.1.txt @@ -11,7 +11,7 @@ ▿ allHTTPHeaderFields: Optional> ▿ some: 1 key/value pair ▿ (2 elements) - - key: "X-Klaviyo-Retry-Attempt" + - key: "X-Klaviyo-Attempt-Count" - value: "0/50" ▿ httpBody: Optional - some: 0 bytes diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt index d45fd287..9563cd57 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithProfile.1.txt @@ -11,7 +11,7 @@ ▿ allHTTPHeaderFields: Optional> ▿ some: 1 key/value pair ▿ (2 elements) - - key: "X-Klaviyo-Retry-Attempt" + - key: "X-Klaviyo-Attempt-Count" - value: "0/50" ▿ httpBody: Optional - some: 0 bytes diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt index 8423a2aa..b80d173f 100644 --- a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoAPITests/testSuccessfulResponseWithStoreToken.1.txt @@ -11,7 +11,7 @@ ▿ allHTTPHeaderFields: Optional> ▿ some: 1 key/value pair ▿ (2 elements) - - key: "X-Klaviyo-Retry-Attempt" + - key: "X-Klaviyo-Attempt-Count" - value: "0/50" ▿ httpBody: Optional - some: 0 bytes diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt new file mode 100644 index 00000000..7be1f88b --- /dev/null +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testLoadNewKlaviyoState.1.txt @@ -0,0 +1,18 @@ +▿ KlaviyoState + ▿ anonymousId: Optional + - some: "00000000-0000-0000-0000-000000000001" + ▿ apiKey: Optional + - some: "foo" + - email: Optional.none + - externalId: Optional.none + - flushInterval: 10.0 + - flushing: false + - initalizationState: InitializationState.uninitialized + - pendingProfile: Optional>.none + - pendingRequests: 0 elements + - phoneNumber: Optional.none + - pushTokenData: Optional.none + - queue: 0 elements + - requestsInFlight: 0 elements + ▿ retryInfo: RetryInfo + - retry: 1 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt new file mode 100644 index 00000000..7be1f88b --- /dev/null +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidData.1.txt @@ -0,0 +1,18 @@ +▿ KlaviyoState + ▿ anonymousId: Optional + - some: "00000000-0000-0000-0000-000000000001" + ▿ apiKey: Optional + - some: "foo" + - email: Optional.none + - externalId: Optional.none + - flushInterval: 10.0 + - flushing: false + - initalizationState: InitializationState.uninitialized + - pendingProfile: Optional>.none + - pendingRequests: 0 elements + - phoneNumber: Optional.none + - pushTokenData: Optional.none + - queue: 0 elements + - requestsInFlight: 0 elements + ▿ retryInfo: RetryInfo + - retry: 1 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt new file mode 100644 index 00000000..7be1f88b --- /dev/null +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testStateFileExistsInvalidJSON.1.txt @@ -0,0 +1,18 @@ +▿ KlaviyoState + ▿ anonymousId: Optional + - some: "00000000-0000-0000-0000-000000000001" + ▿ apiKey: Optional + - some: "foo" + - email: Optional.none + - externalId: Optional.none + - flushInterval: 10.0 + - flushing: false + - initalizationState: InitializationState.uninitialized + - pendingProfile: Optional>.none + - pendingRequests: 0 elements + - phoneNumber: Optional.none + - pushTokenData: Optional.none + - queue: 0 elements + - requestsInFlight: 0 elements + ▿ retryInfo: RetryInfo + - retry: 1 diff --git a/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt new file mode 100644 index 00000000..7be1f88b --- /dev/null +++ b/Tests/KlaviyoSwiftTests/__Snapshots__/KlaviyoStateTests/testValidStateFileExists.1.txt @@ -0,0 +1,18 @@ +▿ KlaviyoState + ▿ anonymousId: Optional + - some: "00000000-0000-0000-0000-000000000001" + ▿ apiKey: Optional + - some: "foo" + - email: Optional.none + - externalId: Optional.none + - flushInterval: 10.0 + - flushing: false + - initalizationState: InitializationState.uninitialized + - pendingProfile: Optional>.none + - pendingRequests: 0 elements + - phoneNumber: Optional.none + - pushTokenData: Optional.none + - queue: 0 elements + - requestsInFlight: 0 elements + ▿ retryInfo: RetryInfo + - retry: 1