1-
21import XCTest
32import Combine
43@testable import OpenFeature
@@ -23,7 +22,7 @@ class ConcurrencyRaceConditionTests: XCTestCase {
2322
2423 let concurrentOperations = 100
2524 let expectedTargetingKeys = Set ( ( 0 ..< concurrentOperations) . map { " user \( $0) " } )
26-
25+
2726 await withTaskGroup ( of: Void . self) { group in
2827 for i in 0 ..< concurrentOperations {
2928 group. addTask {
@@ -34,26 +33,24 @@ class ConcurrencyRaceConditionTests: XCTestCase {
3433 " timestamp " : . string( " \( Date ( ) . timeIntervalSince1970) " )
3534 ] )
3635 )
37-
38- // This should trigger the race condition in updateContext
36+
3937 await OpenFeatureAPI . shared. setEvaluationContextAndWait ( evaluationContext: ctx)
4038 }
4139 }
4240 }
43-
41+
4442 cancellable. cancel ( )
45-
46- // Verify final state is consistent and correct
43+
4744 let finalContext = OpenFeatureAPI . shared. getEvaluationContext ( )
4845 XCTAssertNotNil ( finalContext, " Final evaluation context should not be nil after concurrent operations " )
49-
46+
5047 if let context = finalContext {
5148 let targetingKey = context. getTargetingKey ( )
5249 XCTAssertTrue (
5350 expectedTargetingKeys. contains ( targetingKey) ,
5451 " Final targeting key ' \( targetingKey) ' should be one of the expected keys from concurrent operations "
5552 )
56-
53+
5754 let contextMap = context. asObjectMap ( )
5855 XCTAssertTrue ( contextMap. keys. contains ( " id " ) , " Context should contain 'id' attribute " )
5956 XCTAssertTrue ( contextMap. keys. contains ( " timestamp " ) , " Context should contain 'timestamp' attribute " )
@@ -67,15 +64,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
6764 }
6865 }
6966
70- /// Test the specific race condition between setProvider and setEvaluationContext
71- /// This was the main issue identified by the external reviewer
7267 func testSetProviderVsSetEvaluationContextRaceCondition( ) async throws {
7368 let concurrentOperations = 50
7469
7570 await withTaskGroup ( of: Void . self) { group in
76- // Concurrently set providers and evaluation contexts
7771 for i in 0 ..< concurrentOperations {
78- // Set provider operations
7972 group. addTask {
8073 let provider = MockProvider ( )
8174 let ctx = ImmutableContext (
@@ -85,7 +78,6 @@ class ConcurrencyRaceConditionTests: XCTestCase {
8578 await OpenFeatureAPI . shared. setProviderAndWait ( provider: provider, initialContext: ctx)
8679 }
8780
88- // Set evaluation context operations
8981 group. addTask {
9082 let ctx = ImmutableContext (
9183 targetingKey: " context-user \( i) " ,
@@ -96,13 +88,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
9688 }
9789 }
9890
99- // Verify the API is in a consistent state
10091 let finalState = OpenFeatureAPI . shared. getState ( )
10192 XCTAssertNotNil ( finalState. provider, " Provider should not be nil after concurrent operations " )
10293 XCTAssertNotNil ( finalState. evaluationContext, " Evaluation context should not be nil after concurrent operations " )
103- XCTAssertTrue ( [ . ready, . error , . fatal ] . contains ( finalState. providerStatus) , " Provider status should be in a valid final state " )
94+ XCTAssertTrue ( [ . ready] . contains ( finalState. providerStatus) , " Provider status should be in a valid final state " )
10495
105- // Verify the final context has expected structure from one of the operations
10696 if let context = finalState. evaluationContext {
10797 let targetingKey = context. getTargetingKey ( )
10898 XCTAssertTrue (
@@ -120,52 +110,13 @@ class ConcurrencyRaceConditionTests: XCTestCase {
120110 }
121111 }
122112
123- /// Test the race condition between provider initialization and context updates
124- func testProviderInitializationVsContextUpdateRaceCondition( ) async throws {
125- let concurrentOperations = 30
126-
127- await withTaskGroup ( of: Void . self) { group in
128- for i in 0 ..< concurrentOperations {
129- // Provider initialization with context
130- group. addTask {
131- let provider = MockProvider ( )
132- let initialCtx = ImmutableContext (
133- targetingKey: " init-user \( i) " ,
134- structure: ImmutableStructure ( attributes: [ " init " : . integer( Int64 ( i) ) ] )
135- )
136- await OpenFeatureAPI . shared. setProviderAndWait ( provider: provider, initialContext: initialCtx)
137- }
138-
139- // Immediate context updates
140- group. addTask {
141- let updateCtx = ImmutableContext (
142- targetingKey: " update-user \( i) " ,
143- structure: ImmutableStructure ( attributes: [ " update " : . integer( Int64 ( i) ) ] )
144- )
145- await OpenFeatureAPI . shared. setEvaluationContextAndWait ( evaluationContext: updateCtx)
146- }
147-
148- // Clear provider operations
149- group. addTask {
150- OpenFeatureAPI . shared. clearProvider ( )
151- }
152- }
153- }
154-
155- // Verify the API ends in a consistent state
156- let finalState = OpenFeatureAPI . shared. getState ( )
157- XCTAssertTrue ( [ . notReady, . ready, . error, . fatal] . contains ( finalState. providerStatus) )
158- }
159-
160- /// Test high-frequency state changes to stress test synchronization
161113 func testHighFrequencyStateChangesRaceCondition( ) async throws {
162114 let highFrequencyOperations = 200
163115 let startTime = Date ( )
164116
165117 await withTaskGroup ( of: Void . self) { group in
166118 for i in 0 ..< highFrequencyOperations {
167119 group. addTask {
168- // Rapid fire operations
169120 let provider = MockProvider ( )
170121 let ctx = ImmutableContext (
171122 targetingKey: " rapid-user \( i) " ,
@@ -174,8 +125,6 @@ class ConcurrencyRaceConditionTests: XCTestCase {
174125 " timestamp " : . string( " \( Date ( ) . timeIntervalSince1970) " )
175126 ] )
176127 )
177-
178- // Alternate between different operations
179128 switch i % 4 {
180129 case 0 :
181130 await OpenFeatureAPI . shared. setProviderAndWait ( provider: provider)
@@ -198,14 +147,11 @@ class ConcurrencyRaceConditionTests: XCTestCase {
198147 // Verify operations completed in reasonable time (no deadlocks)
199148 XCTAssertLessThan ( duration, 10.0 , " Operations took too long, possible deadlock " )
200149
201- // Verify final state is consistent
202150 let finalState = OpenFeatureAPI . shared. getState ( )
203151 XCTAssertTrue (
204- [ ProviderStatus . notReady , . ready, . error , . fatal ] . contains ( finalState. providerStatus) ,
152+ [ ProviderStatus . ready ] . contains ( finalState. providerStatus) ,
205153 " Provider status ' \( finalState. providerStatus) ' should be in a valid state after high-frequency operations "
206154 )
207-
208- // If we have a provider and context, verify they're consistent
209155 if finalState. provider != nil && finalState. evaluationContext != nil {
210156 let context = finalState. evaluationContext!
211157 let targetingKey = context. getTargetingKey ( )
@@ -218,6 +164,8 @@ class ConcurrencyRaceConditionTests: XCTestCase {
218164 if contextMap. keys. contains ( " iteration " ) {
219165 XCTAssertTrue ( contextMap. keys. contains ( " timestamp " ) , " Context with iteration should also have timestamp " )
220166 }
167+ } else {
168+ XCTFail ( )
221169 }
222170 }
223171}
0 commit comments