Repository: RevenueCat/purchases-ios
Pull request #4862: chore: `EventsManagerIntegrationTests` working as expected
Author: login: facumenzella
Labels: name: enhancement
name: pr:other
Created: 2025-03-10T12:16:09Z | Merged: 2025-03-11T12:08:20Z

### Motivation
We need to be sure events are working as expected =)

### Description
I've tweaked the test to just call Purchases api. The only caveat is that the tracking for customer center async, and the one for paywalls is not, so I had to add an arbitrary sleep. Once this is merged, I can go with https://github.com/RevenueCat/purchases-ios/pull/4837

This will enable further refactors for PaywallEventsManager, like renaming it to support `FeatureEvent`
Test is only ignored in the offline test plan



Diff:
diff --git a/BackendIntegrationTests/BackendIntegrationTests-All-CI.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-All-CI.xctestplan
index 6321effc7e..a59888dfef 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-All-CI.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-All-CI.xctestplan
@@ -37,9 +37,6 @@
   },
   "testTargets" : [
     {
-      "skippedTests" : [
-         "EventsManagerIntegrationTests"
-       ],
       "target" : {
         "containerPath" : "container:RevenueCat.xcodeproj",
         "identifier" : "2DE20B6B264087FB004C597D",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-All.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-All.xctestplan
index 16781cf5af..a617b07114 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-All.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-All.xctestplan
@@ -33,9 +33,6 @@
   },
   "testTargets" : [
     {
-      "skippedTests" : [
-        "EventsManagerIntegrationTests"
-      ],
       "target" : {
         "containerPath" : "container:RevenueCat.xcodeproj",
         "identifier" : "2DE20B6B264087FB004C597D",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-CustomEntitlements.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-CustomEntitlements.xctestplan
index 132a54cf80..d6f737eaec 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-CustomEntitlements.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-CustomEntitlements.xctestplan
@@ -37,9 +37,6 @@
   },
   "testTargets" : [
     {
-      "skippedTests" : [
-         "EventsManagerIntegrationTests"
-       ],
       "target" : {
         "containerPath" : "container:RevenueCat.xcodeproj",
         "identifier" : "4F6BEE042A27B02400CD9322",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-Offline.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-Offline.xctestplan
index 8d3d2784f4..f5818b2d95 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-Offline.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-Offline.xctestplan
@@ -45,7 +45,7 @@
         "DisabledSignatureVerificationIntegrationTests",
         "DynamicModeSignatureVerificationIntegrationTests",
         "EnforcedSignatureVerificationIntegrationTests",
-        "EventsManagerIntegrationTests", 
+	"EventsManagerIntegrationTests",
         "InformationalSignatureVerificationIntegrationTests",
         "LoadShedderSignatureVerificationWithoutHeaderHashIntegrationTests",
         "LoadShedderStoreKit1IntegrationTests",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-Other.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-Other.xctestplan
index 69ed8487c1..b923468622 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-Other.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-Other.xctestplan
@@ -43,7 +43,6 @@
         "LoadShedderStoreKit1IntegrationTests",
         "LoadShedderStoreKit2IntegrationTests",
         "LoadShedderStoreKit2JWSIntegrationTests",
-	"EventsManagerIntegrationTests",
         "OfflineStoreKit1IntegrationTests",
         "OfflineStoreKit2IntegrationTests",
         "OfflineStoreKit2JWSIntegrationTests",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-SK1.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-SK1.xctestplan
index d3b245c36c..965e8977b7 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-SK1.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-SK1.xctestplan
@@ -46,7 +46,6 @@
         "DisabledSignatureVerificationIntegrationTests",
         "DynamicModeSignatureVerificationIntegrationTests",
         "EnforcedSignatureVerificationIntegrationTests",
-	"EventsManagerIntegrationTests",
         "InformationalSignatureVerificationIntegrationTests",
         "LoadShedderSignatureVerificationWithoutHeaderHashIntegrationTests",
         "LoadShedderStoreKit2IntegrationTests",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-SK2.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-SK2.xctestplan
index f13709193f..a6a5a5eee6 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-SK2.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-SK2.xctestplan
@@ -46,7 +46,6 @@
         "DisabledSignatureVerificationIntegrationTests",
         "DynamicModeSignatureVerificationIntegrationTests",
         "EnforcedSignatureVerificationIntegrationTests",
-	"EventsManagerIntegrationTests",
         "InformationalSignatureVerificationIntegrationTests",
         "LoadShedderSignatureVerificationWithoutHeaderHashIntegrationTests",
         "LoadShedderStoreKit1IntegrationTests",
diff --git a/BackendIntegrationTests/BackendIntegrationTests-Shedder.xctestplan b/BackendIntegrationTests/BackendIntegrationTests-Shedder.xctestplan
index 5b5331ba70..c1f1bf5369 100644
--- a/BackendIntegrationTests/BackendIntegrationTests-Shedder.xctestplan
+++ b/BackendIntegrationTests/BackendIntegrationTests-Shedder.xctestplan
@@ -46,7 +46,6 @@
         "DisabledSignatureVerificationIntegrationTests",
         "DynamicModeSignatureVerificationIntegrationTests",
         "EnforcedSignatureVerificationIntegrationTests",
-	"EventsManagerIntegrationTests",
         "InformationalSignatureVerificationIntegrationTests",
         "OfflineStoreKit1IntegrationTests",
         "OfflineStoreKit2IntegrationTests",
diff --git a/Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift b/Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
index 0ca04c5554..d5ee9406d7 100644
--- a/Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
+++ b/Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
@@ -23,10 +23,6 @@ import XCTest
 @MainActor
 final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
 
-    override var apiKey: String { return Constants.customEntitlementComputationApiKey }
-
-    var eventsManager: PaywallEventsManager!
-
     func testPostingPaywallsDoesNotFail() async throws {
         let events = [
             PaywallEvent.cancel(
@@ -41,28 +37,19 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
         ]
 
         for event in events {
-            await eventsManager.track(
-                featureEvent: event
+            await Purchases.shared.track(
+                paywallEvent: event
             )
         }
 
-        _ = try await eventsManager.flushEvents(count: 100)
-
-        self.logger.verifyMessageWasLogged(
-            Strings.paywalls.event_flush_starting(count: events.count)
-        )
-
-        try self.logger.verifyMessageWasLogged(
-            Strings.analytics.flush_events_success,
-            level: .debug,
-            expectedCount: 1
-        )
+        try await flushAndVerify(eventsCount: events.count)
     }
 
     func testPostingCustomerCenterDoesNotFail() async throws {
         let locale = Locale(identifier: "es_ES")
-        await eventsManager.track(
-            featureEvent: CustomerCenterEvent.impression(
+
+        Purchases.shared.track(
+            customerCenterEvent: CustomerCenterEvent.impression(
                 Self.customerCenterCreationData,
                 CustomerCenterEvent.Data(
                     locale: locale,
@@ -73,8 +60,8 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
             )
         )
 
-        await eventsManager.track(
-            featureEvent: CustomerCenterAnswerSubmittedEvent.answerSubmitted(
+        Purchases.shared.track(
+            customerCenterEvent: CustomerCenterAnswerSubmittedEvent.answerSubmitted(
                 Self.customerCenterCreationData,
                 CustomerCenterAnswerSubmittedEvent.Data(
                     locale: locale,
@@ -89,14 +76,27 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
                 )
             )
         )
+        // give background task a chance to run
+        await Task.yield()
+
+        try await self.logger.verifyMessageIsEventuallyLogged(
+            "Storing event:",
+            expectedCount: 2,
+            timeout: .seconds(3),
+            pollInterval: .seconds(1)
+        )
 
-        _ = try await eventsManager.flushEvents(count: 2)
+        try await flushAndVerify(eventsCount: 2)
+    }
+
+    private func flushAndVerify(eventsCount: Int) async throws {
+        _ = try await Purchases.shared.flushPaywallEvents(count: 2)
 
         self.logger.verifyMessageWasLogged(
             Strings.paywalls.event_flush_starting(count: 2)
         )
 
-        try self.logger.verifyMessageWasLogged(
+        self.logger.verifyMessageWasLogged(
             Strings.analytics.flush_events_success,
             level: .debug,
             expectedCount: 1
diff --git a/Tests/UnitTests/TestHelpers/TestLogHandler.swift b/Tests/UnitTests/TestHelpers/TestLogHandler.swift
index f23dbec6b7..79f75cbe88 100644
--- a/Tests/UnitTests/TestHelpers/TestLogHandler.swift
+++ b/Tests/UnitTests/TestHelpers/TestLogHandler.swift
@@ -128,7 +128,7 @@ extension TestLogHandler {
     }
 
     func verifyMessageIsEventuallyLogged(
-        _ message: String,
+        _ message: CustomStringConvertible,
         level: LogLevel? = nil,
         expectedCount: Int? = nil,
         timeout: DispatchTimeInterval = AsyncDefaults.timeout,


Reviews:
user: login: JayShortway
body: More testing is more better! Just a comment on reducing the risk of flakiness. 
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T14:33:24Z
user: login: facumenzella
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T15:16:41Z
user: login: JayShortway
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-10T17:34:26Z
user: login: JayShortway
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-10T17:34:46Z

Review comments:
diff_hunk: @@ -90,13 +77,22 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
             )
         )
 
-        _ = try await eventsManager.flushEvents(count: 2)
+        try await flushAndVerify(eventsCount: 2, trackingIsAsync: true)
+    }
+
+    private func flushAndVerify(eventsCount: Int, trackingIsAsync: Bool = false) async throws {
+        if trackingIsAsync {
+            // tracking customer center is async
+            try await Task.sleep(nanoseconds: 1_000_000_000)
+        }
path: Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
user: login: JayShortway
body: This could add flakiness to our tests. Maybe we can reduce that risk by polling with a timeout instead? E.g. verifying the message was logged every X milliseconds, with a timeout of Y seconds? 
created_at: 2025-03-10T14:32:26Z
author_association: MEMBER
diff_hunk: @@ -90,13 +77,22 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
             )
         )
 
-        _ = try await eventsManager.flushEvents(count: 2)
+        try await flushAndVerify(eventsCount: 2, trackingIsAsync: true)
+    }
+
+    private func flushAndVerify(eventsCount: Int, trackingIsAsync: Bool = false) async throws {
+        if trackingIsAsync {
+            // tracking customer center is async
+            try await Task.sleep(nanoseconds: 1_000_000_000)
+        }
path: Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
user: login: facumenzella
body: You're right. I actually found a way to do it. Check 07bf34347

The tricky part is that we need a `Task.yield()` to let the background task run, but seems to work and is definitely less flaky
created_at: 2025-03-10T15:16:41Z
author_association: MEMBER
diff_hunk: @@ -90,13 +77,22 @@ final class EventsManagerIntegrationTests: BaseBackendIntegrationTests {
             )
         )
 
-        _ = try await eventsManager.flushEvents(count: 2)
+        try await flushAndVerify(eventsCount: 2, trackingIsAsync: true)
+    }
+
+    private func flushAndVerify(eventsCount: Int, trackingIsAsync: Bool = false) async throws {
+        if trackingIsAsync {
+            // tracking customer center is async
+            try await Task.sleep(nanoseconds: 1_000_000_000)
+        }
path: Tests/BackendIntegrationTests/EventsManagerIntegrationTests.swift
user: login: JayShortway
body: Very nice!! 🙌 
created_at: 2025-03-10T17:34:46Z
author_association: MEMBER

PR comments:
user: login: facumenzella
created_at: 2025-03-10T12:16:22Z
body: @RCGitBot please test
author_association: MEMBER
user: login: facumenzella
created_at: 2025-03-10T12:49:28Z
body: @RCGitBot please test
author_association: MEMBER
user: login: facumenzella
created_at: 2025-03-10T15:16:48Z
body: @RCGitBot please test
author_association: MEMBER