From 0b238d8081c97ba5c090d85ea6fe8e3e8640ba50 Mon Sep 17 00:00:00 2001 From: Megha Date: Tue, 4 Feb 2025 19:17:47 +0530 Subject: [PATCH 1/2] Fix AUT issue where multiple users are created and merge --- swift-sdk/Internal/AnonymousUserManager.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/swift-sdk/Internal/AnonymousUserManager.swift b/swift-sdk/Internal/AnonymousUserManager.swift index 0e1f31ff..f9d013ca 100644 --- a/swift-sdk/Internal/AnonymousUserManager.swift +++ b/swift-sdk/Internal/AnonymousUserManager.swift @@ -28,6 +28,7 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol { private let notificationStateProvider: NotificationStateProviderProtocol private var config: IterableConfig private(set) var lastCriteriaFetch: Double = 0 + private var isCriteriaMatched = false /// Tracks an anonymous event and store it locally public func trackAnonEvent(name: String, dataFields: [AnyHashable: Any]?) { @@ -182,6 +183,7 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol { dataFields: self.localStorage.anonymousUserUpdate, requestJson: anonSessions ).onError { error in + self.isCriteriaMatched = false if error.httpStatusCode == 409 { self.getAnonCriteria() // refetch the criteria } @@ -229,7 +231,8 @@ public class AnonymousUserManager: AnonymousUserManagerProtocol { processAndStoreEvent(type: type, data: data) } - if let criteriaId = evaluateCriteriaAndReturnID() { + if let criteriaId = evaluateCriteriaAndReturnID(), !isCriteriaMatched { + isCriteriaMatched = true createAnonymousUser(criteriaId) } } From eeef2bcebd068713881f4ef80daf31457c3c7a3c Mon Sep 17 00:00:00 2001 From: Evan Greer Date: Fri, 14 Feb 2025 15:08:58 -0700 Subject: [PATCH 2/2] adds unit test --- .../IterableApiCriteriaFetchTests.swift | 6 +-- .../unit-tests/UserMergeScenariosTests.swift | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/tests/unit-tests/IterableApiCriteriaFetchTests.swift b/tests/unit-tests/IterableApiCriteriaFetchTests.swift index ee8fe568..794ac9f0 100644 --- a/tests/unit-tests/IterableApiCriteriaFetchTests.swift +++ b/tests/unit-tests/IterableApiCriteriaFetchTests.swift @@ -49,7 +49,7 @@ class IterableApiCriteriaFetchTests: XCTestCase { let config = IterableConfig() config.enableAnonActivation = true - config.enableOnForegroundCriteriaFetching = true + config.enableForegroundCriteriaFetch = true IterableAPI.initializeForTesting(apiKey: IterableApiCriteriaFetchTests.apiKey, config: config, @@ -85,7 +85,7 @@ class IterableApiCriteriaFetchTests: XCTestCase { let config = IterableConfig() config.enableAnonActivation = true - config.enableOnForegroundCriteriaFetching = false + config.enableForegroundCriteriaFetch = false internalApi = InternalIterableAPI.initializeForTesting( config: config, @@ -124,7 +124,7 @@ class IterableApiCriteriaFetchTests: XCTestCase { let config = IterableConfig() config.enableAnonActivation = true - config.enableOnForegroundCriteriaFetching = true + config.enableForegroundCriteriaFetch = true IterableAPI.initializeForTesting(apiKey: IterableApiCriteriaFetchTests.apiKey, config: config, diff --git a/tests/unit-tests/UserMergeScenariosTests.swift b/tests/unit-tests/UserMergeScenariosTests.swift index 91888cee..64a38f51 100644 --- a/tests/unit-tests/UserMergeScenariosTests.swift +++ b/tests/unit-tests/UserMergeScenariosTests.swift @@ -974,6 +974,49 @@ class UserMergeScenariosTests: XCTestCase, AuthProvider { waitForExpectations(timeout: 5, handler: nil) } + + func testCriteriaMetTwice() { + let config = IterableConfig() + config.enableAnonActivation = true + + let mockSession = MockNetworkSession() + + IterableAPI.initializeForTesting(apiKey: UserMergeScenariosTests.apiKey, + config: config, + networkSession: mockSession, + localStorage: localStorage) + + IterableAPI.logoutUser() + guard let jsonData = mockData.data(using: .utf8) else { return } + localStorage.criteriaData = jsonData + + IterableAPI.track(event: "testEvent") + IterableAPI.track(event: "testEvent") + + waitForDuration(seconds: 3) + + if let anonUser = localStorage.userIdAnnon { + XCTAssertFalse(anonUser.isEmpty, "Expected anon user nil") + } else { + XCTFail("Expected anon user nil but found") + } + + // Verify that anon session request was made exactly once + let anonSessionRequest = mockSession.getRequest(withEndPoint: Const.Path.trackAnonSession) + XCTAssertNotNil(anonSessionRequest, "Anonymous session request should not be nil") + + // Count total requests with anon session endpoint + let anonSessionRequests = mockSession.requests.filter { request in + request.url?.absoluteString.contains(Const.Path.trackAnonSession) == true + } + XCTAssertEqual(anonSessionRequests.count, 1, "Anonymous session should be called exactly once") + + // Verify track events were made + let trackRequests = mockSession.requests.filter { request in + request.url?.absoluteString.contains(Const.Path.trackEvent) == true + } + XCTAssertEqual(trackRequests.count, 2, "Track event should be called twice") + } }