From d2bee23e5004abb054c1d4726fada565e42aa090 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 28 Jul 2025 19:27:57 -0300 Subject: [PATCH 01/15] Test added --- .../Splits/PersistentSplitsStorage.swift | 6 +- Split/Storage/Splits/SplitsStorage.swift | 6 +- .../Storage/PersistentSplitsStorageStub.swift | 2 +- SplitTests/Helpers/SplitTestHelper.swift | 121 +--------------- .../Api/SplitIntegrationTest.swift | 2 +- .../streaming/MySegmentUpdateTest.swift | 129 +++++++++++++++--- 6 files changed, 118 insertions(+), 148 deletions(-) diff --git a/Split/Storage/Splits/PersistentSplitsStorage.swift b/Split/Storage/Splits/PersistentSplitsStorage.swift index 2345e201..e6faf256 100644 --- a/Split/Storage/Splits/PersistentSplitsStorage.swift +++ b/Split/Storage/Splits/PersistentSplitsStorage.swift @@ -17,7 +17,7 @@ protocol PersistentSplitsStorage { func getSplitsSnapshot() -> SplitsSnapshot func getChangeNumber() -> Int64 func getUpdateTimestamp() -> Int64 - func getSegmentsInUse() -> Int64 + func getSegmentsInUse() -> Int64? func getAll() -> [Split] func delete(splitNames: [String]) func clear() @@ -64,8 +64,8 @@ class DefaultPersistentSplitsStorage: PersistentSplitsStorage { return generalInfoDao.stringValue(info: .flagsSpec) ?? "" } - func getSegmentsInUse() -> Int64 { - generalInfoDao.longValue(info: .segmentsInUse) ?? 0 + func getSegmentsInUse() -> Int64? { + generalInfoDao.longValue(info: .segmentsInUse) } func update(bySetsFilter filter: SplitFilter?) { diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index bf78c6e7..81acd939 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -51,7 +51,7 @@ class DefaultSplitsStorage: SplitsStorage { func loadLocal() { // Ensure count of Flags with Segments (for optimization feature) - segmentsInUse = persistentStorage.getSegmentsInUse() + segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 defer { persistentStorage.update(segmentsInUse: segmentsInUse) } let snapshot = persistentStorage.getSplitsSnapshot() @@ -98,7 +98,7 @@ class DefaultSplitsStorage: SplitsStorage { func update(splitChange: ProcessedSplitChange) -> Bool { // Ensure count of Flags with Segments (for optimization feature) - segmentsInUse = persistentStorage.getSegmentsInUse() + segmentsInUse = persistentStorage.getSegmentsInUse() ?? 0 defer { persistentStorage.update(segmentsInUse: segmentsInUse) } // Process @@ -145,6 +145,7 @@ class DefaultSplitsStorage: SplitsStorage { var splitsRemoved = false for split in splits { + guard let splitName = split.name?.lowercased() else { Logger.e("Invalid feature flag name received while updating feature flags") continue @@ -168,6 +169,7 @@ class DefaultSplitsStorage: SplitsStorage { segmentsInUse += 1 } else if inMemorySplits.value(forKey: splitName) != nil && !active { // If known Split and archived segmentsInUse -= 1 + } } diff --git a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift index 4e00c8be..da459b14 100644 --- a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift @@ -93,7 +93,7 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { self.segmentsInUse = segmentsInUse } - func getSegmentsInUse() -> Int64 { + func getSegmentsInUse() -> Int64? { segmentsInUse } } diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index f1fdf9da..5a37c5ae 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -177,126 +177,7 @@ class SplitTestHelper { } static func newSplit(name: String, trafficType: String) -> Split { - - let splitJSONExample = """ - { - "trafficTypeName":"\(trafficType)", - "name": "\(name)\", - "trafficAllocation":59, - "trafficAllocationSeed":-2108186082, - "seed":-1947050785, - "status":"ACTIVE", - "killed":false, - "defaultTreatment":"off", - "changeNumber":1506703262916, - "algo":2, - "conditions":[ - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "nico_test", - "othertest" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"WHITELIST", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":null, - "matcherType":"WHITELIST", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":{ - "whitelist":[ - "bla" - ] - }, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"off", - "size":100 - } - ], - "label":"whitelisted" - }, - { - "conditionType":"ROLLOUT", - "matcherGroup":{ - "combiner":"AND", - "matchers":[ - { - "keySelector":{ - "trafficType":"account", - "attribute":null - }, - "matcherType":"ALL_KEYS", - "negate":false, - "userDefinedSegmentMatcherData":null, - "whitelistMatcherData":null, - "unaryNumericMatcherData":null, - "betweenMatcherData":null, - "booleanMatcherData":null, - "dependencyMatcherData":null, - "stringMatcherData":null - } - ] - }, - "partitions":[ - { - "treatment":"on", - "size":0 - }, - { - "treatment":"off", - "size":100 - }, - { - "treatment":"visa", - "size":0 - } - ], - "label":"in segment all" - } - ] - } - """ - - let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: splitJSONExample) + let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: "") split.isCompletelyParsed = false return split } diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 148cd5cd..7e8f6dd2 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -108,7 +108,7 @@ class SplitIntegrationTests: XCTestCase { splitConfig.logLevel = TestingHelper.testLogLevel splitConfig.impressionsMode = "DEBUG" splitConfig.serviceEndpoints = ServiceEndpoints.builder() - .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() + .set(sdkEndpoint: "localhost").set(eventsEndpoint: "localhost").build() splitConfig.impressionListener = { impression in impressions[IntegrationHelper.buildImpressionKey(impression: impression)] = impression diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index eaccb54d..4adca76d 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -125,14 +125,13 @@ class MySegmentUpdateTest: XCTestCase { let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") let membershipsHit = XCTestExpectation(description: "/memberships should be hit multiple times") - //MARK: This is the key part of this test + //MARK: Key part membershipsHit.expectedFulfillmentCount = 4 - // Configure dispatcher here + // 1. Configure dispatcher let dispatcher: HttpClientTestDispatcher = { request in if request.url.absoluteString.contains("/splitChanges") { - // Send splitChanges using Segments - let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) // send splitChanges with Segments return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) } @@ -144,7 +143,7 @@ class MySegmentUpdateTest: XCTestCase { return TestDispatcherResponse(code: 200) } - // Setup Factory, Network & Client + // 2. Setup Factory, Network & Client let testFactory = TestSplitFactory(userKey: userKey) testFactory.createHttpClient(dispatcher: dispatcher, streamingHandler: buildStreamingHandler()) try testFactory.buildSdk(polling: true) @@ -158,13 +157,11 @@ class MySegmentUpdateTest: XCTestCase { wait(for: [segmentsHit], timeout: 3) XCTAssertEqual(sdkReadyFired, false) + // 3. Test wait(for: [sdkReady, membershipsHit], timeout: 20) - let semaphore = DispatchSemaphore(value: 0) - client.destroy { - semaphore.signal() - } - semaphore.wait() + // Cleanup + destroy(client) } func testSdkAvoidsMembershipsIfNoSegmentsAreUsed() throws { @@ -176,11 +173,10 @@ class MySegmentUpdateTest: XCTestCase { let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") var membershipsHit = 0 - // Configure dispatcher here + // 1. Configure dispatcher let dispatcher: HttpClientTestDispatcher = { request in if request.url.absoluteString.contains("/splitChanges") { - // Send splitChanges using Segments - let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtihout Segments return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) } @@ -193,7 +189,7 @@ class MySegmentUpdateTest: XCTestCase { return TestDispatcherResponse(code: 200) } - // Setup Factory, Network & Client + // 2. Setup Factory, Network & Client let testFactory = TestSplitFactory(userKey: userKey) testFactory.createHttpClient(dispatcher: dispatcher, streamingHandler: buildStreamingHandler()) try testFactory.buildSdk(polling: true) @@ -207,19 +203,102 @@ class MySegmentUpdateTest: XCTestCase { wait(for: [segmentsHit], timeout: 3) XCTAssertEqual(sdkReadyFired, false) - // Imverted expectation + // Inverted expectation let waitExp = XCTestExpectation(description: "Just waiting") waitExp.isInverted = true wait(for: [waitExp], timeout: 15) - // MARK: Key part of the test: After 15 seconds it hit /memberships just once - XCTAssertEqual(membershipsHit, 1) + // MARK: Key part + XCTAssertEqual(membershipsHit, 1, "After 15 seconds it should hit /memberships just once") - let semaphore = DispatchSemaphore(value: 0) - client.destroy { - semaphore.signal() + // Cleanup + destroy(client) + } + + func testSdkAvoidsMembershipsIfNoSegmentsAreUsedFromCache() throws { + + var sdkReadyFired = false + var cacheReadyFired = true + let sdkReady = XCTestExpectation(description: "SDK should be ready") + let cacheReadyExp = XCTestExpectation(description: "Cache should be ready") + let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") + var membershipsHit = 0 + + // 1. Configure dispatcher + let dispatcher: HttpClientTestDispatcher = { request in + if request.url.absoluteString.contains("/splitChanges") { + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtihout Segments + return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) + } + + if request.url.absoluteString.contains("/memberships") { + segmentsHit.fulfill() + membershipsHit += 1 + return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) + } + + return TestDispatcherResponse(code: 200) + } + + // 2. Setup Factory, Network & Client + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 4 + splitConfig.segmentsRefreshRate = 4 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.eventsPerPush = 10 + splitConfig.streamingEnabled = false + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: "localhost").set(eventsEndpoint: "localhost").build() + + let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") + splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") + //splitDatabase.generalInfoDao.update(info: .segmentsInUse, longValue: 0) + let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) + splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) + + let userKey = "test-user-key" + let key: Key = Key(matchingKey: userKey, bucketingKey: nil) + let session = HttpSessionMock() + let reqManager = HttpRequestManagerTestDispatcher(dispatcher: dispatcher, streamingHandler: buildStreamingHandler()) + httpClient = DefaultHttpClient(session: session, requestManager: reqManager) + let builder = DefaultSplitFactoryBuilder() + + _ = builder.setTestDatabase(splitDatabase) + _ = builder.setHttpClient(httpClient) + var factory = builder.setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + + client?.on(event: .sdkReady) { + sdkReadyFired = true + sdkReady.fulfill() + } + + client?.on(event: .sdkReadyFromCache) { + cacheReadyExp.fulfill() + cacheReadyFired = true + } + + wait(for: [segmentsHit], timeout: 3) + XCTAssertEqual(sdkReadyFired, false) + + wait(for: [cacheReadyExp, sdkReady], timeout: 3) + + // MARK: Key part + let waitExp = XCTestExpectation(description: "Just waiting") + waitExp.isInverted = true // Inverted expectation + wait(for: [waitExp], timeout: 10) + + XCTAssertEqual(membershipsHit, 1, "After 15 seconds it should hit /memberships just once") + + // Cleanup + if let client = client { + destroy(client) } - semaphore.wait() } func testMySegmentsUpdateBounded() throws { @@ -452,4 +531,12 @@ class MySegmentUpdateTest: XCTestCase { msg = notificationTemplate.replacingOccurrences(of: kDataField, with: msg) streamingBinding?.push(message: msg) } + + fileprivate func destroy(_ client: SplitClient) { + let semaphore = DispatchSemaphore(value: 0) + client.destroy { + semaphore.signal() + } + semaphore.wait() + } } From 2f148be3a83e6822c6fe8422ab211ede579d6fc0 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 28 Jul 2025 19:40:05 -0300 Subject: [PATCH 02/15] Test added --- .../streaming/MySegmentUpdateTest.swift | 87 ++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 4adca76d..527d0253 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -257,7 +257,6 @@ class MySegmentUpdateTest: XCTestCase { let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") - //splitDatabase.generalInfoDao.update(info: .segmentsInUse, longValue: 0) let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) @@ -300,6 +299,92 @@ class MySegmentUpdateTest: XCTestCase { destroy(client) } } + + func testSdkHitsMembershipsIfSegmentsAreUsedFromCache() throws { + + var sdkReadyFired = false + var cacheReadyFired = true + let sdkReady = XCTestExpectation(description: "SDK should be ready") + let cacheReadyExp = XCTestExpectation(description: "Cache should be ready") + let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") + var membershipsHit = 0 + + // 1. Configure dispatcher + let dispatcher: HttpClientTestDispatcher = { request in + if request.url.absoluteString.contains("/splitChanges") { + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtih Segments + return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) + } + + if request.url.absoluteString.contains("/memberships") { + segmentsHit.fulfill() + membershipsHit += 1 + return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) + } + + return TestDispatcherResponse(code: 200) + } + + // 2. Setup Factory, Network & Client + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 4 + splitConfig.segmentsRefreshRate = 4 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.eventsPerPush = 10 + splitConfig.streamingEnabled = false + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: "localhost").set(eventsEndpoint: "localhost").build() + + let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") + splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") + splitDatabase.generalInfoDao.update(info: .segmentsInUse, longValue: 1) + let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) + splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) + + let userKey = "test-user-key" + let key: Key = Key(matchingKey: userKey, bucketingKey: nil) + let session = HttpSessionMock() + let reqManager = HttpRequestManagerTestDispatcher(dispatcher: dispatcher, streamingHandler: buildStreamingHandler()) + httpClient = DefaultHttpClient(session: session, requestManager: reqManager) + let builder = DefaultSplitFactoryBuilder() + + _ = builder.setTestDatabase(splitDatabase) + _ = builder.setHttpClient(httpClient) + var factory = builder.setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + + client?.on(event: .sdkReady) { + sdkReadyFired = true + sdkReady.fulfill() + } + + client?.on(event: .sdkReadyFromCache) { + cacheReadyExp.fulfill() + cacheReadyFired = true + } + + wait(for: [segmentsHit], timeout: 3) + XCTAssertEqual(sdkReadyFired, false) + + wait(for: [cacheReadyExp, sdkReady], timeout: 3) + + // MARK: Key part + let waitExp = XCTestExpectation(description: "Just waiting") + waitExp.isInverted = true // Inverted expectation + wait(for: [waitExp], timeout: 10) + + XCTAssertGreaterThan(membershipsHit, 2, "After 15 seconds, if segments are used, SDK should hit /memberships many times") + + // Cleanup + if let client = client { + destroy(client) + } + } func testMySegmentsUpdateBounded() throws { try mySegmentsUpdateBoundedTest(type: .mySegmentsUpdate) From 0897ca5a09eb06f46563f4d236afc784c6797939 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 28 Jul 2025 19:57:27 -0300 Subject: [PATCH 03/15] Test fixed --- .../Storage/PersistentSplitsStorageStub.swift | 3 +- SplitTests/Helpers/SplitTestHelper.swift | 120 +++++++++++++++++- 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift index da459b14..43729167 100644 --- a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift @@ -94,6 +94,7 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { } func getSegmentsInUse() -> Int64? { - segmentsInUse + getSegmentsInUseCalled = true + return segmentsInUse } } diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index 5a37c5ae..77ea6451 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -177,7 +177,125 @@ class SplitTestHelper { } static func newSplit(name: String, trafficType: String) -> Split { - let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: "") + let splitJSONExample = """ + { + "trafficTypeName":"\(trafficType)", + "name": "\(name)\", + "trafficAllocation":59, + "trafficAllocationSeed":-2108186082, + "seed":-1947050785, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off", + "changeNumber":1506703262916, + "algo":2, + "conditions":[ + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "nico_test", + "othertest" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "bla" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"off", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"ROLLOUT", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":{ + "trafficType":"account", + "attribute":null + }, + "matcherType":"ALL_KEYS", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":0 + }, + { + "treatment":"off", + "size":100 + }, + { + "treatment":"visa", + "size":0 + } + ], + "label":"in segment all" + } + ] + } + """ + + let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: splitJSONExample) split.isCompletelyParsed = false return split } From adf788f4be4b9d54f4711e90e653411fde14c0fc Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Mon, 28 Jul 2025 19:58:53 -0300 Subject: [PATCH 04/15] Tests fixed --- .../Storage/PersistentSplitsStorageStub.swift | 3 +- SplitTests/Helpers/SplitTestHelper.swift | 120 +++++++++++++++++- 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift index da459b14..43729167 100644 --- a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift @@ -94,6 +94,7 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { } func getSegmentsInUse() -> Int64? { - segmentsInUse + getSegmentsInUseCalled = true + return segmentsInUse } } diff --git a/SplitTests/Helpers/SplitTestHelper.swift b/SplitTests/Helpers/SplitTestHelper.swift index 5a37c5ae..77ea6451 100644 --- a/SplitTests/Helpers/SplitTestHelper.swift +++ b/SplitTests/Helpers/SplitTestHelper.swift @@ -177,7 +177,125 @@ class SplitTestHelper { } static func newSplit(name: String, trafficType: String) -> Split { - let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: "") + let splitJSONExample = """ + { + "trafficTypeName":"\(trafficType)", + "name": "\(name)\", + "trafficAllocation":59, + "trafficAllocationSeed":-2108186082, + "seed":-1947050785, + "status":"ACTIVE", + "killed":false, + "defaultTreatment":"off", + "changeNumber":1506703262916, + "algo":2, + "conditions":[ + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "nico_test", + "othertest" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"WHITELIST", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":null, + "matcherType":"WHITELIST", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":{ + "whitelist":[ + "bla" + ] + }, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"off", + "size":100 + } + ], + "label":"whitelisted" + }, + { + "conditionType":"ROLLOUT", + "matcherGroup":{ + "combiner":"AND", + "matchers":[ + { + "keySelector":{ + "trafficType":"account", + "attribute":null + }, + "matcherType":"ALL_KEYS", + "negate":false, + "userDefinedSegmentMatcherData":null, + "whitelistMatcherData":null, + "unaryNumericMatcherData":null, + "betweenMatcherData":null, + "booleanMatcherData":null, + "dependencyMatcherData":null, + "stringMatcherData":null + } + ] + }, + "partitions":[ + { + "treatment":"on", + "size":0 + }, + { + "treatment":"off", + "size":100 + }, + { + "treatment":"visa", + "size":0 + } + ], + "label":"in segment all" + } + ] + } + """ + + let split = Split(name: name, trafficType: trafficType, status: .active, sets: nil, json: splitJSONExample) split.isCompletelyParsed = false return split } From 606c54baba104f96760c289869e598ac3ce0333b Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 10:42:01 -0300 Subject: [PATCH 05/15] Update MySegmentUpdateTest.swift --- SplitTests/Integration/streaming/MySegmentUpdateTest.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 4adca76d..abaec642 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -257,7 +257,6 @@ class MySegmentUpdateTest: XCTestCase { let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") - //splitDatabase.generalInfoDao.update(info: .segmentsInUse, longValue: 0) let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) From 4ff7eada05243f0456591c44281471e816f769c3 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 12:16:26 -0300 Subject: [PATCH 06/15] Update MySegmentUpdateTest.swift --- SplitTests/Integration/streaming/MySegmentUpdateTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 527d0253..2ab4f2df 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -343,7 +343,7 @@ class MySegmentUpdateTest: XCTestCase { let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") splitDatabase.generalInfoDao.update(info: .segmentsInUse, longValue: 1) - let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) + let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .inSegment) splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) let userKey = "test-user-key" From 36aae9322262ac443068434f761c53a1aad88a88 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 12:27:26 -0300 Subject: [PATCH 07/15] Update MySegmentUpdateTest.swift --- SplitTests/Integration/streaming/MySegmentUpdateTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 2ab4f2df..66ea4532 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -312,7 +312,7 @@ class MySegmentUpdateTest: XCTestCase { // 1. Configure dispatcher let dispatcher: HttpClientTestDispatcher = { request in if request.url.absoluteString.contains("/splitChanges") { - let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtih Segments + let json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // splitChanges wtih no Segments return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) } From b298f1afd748d10926c1c8d85b2853f8fb31d30a Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 12:28:11 -0300 Subject: [PATCH 08/15] Initial commit --- Split/Storage/Splits/SplitsStorage.swift | 2 +- .../streaming/MySegmentUpdateTest.swift | 95 +++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index 81acd939..fd7f1d68 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -167,9 +167,9 @@ class DefaultSplitsStorage: SplitsStorage { if StorageHelper.usesSegments(split.conditions ?? []) { if inMemorySplits.value(forKey: splitName) == nil && active { // If new Split and active segmentsInUse += 1 + print("::: NEW FLAG USING SEGMENTS") } else if inMemorySplits.value(forKey: splitName) != nil && !active { // If known Split and archived segmentsInUse -= 1 - } } diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 527d0253..a55a29c7 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -385,6 +385,101 @@ class MySegmentUpdateTest: XCTestCase { destroy(client) } } + + func testSdkRestartMembershipsSyncIfNewFlag() throws { + + var sdkReadyFired = false + var cacheReadyFired = true + let sdkReady = XCTestExpectation(description: "SDK should be ready") + let cacheReadyExp = XCTestExpectation(description: "Cache should be ready") + let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") + var membershipsHit = 0 + + var json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtih Segments + + // 1. Configure dispatcher + let dispatcher: HttpClientTestDispatcher = { request in + if request.url.absoluteString.contains("/splitChanges") { + print("::: Getting flags") + return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) + } + + if request.url.absoluteString.contains("/memberships") { + segmentsHit.fulfill() + membershipsHit += 1 + print("::: Hit /memberships") + return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) + } + + return TestDispatcherResponse(code: 200) + } + + // 2. Setup Factory, Network & Client + let splitConfig: SplitClientConfig = SplitClientConfig() + splitConfig.featuresRefreshRate = 10 + splitConfig.segmentsRefreshRate = 10 + splitConfig.impressionRefreshRate = 30 + splitConfig.sdkReadyTimeOut = 60000 + splitConfig.eventsPerPush = 10 + splitConfig.streamingEnabled = false + splitConfig.eventsQueueSize = 100 + splitConfig.eventsPushRate = 999999 + splitConfig.eventsFirstPushWindow = 999 + splitConfig.impressionsMode = "DEBUG" + splitConfig.serviceEndpoints = ServiceEndpoints.builder() + .set(sdkEndpoint: "localhost").set(eventsEndpoint: "localhost").build() + + let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") + splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") + let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) + splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) + + let userKey = "test-user-key" + let key: Key = Key(matchingKey: userKey, bucketingKey: nil) + let session = HttpSessionMock() + let reqManager = HttpRequestManagerTestDispatcher(dispatcher: dispatcher, streamingHandler: buildStreamingHandler()) + httpClient = DefaultHttpClient(session: session, requestManager: reqManager) + let builder = DefaultSplitFactoryBuilder() + + _ = builder.setTestDatabase(splitDatabase) + _ = builder.setHttpClient(httpClient) + var factory = builder.setApiKey(apiKey).setKey(key).setConfig(splitConfig).build() + let client = factory?.client + + client?.on(event: .sdkReady) { + sdkReadyFired = true + sdkReady.fulfill() + } + + client?.on(event: .sdkReadyFromCache) { + cacheReadyExp.fulfill() + cacheReadyFired = true + } + + wait(for: [segmentsHit], timeout: 3) + XCTAssertEqual(sdkReadyFired, false) + + wait(for: [cacheReadyExp, sdkReady], timeout: 4) + + // MARK: Key part + var waitExp = XCTestExpectation(description: "Just waiting") + waitExp.isInverted = true // Inverted expectation + wait(for: [waitExp], timeout: 20) + XCTAssertEqual(membershipsHit, 1, "After some time, if segments are not used, SDK shouldn't hit /memberships") + + // MARK: Key part 2 + json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) // send splitChanges, now WITH Segments + + waitExp = XCTestExpectation(description: "Just waiting") + waitExp.isInverted = true // Inverted expectation + wait(for: [waitExp], timeout: 20) + XCTAssertGreaterThan(membershipsHit, 1, "If new flags with segments arrive, the mechanism should be restarted and SDK should hit /memberships many times again") + + // Cleanup + if let client = client { + destroy(client) + } + } func testMySegmentsUpdateBounded() throws { try mySegmentsUpdateBoundedTest(type: .mySegmentsUpdate) From 6e689a6f8be2d61ca45c967c5a9557118ef7e3bc Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 13:23:27 -0300 Subject: [PATCH 09/15] Test added --- Split/Storage/Splits/SplitsStorage.swift | 1 - .../streaming/MySegmentUpdateTest.swift | 18 ++--- .../Resources/splitschanges_no_segments.json | 66 +++++++++---------- 3 files changed, 40 insertions(+), 45 deletions(-) diff --git a/Split/Storage/Splits/SplitsStorage.swift b/Split/Storage/Splits/SplitsStorage.swift index fd7f1d68..e4b978f5 100644 --- a/Split/Storage/Splits/SplitsStorage.swift +++ b/Split/Storage/Splits/SplitsStorage.swift @@ -167,7 +167,6 @@ class DefaultSplitsStorage: SplitsStorage { if StorageHelper.usesSegments(split.conditions ?? []) { if inMemorySplits.value(forKey: splitName) == nil && active { // If new Split and active segmentsInUse += 1 - print("::: NEW FLAG USING SEGMENTS") } else if inMemorySplits.value(forKey: splitName) != nil && !active { // If known Split and archived segmentsInUse -= 1 } diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 3ca84d9a..fd1175c0 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -395,19 +395,17 @@ class MySegmentUpdateTest: XCTestCase { let segmentsHit = XCTestExpectation(description: "/memberships should be hit at least once") var membershipsHit = 0 - var json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // send splitChanges wtih Segments + var json = IntegrationHelper.loadSplitChangeFileJson(name: "splitschanges_no_segments", sourceClass: IntegrationHelper()) // no Segments // 1. Configure dispatcher let dispatcher: HttpClientTestDispatcher = { request in if request.url.absoluteString.contains("/splitChanges") { - print("::: Getting flags") return TestDispatcherResponse(code: 200, data: Data(json!.utf8)) } if request.url.absoluteString.contains("/memberships") { segmentsHit.fulfill() membershipsHit += 1 - print("::: Hit /memberships") return TestDispatcherResponse(code: 200, data: Data(IntegrationHelper.emptyMySegments.utf8)) } @@ -416,8 +414,8 @@ class MySegmentUpdateTest: XCTestCase { // 2. Setup Factory, Network & Client let splitConfig: SplitClientConfig = SplitClientConfig() - splitConfig.featuresRefreshRate = 10 - splitConfig.segmentsRefreshRate = 10 + splitConfig.featuresRefreshRate = 5 + splitConfig.segmentsRefreshRate = 5 splitConfig.impressionRefreshRate = 30 splitConfig.sdkReadyTimeOut = 60000 splitConfig.eventsPerPush = 10 @@ -431,8 +429,6 @@ class MySegmentUpdateTest: XCTestCase { let splitDatabase = TestingHelper.createTestDatabase(name: "ready_from_cache_test") splitDatabase.generalInfoDao.update(info: .flagsSpec, stringValue: "1.3") - let savedSplit = SplitTestHelper.newSplitWithMatcherType("splits_segments", .allKeys) - splitDatabase.splitDao.syncInsertOrUpdate(split: savedSplit) let userKey = "test-user-key" let key: Key = Key(matchingKey: userKey, bucketingKey: nil) @@ -464,16 +460,16 @@ class MySegmentUpdateTest: XCTestCase { // MARK: Key part var waitExp = XCTestExpectation(description: "Just waiting") waitExp.isInverted = true // Inverted expectation - wait(for: [waitExp], timeout: 20) + wait(for: [waitExp], timeout: 10) XCTAssertEqual(membershipsHit, 1, "After some time, if segments are not used, SDK shouldn't hit /memberships") // MARK: Key part 2 - json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) // send splitChanges, now WITH Segments + json = IntegrationHelper.loadSplitChangeFileJson(name: "splitchanges_1", sourceClass: IntegrationHelper()) // splitChanges, now WITH Segments waitExp = XCTestExpectation(description: "Just waiting") waitExp.isInverted = true // Inverted expectation - wait(for: [waitExp], timeout: 20) - XCTAssertGreaterThan(membershipsHit, 1, "If new flags with segments arrive, the mechanism should be restarted and SDK should hit /memberships many times again") + wait(for: [waitExp], timeout: 15) + XCTAssertGreaterThan(membershipsHit, 2, "If new flags with segments arrive, the mechanism should be restarted and SDK should hit /memberships many times again") // Cleanup if let client = client { diff --git a/SplitTests/Resources/splitschanges_no_segments.json b/SplitTests/Resources/splitschanges_no_segments.json index 3544cb2f..62d591bd 100644 --- a/SplitTests/Resources/splitschanges_no_segments.json +++ b/SplitTests/Resources/splitschanges_no_segments.json @@ -2,7 +2,7 @@ "d":[ { "trafficTypeName":"account", - "name":"FACUNDO_TEST", + "name":"FLFACUNDO_TEST", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -117,7 +117,7 @@ }, { "trafficTypeName":"account", - "name":"testing", + "name":"FLtesting", "trafficAllocation":100, "trafficAllocationSeed":527505678, "seed":-1716462249, @@ -229,7 +229,7 @@ }, { "trafficTypeName":"account", - "name":"testing222", + "name":"FLtesting222", "trafficAllocation":100, "trafficAllocationSeed":-397360967, "seed":1058132210, @@ -281,7 +281,7 @@ }, { "trafficTypeName":"account", - "name":"a_new_split_2", + "name":"FLa_new_split_2", "trafficAllocation":99, "trafficAllocationSeed":-1349440646, "seed":-1536389703, @@ -574,7 +574,7 @@ }, { "trafficTypeName":"account", - "name":"test_string_without_attr", + "name":"FLtest_string_without_attr", "trafficAllocation":100, "trafficAllocationSeed":-782597068, "seed":-1682478887, @@ -643,7 +643,7 @@ }, { "trafficTypeName":"user", - "name":"OldTest", + "name":"FLOldTest", "trafficAllocation":100, "trafficAllocationSeed":217539832, "seed":52164426, @@ -826,7 +826,7 @@ }, { "trafficTypeName":"account", - "name":"Test_Save_1", + "name":"FLTest_Save_1", "trafficAllocation":100, "trafficAllocationSeed":-257595325, "seed":-665945237, @@ -975,7 +975,7 @@ }, { "trafficTypeName":"account", - "name":"TEST", + "name":"FLTEST", "trafficAllocation":100, "trafficAllocationSeed":-673356676, "seed":-511119211, @@ -1023,7 +1023,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_1", + "name":"FLbenchmark_jw_1", "trafficAllocation":100, "trafficAllocationSeed":987354894, "seed":1292874260, @@ -1109,7 +1109,7 @@ }, { "trafficTypeName":"user", - "name":"nico_tests", + "name":"FLnico_tests", "trafficAllocation":100, "trafficAllocationSeed":1409699192, "seed":-1997241870, @@ -1190,7 +1190,7 @@ }, { "trafficTypeName":"account", - "name":"testo2222", + "name":"FLtesto2222", "trafficAllocation":100, "trafficAllocationSeed":1164474086, "seed":1270508512, @@ -1431,7 +1431,7 @@ }, { "trafficTypeName":"user", - "name":"Tagging", + "name":"FLTagging", "trafficAllocation":100, "trafficAllocationSeed":1910132597, "seed":-311493896, @@ -1479,7 +1479,7 @@ }, { "trafficTypeName":"account", - "name":"Welcome_Page_UI", + "name":"FLWelcome_Page_UI", "trafficAllocation":100, "trafficAllocationSeed":1848523960, "seed":1608586361, @@ -1530,7 +1530,7 @@ }, { "trafficTypeName":"test", - "name":"pato_test_3", + "name":"FLpato_test_3", "trafficAllocation":100, "trafficAllocationSeed":458647735, "seed":95677506, @@ -1578,7 +1578,7 @@ }, { "trafficTypeName":"account", - "name":"testo23", + "name":"FLtesto23", "trafficAllocation":100, "trafficAllocationSeed":-689658216, "seed":1711156051, @@ -1626,7 +1626,7 @@ }, { "trafficTypeName":"account", - "name":"testo909090", + "name":"FLtesto909090", "trafficAllocation":100, "trafficAllocationSeed":-1196467266, "seed":-1998101827, @@ -1792,7 +1792,7 @@ }, { "trafficTypeName":"account", - "name":"testo22", + "name":"FLtesto22", "trafficAllocation":100, "trafficAllocationSeed":1223277820, "seed":-1152948537, @@ -1840,7 +1840,7 @@ }, { "trafficTypeName":"user", - "name":"test-net", + "name":"FLtest-net", "trafficAllocation":100, "trafficAllocationSeed":-2038196969, "seed":-862203077, @@ -1888,7 +1888,7 @@ }, { "trafficTypeName":"account", - "name":"test_dep_2", + "name":"FLtest_dep_2", "trafficAllocation":100, "trafficAllocationSeed":-806171485, "seed":922684950, @@ -1980,7 +1980,7 @@ }, { "trafficTypeName":"account", - "name":"Definition_As_Of_Clickable_UI", + "name":"FLDefinition_As_Of_Clickable_UI", "trafficAllocation":100, "trafficAllocationSeed":-198035199, "seed":-151947071, @@ -2060,7 +2060,7 @@ }, { "trafficTypeName":"account", - "name":"Identify_UI", + "name":"FLIdentify_UI", "trafficAllocation":100, "trafficAllocationSeed":-139516103, "seed":1543172523, @@ -2108,7 +2108,7 @@ }, { "trafficTypeName":"account", - "name":"test_definition_as_of", + "name":"FLtest_definition_as_of", "trafficAllocation":100, "trafficAllocationSeed":1025823325, "seed":-554248124, @@ -2156,7 +2156,7 @@ }, { "trafficTypeName":"user", - "name":"Test-jw-go", + "name":"FLTest-jw-go", "trafficAllocation":100, "trafficAllocationSeed":768122971, "seed":1539205707, @@ -2234,7 +2234,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_7", + "name":"FLbenchmark_jw_7", "trafficAllocation":100, "trafficAllocationSeed":-1340337178, "seed":-1091938685, @@ -2282,7 +2282,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_6", + "name":"FLbenchmark_jw_6", "trafficAllocation":100, "trafficAllocationSeed":-1202331834, "seed":-48445256, @@ -2330,7 +2330,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_5", + "name":"FLbenchmark_jw_5", "trafficAllocation":100, "trafficAllocationSeed":2119994290, "seed":-227092192, @@ -2378,7 +2378,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_4", + "name":"FLbenchmark_jw_4", "trafficAllocation":100, "trafficAllocationSeed":1066635158, "seed":-850704283, @@ -2426,7 +2426,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_3", + "name":"FLbenchmark_jw_3", "trafficAllocation":100, "trafficAllocationSeed":1252392550, "seed":971538037, @@ -2474,7 +2474,7 @@ }, { "trafficTypeName":"user", - "name":"benchmark_jw_2", + "name":"FLbenchmark_jw_2", "trafficAllocation":100, "trafficAllocationSeed":-285565213, "seed":-1992295819, @@ -2522,7 +2522,7 @@ }, { "trafficTypeName":"user", - "name":"broken_split", + "name":"FLbroken_split", "trafficAllocation":100, "trafficAllocationSeed":-285565213, "status":"ACTIVE", @@ -2532,7 +2532,7 @@ }, { "trafficTypeName":"account", - "name":"TEST_SETS_1", + "name":"FLTEST_SETS_1", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2648,7 +2648,7 @@ }, { "trafficTypeName":"account", - "name":"TEST_SETS_2", + "name":"FLTEST_SETS_2", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2767,7 +2767,7 @@ }, { "trafficTypeName":"account", - "name":"TEST_SETS_3", + "name":"FLTEST_SETS_3", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, From 4c39693e68ac01f295957f2b6c7f8c3f446588c7 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Tue, 29 Jul 2025 13:35:20 -0300 Subject: [PATCH 10/15] Flags renamed --- .../Resources/splitschanges_no_segments.json | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/SplitTests/Resources/splitschanges_no_segments.json b/SplitTests/Resources/splitschanges_no_segments.json index 62d591bd..6feaf780 100644 --- a/SplitTests/Resources/splitschanges_no_segments.json +++ b/SplitTests/Resources/splitschanges_no_segments.json @@ -2,7 +2,7 @@ "d":[ { "trafficTypeName":"account", - "name":"FLFACUNDO_TEST", + "name":"NEW_FACUNDO_TEST", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -117,7 +117,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesting", + "name":"NEW_testing", "trafficAllocation":100, "trafficAllocationSeed":527505678, "seed":-1716462249, @@ -229,7 +229,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesting222", + "name":"NEW_testing222", "trafficAllocation":100, "trafficAllocationSeed":-397360967, "seed":1058132210, @@ -281,7 +281,7 @@ }, { "trafficTypeName":"account", - "name":"FLa_new_split_2", + "name":"NEW_a_new_split_2", "trafficAllocation":99, "trafficAllocationSeed":-1349440646, "seed":-1536389703, @@ -574,7 +574,7 @@ }, { "trafficTypeName":"account", - "name":"FLtest_string_without_attr", + "name":"NEW_test_string_without_attr", "trafficAllocation":100, "trafficAllocationSeed":-782597068, "seed":-1682478887, @@ -643,7 +643,7 @@ }, { "trafficTypeName":"user", - "name":"FLOldTest", + "name":"NEW_OldTest", "trafficAllocation":100, "trafficAllocationSeed":217539832, "seed":52164426, @@ -826,7 +826,7 @@ }, { "trafficTypeName":"account", - "name":"FLTest_Save_1", + "name":"NEW_Test_Save_1", "trafficAllocation":100, "trafficAllocationSeed":-257595325, "seed":-665945237, @@ -975,7 +975,7 @@ }, { "trafficTypeName":"account", - "name":"FLTEST", + "name":"NEW_TEST", "trafficAllocation":100, "trafficAllocationSeed":-673356676, "seed":-511119211, @@ -1023,7 +1023,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_1", + "name":"NEW_benchmark_jw_1", "trafficAllocation":100, "trafficAllocationSeed":987354894, "seed":1292874260, @@ -1109,7 +1109,7 @@ }, { "trafficTypeName":"user", - "name":"FLnico_tests", + "name":"NEW_nico_tests", "trafficAllocation":100, "trafficAllocationSeed":1409699192, "seed":-1997241870, @@ -1190,7 +1190,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesto2222", + "name":"NEW_testo2222", "trafficAllocation":100, "trafficAllocationSeed":1164474086, "seed":1270508512, @@ -1431,7 +1431,7 @@ }, { "trafficTypeName":"user", - "name":"FLTagging", + "name":"NEW_Tagging", "trafficAllocation":100, "trafficAllocationSeed":1910132597, "seed":-311493896, @@ -1479,7 +1479,7 @@ }, { "trafficTypeName":"account", - "name":"FLWelcome_Page_UI", + "name":"NEW_Welcome_Page_UI", "trafficAllocation":100, "trafficAllocationSeed":1848523960, "seed":1608586361, @@ -1530,7 +1530,7 @@ }, { "trafficTypeName":"test", - "name":"FLpato_test_3", + "name":"NEW_pato_test_3", "trafficAllocation":100, "trafficAllocationSeed":458647735, "seed":95677506, @@ -1578,7 +1578,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesto23", + "name":"NEW_testo23", "trafficAllocation":100, "trafficAllocationSeed":-689658216, "seed":1711156051, @@ -1626,7 +1626,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesto909090", + "name":"NEW_testo909090", "trafficAllocation":100, "trafficAllocationSeed":-1196467266, "seed":-1998101827, @@ -1792,7 +1792,7 @@ }, { "trafficTypeName":"account", - "name":"FLtesto22", + "name":"NEW_testo22", "trafficAllocation":100, "trafficAllocationSeed":1223277820, "seed":-1152948537, @@ -1840,7 +1840,7 @@ }, { "trafficTypeName":"user", - "name":"FLtest-net", + "name":"NEW_test-net", "trafficAllocation":100, "trafficAllocationSeed":-2038196969, "seed":-862203077, @@ -1888,7 +1888,7 @@ }, { "trafficTypeName":"account", - "name":"FLtest_dep_2", + "name":"NEW_test_dep_2", "trafficAllocation":100, "trafficAllocationSeed":-806171485, "seed":922684950, @@ -1980,7 +1980,7 @@ }, { "trafficTypeName":"account", - "name":"FLDefinition_As_Of_Clickable_UI", + "name":"NEW_Definition_As_Of_Clickable_UI", "trafficAllocation":100, "trafficAllocationSeed":-198035199, "seed":-151947071, @@ -2060,7 +2060,7 @@ }, { "trafficTypeName":"account", - "name":"FLIdentify_UI", + "name":"NEW_Identify_UI", "trafficAllocation":100, "trafficAllocationSeed":-139516103, "seed":1543172523, @@ -2108,7 +2108,7 @@ }, { "trafficTypeName":"account", - "name":"FLtest_definition_as_of", + "name":"NEW_test_definition_as_of", "trafficAllocation":100, "trafficAllocationSeed":1025823325, "seed":-554248124, @@ -2156,7 +2156,7 @@ }, { "trafficTypeName":"user", - "name":"FLTest-jw-go", + "name":"NEW_Test-jw-go", "trafficAllocation":100, "trafficAllocationSeed":768122971, "seed":1539205707, @@ -2234,7 +2234,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_7", + "name":"NEW_benchmark_jw_7", "trafficAllocation":100, "trafficAllocationSeed":-1340337178, "seed":-1091938685, @@ -2282,7 +2282,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_6", + "name":"NEW_benchmark_jw_6", "trafficAllocation":100, "trafficAllocationSeed":-1202331834, "seed":-48445256, @@ -2330,7 +2330,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_5", + "name":"NEW_benchmark_jw_5", "trafficAllocation":100, "trafficAllocationSeed":2119994290, "seed":-227092192, @@ -2378,7 +2378,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_4", + "name":"NEW_benchmark_jw_4", "trafficAllocation":100, "trafficAllocationSeed":1066635158, "seed":-850704283, @@ -2426,7 +2426,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_3", + "name":"NEW_benchmark_jw_3", "trafficAllocation":100, "trafficAllocationSeed":1252392550, "seed":971538037, @@ -2474,7 +2474,7 @@ }, { "trafficTypeName":"user", - "name":"FLbenchmark_jw_2", + "name":"NEW_benchmark_jw_2", "trafficAllocation":100, "trafficAllocationSeed":-285565213, "seed":-1992295819, @@ -2522,7 +2522,7 @@ }, { "trafficTypeName":"user", - "name":"FLbroken_split", + "name":"NEW_broken_split", "trafficAllocation":100, "trafficAllocationSeed":-285565213, "status":"ACTIVE", @@ -2532,7 +2532,7 @@ }, { "trafficTypeName":"account", - "name":"FLTEST_SETS_1", + "name":"NEW_TEST_SETS_1", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2648,7 +2648,7 @@ }, { "trafficTypeName":"account", - "name":"FLTEST_SETS_2", + "name":"NEW_TEST_SETS_2", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, @@ -2767,7 +2767,7 @@ }, { "trafficTypeName":"account", - "name":"FLTEST_SETS_3", + "name":"NEW_TEST_SETS_3", "trafficAllocation":59, "trafficAllocationSeed":-2108186082, "seed":-1947050785, From 0f2ce61707d736487e7fbe8fe6768f61e303f74c Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Wed, 30 Jul 2025 12:04:07 -0300 Subject: [PATCH 11/15] Update MySegmentUpdateTest.swift --- SplitTests/Integration/streaming/MySegmentUpdateTest.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index fd1175c0..1d856505 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -368,10 +368,7 @@ class MySegmentUpdateTest: XCTestCase { cacheReadyFired = true } - wait(for: [segmentsHit], timeout: 3) - XCTAssertEqual(sdkReadyFired, false) - - wait(for: [cacheReadyExp, sdkReady], timeout: 3) + wait(for: [segmentsHit, cacheReadyExp, sdkReady], timeout: 4) // MARK: Key part let waitExp = XCTestExpectation(description: "Just waiting") From cb43feb49ee7f03de651c9f22d06e18d72557571 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 31 Jul 2025 12:11:42 -0300 Subject: [PATCH 12/15] Test fixed --- Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift b/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift index 2d623ffa..6b47c3ea 100644 --- a/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift +++ b/Split/Storage/RuleBasedSegments/RuleBasedSegmentsStorage.swift @@ -33,6 +33,7 @@ class DefaultRuleBasedSegmentsStorage: RuleBasedSegmentsStorage { } func loadLocal() { + segmentsInUse = persistentStorage.getSegmentsInUse() let snapshot = persistentStorage.getSnapshot() let active = snapshot.segments.filter { $0.status == .active } let archived = snapshot.segments.filter { $0.status == .archived } From b2e02d25355f62b6c8ca48d625babe5188e7fb70 Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 31 Jul 2025 12:21:03 -0300 Subject: [PATCH 13/15] Typo --- SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift index 892ba2d8..43729167 100644 --- a/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift +++ b/SplitTests/Fake/Storage/PersistentSplitsStorageStub.swift @@ -93,7 +93,7 @@ class PersistentSplitsStorageStub: PersistentSplitsStorage { self.segmentsInUse = segmentsInUse } - func getSegmentsInUse() -> Int64 { + func getSegmentsInUse() -> Int64? { getSegmentsInUseCalled = true return segmentsInUse } From 2ea1ffdf473b3db73dc928eceec181c94f6d22ad Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 31 Jul 2025 12:57:35 -0300 Subject: [PATCH 14/15] Update MySegmentUpdateTest.swift --- .../Integration/streaming/MySegmentUpdateTest.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift index 1d856505..90d03c7d 100644 --- a/SplitTests/Integration/streaming/MySegmentUpdateTest.swift +++ b/SplitTests/Integration/streaming/MySegmentUpdateTest.swift @@ -242,8 +242,8 @@ class MySegmentUpdateTest: XCTestCase { // 2. Setup Factory, Network & Client let splitConfig: SplitClientConfig = SplitClientConfig() - splitConfig.featuresRefreshRate = 4 - splitConfig.segmentsRefreshRate = 4 + splitConfig.featuresRefreshRate = 1 + splitConfig.segmentsRefreshRate = 1 splitConfig.impressionRefreshRate = 30 splitConfig.sdkReadyTimeOut = 60000 splitConfig.eventsPerPush = 10 @@ -327,8 +327,8 @@ class MySegmentUpdateTest: XCTestCase { // 2. Setup Factory, Network & Client let splitConfig: SplitClientConfig = SplitClientConfig() - splitConfig.featuresRefreshRate = 4 - splitConfig.segmentsRefreshRate = 4 + splitConfig.featuresRefreshRate = 1 + splitConfig.segmentsRefreshRate = 1 splitConfig.impressionRefreshRate = 30 splitConfig.sdkReadyTimeOut = 60000 splitConfig.eventsPerPush = 10 @@ -411,8 +411,8 @@ class MySegmentUpdateTest: XCTestCase { // 2. Setup Factory, Network & Client let splitConfig: SplitClientConfig = SplitClientConfig() - splitConfig.featuresRefreshRate = 5 - splitConfig.segmentsRefreshRate = 5 + splitConfig.featuresRefreshRate = 2 + splitConfig.segmentsRefreshRate = 2 splitConfig.impressionRefreshRate = 30 splitConfig.sdkReadyTimeOut = 60000 splitConfig.eventsPerPush = 10 From b2497e795edb0824e3a2994e1ef09c55d50a72aa Mon Sep 17 00:00:00 2001 From: Martin Cardozo Date: Thu, 31 Jul 2025 12:58:03 -0300 Subject: [PATCH 15/15] Update SplitTests/Integration/Api/SplitIntegrationTest.swift Co-authored-by: gthea --- SplitTests/Integration/Api/SplitIntegrationTest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SplitTests/Integration/Api/SplitIntegrationTest.swift b/SplitTests/Integration/Api/SplitIntegrationTest.swift index 7e8f6dd2..148cd5cd 100644 --- a/SplitTests/Integration/Api/SplitIntegrationTest.swift +++ b/SplitTests/Integration/Api/SplitIntegrationTest.swift @@ -108,7 +108,7 @@ class SplitIntegrationTests: XCTestCase { splitConfig.logLevel = TestingHelper.testLogLevel splitConfig.impressionsMode = "DEBUG" splitConfig.serviceEndpoints = ServiceEndpoints.builder() - .set(sdkEndpoint: "localhost").set(eventsEndpoint: "localhost").build() + .set(sdkEndpoint: serverUrl).set(eventsEndpoint: serverUrl).build() splitConfig.impressionListener = { impression in impressions[IntegrationHelper.buildImpressionKey(impression: impression)] = impression