Commit Graph
5 Commits
Author SHA1 Message Date
ac81308e33 Support for flushing non subscription in the Events Manager (#5726)
* Refactor: Extract flushEvents logic into flushFeatureEvents

Separates feature event flushing into its own method:
- Add flushFeatureEvents to EventsManagerType protocol
- Implement flushFeatureEvents with existing flush logic
- Update flushEvents to delegate to flushFeatureEvents
- Update Purchases.flushPaywallEvents to call flushFeatureEvents
- Add flushFeatureEvents to MockEventsManager

This prepares EventsManager for supporting multiple event types.

* Add ad event storage and serialization

Adds core infrastructure for storing and serializing ad events:
- StoredAdEvent: Model for ad events with user context
- StoredAdEventSerializer: JSON serialization/deserialization
- AdEventStore: File-based persistent storage with fetch/clear operations
- AdEventStoreTests: Comprehensive test coverage

Clean implementation without legacy migration code since feature is behind compiler flag.

* Add ad events integration to EventsManager

Implements complete ad events lifecycle in EventsManager:
- Add flushAdEvents to EventsManagerType protocol
- AdEventStore integration with new initialization parameter
- Implement track(adEvent:) with proper serialization and storage
- Update flushEvents to flush both feature and ad events
- Network request handling via InternalAPI.postAdEvents
- Comprehensive error handling and logging (EventsManagerStrings)
- Full test coverage (EventsManagerAdEventsTests)
- Mock support for ad events in test infrastructure

Network layer additions:
- AdEventsRequest: HTTP request body for ad events endpoint
- PostAdEventsOperation: Network operation for posting ad events
- InternalAPI.postAdEvents: Async method for posting ad events

* Rename adInstanceId to impressionId to match backend schema

The backend expects `impression_id` but we were sending `ad_instance_id`.
This renames the field throughout the codebase:
- AdEvent.swift: Update AdEventData protocol and all event classes
- AdEventsRequest.swift: Update serialization field name
- All test files: Update to use impressionId parameter
- Objective-C API: Update parameter names

The field will be serialized as `impression_id` via snake_case conversion.

* Add AdEventsIntegrationTests and internal flush method

- Add AdEventsIntegrationTests.swift following BaseBackendIntegrationTests pattern
- Add internal flushAdEvents() method in Purchases for test access
- Add 4 test cases: tracking/flushing, empty flush, clearing after flush, persistence

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Clarify side-effects of removing the file

Co-authored-by: JayShortway <29483617+JayShortway@users.noreply.github.com>

* Remove test-only flushAdEvents method from Purchases

Refactor to expose eventsManager as internal instead of having a test-only wrapper method.

Changes:
- Make eventsManager internal instead of private in Purchases
- Remove flushAdEvents wrapper method from Purchases
- Update integration tests to access eventsManager.flushAdEvents() directly

This keeps test-only code out of the Purchases API while maintaining test access via @testable import.

* Simplify StoredAdEvent equality with sorted keys

Use JSONEncoder with sortedKeys to ensure consistent JSON encoding, allowing
StoredAdEvent to rely on automatic Equatable synthesis instead of complex
JSON decoding and NSDictionary comparison.

Benefits:
- Cleaner code: removed 18 lines of custom equality logic
- Better performance: direct string comparison vs JSON decoding
- More maintainable: automatic Equatable synthesis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Refactor ad events integration tests to use app lifecycle events instead of internal API

- Remove dependency on internal eventsManager property
- Revert eventsManager back to private access level
- Tests now simulate app backgrounding to trigger automatic event flushing
- Aligns with production behavior where events flush on app lifecycle events
- No public API needed for testing

* Improve testSizeLimitTriggersCleanup with stronger assertions

- Add event identity verification to ensure the new event is retained after cleanup
- Replace count-only assertions with exact event equality checks
- Add logging verification to confirm cleanup was triggered
- Remove unused mock reset code
- Improve variable naming for clarity (overflowedEvents -> remainingEvents)

* Replace Task.sleep with deterministic message waiting in ad events tests

Test improvements:
- Replace arbitrary Task.sleep() delays with await logger.waitForMessage()
- Wait for specific log messages instead of fixed time delays
- Add allowNoMessages parameter where needed for empty flush scenarios

TestLogHandler enhancements:
- Add waitForMessage() async method for deterministic waiting
- Support task cancellation with proper cleanup
- Resume waiting continuations when matching messages arrive
- Change EntryCondition from private to fileprivate for internal usage

This eliminates race conditions and makes tests more reliable by waiting
for actual events rather than guessing at timing delays.

* Replace waitForMessage with verifyMessageIsEventuallyLogged in ad events tests

Test changes:
- Replace custom event-driven waitForMessage() with Nimble's verifyMessageIsEventuallyLogged()
- Use established polling-based assertion pattern consistent with rest of test suite
- All 5 call sites in AdEventsIntegrationTests updated to use try await

TestLogHandler cleanup:
- Remove waitForMessage() method and continuation-based waiting infrastructure
- Remove PendingWait struct, pendingWaits property, and related helper methods
- Remove continuation resumption logic from didReceive()
- Change EntryCondition back to private

This simplifies the test helper while using the existing, proven polling approach.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: JayShortway <29483617+JayShortway@users.noreply.github.com>
2025-11-14 11:19:08 +01:00
Facundo Menzella fa3ea3264d feat: Add tests for customer center events encoding (#4739) 2025-02-03 12:17:31 +01:00
NachoSoto 4187316738 Encodable.jsonEncodedData: fixed tests on iOS 17 due to inconsistent key ordering (#2607)
A bunch of tests were failing on Xcode 15 due to [a change in
Swift](https://github.com/apple/swift/issues/66466), because we relying
on being able to verify that the encoded `Data` saved to caches is
consistent.
Starting on Swift 5.9 `JSONEncoder` has optimizations that mean we can't
rely on it.

To fix this, this PR does 2 things:
- Changed `JSONEncoder.prettyPrinted` to have `.sortedKeys`
- Changed test to verify the decoded response `struct`, not the raw
encoded `Data`

Note that I didn't make `JSONEncoder.default` use `.sortedKeys` because
of the potential performance impact. `Encodable.prettyPrintedData` is
used in tests like `ReceiptFetcherTests` (which this PR fixes) and for
debug purposes, but `Encodable.encodedJSON` is used everywhere.
2023-06-09 11:30:59 -07:00
NachoSoto 2276d5c2ce AppleReceipt.debugDescription: don't pretty-print JSON (#2564) 2023-05-31 07:11:23 -07:00
NachoSoto b3875a9380 Exposed PurchasesReceiptParser and AppleReceipt (#2153)
Initial step for [CSDK-17]. Fixes [CSDK-523].

### Changes:
- Renamed `ReceiptParser` to `PurchasesReceiptParser` to avoid name
collision with future SPM `ReceiptParser`
- Exposed `ReceiptParser`, `AppleReceipt`,
`PurchasesReceiptParser.Error`
- Added new types to `SwiftAPITester`
- Added docs to these types
- Moved `LoggerType` to be available to `ReceiptParser`
- Moved `DateFormatter+Extensions` to be available to `ReceiptParser`
- Moved `Codable+Extensions` to be available to `ReceiptParser`
- Created `ReceiptParserLogger` for `ReceiptParser`


[CSDK-17]:
https://revenuecats.atlassian.net/browse/CSDK-17?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

[CSDK-523]:
https://revenuecats.atlassian.net/browse/CSDK-523?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
2022-12-22 10:09:12 -08:00