1- using Moq ;
1+ using System ;
22using System . Collections . Generic ;
3+ using System . IO ;
4+ using System . Linq ;
5+ using System . Net ;
36using System . Net . Http ;
7+ using System . Threading ;
48using System . Threading . Tasks ;
5- using Xunit ;
69using FlagsmithEngine . Environment . Models ;
7- using OfflineHandler ;
10+ using Moq ;
811using Newtonsoft . Json . Linq ;
9- using System . IO ;
10- using System ;
11- using System . Net ;
12+ using OfflineHandler ;
13+ using Xunit ;
1214
1315namespace Flagsmith . FlagsmithClientTest
1416{
@@ -23,7 +25,7 @@ public void TestFlagsmithStartsPollingManagerOnInitIfEnabled()
2325 Content = new StringContent ( Fixtures . JsonObject . ToString ( ) )
2426 } ) ;
2527 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object , enableClientSideEvaluation : true ) ;
26- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
28+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
2729 }
2830 [ Fact ]
2931 public async void TestUpdateEnvironmentSetsEnvironment ( )
@@ -50,10 +52,10 @@ public async void TestUpdateEnvironmentSetsEnvironment()
5052 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , enableClientSideEvaluation : true , httpClient : mockHttpClient . Object ) ;
5153
5254 // Then
53- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
55+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
5456 await flagsmithClientTest . GetEnvironmentFlags ( ) ;
55- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
56- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Never ) ;
57+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
58+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Never ) ;
5759 }
5860 [ Fact ]
5961 public async Task TestGetEnvironmentFlagsCallsApiWhenNoLocalEnvironment ( )
@@ -65,7 +67,7 @@ public async Task TestGetEnvironmentFlagsCallsApiWhenNoLocalEnvironment()
6567 } ) ;
6668 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object ) ;
6769 var flags = ( await flagsmithClientTest . GetEnvironmentFlags ( ) ) . AllFlags ( ) ;
68- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Once ) ;
70+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Once ) ;
6971 Assert . True ( flags [ 0 ] . Enabled ) ;
7072 Assert . Equal ( "some-value" , flags [ 0 ] . Value ) ;
7173 Assert . Equal ( "some_feature" , flags [ 0 ] . GetFeatureName ( ) ) ;
@@ -95,18 +97,36 @@ public async Task TestGetEnvironmentFlagsUsesLocalEnvironmentWhenAvailable()
9597 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , enableClientSideEvaluation : true , httpClient : mockHttpClient . Object ) ;
9698
9799 // Then
98- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
100+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
99101 var flags = ( await flagsmithClientTest . GetEnvironmentFlags ( ) ) . AllFlags ( ) ;
100102 var fs = Fixtures . Environment . FeatureStates [ 0 ] ;
101103 Assert . Equal ( fs . Enabled , flags [ 0 ] . Enabled ) ;
102104 Assert . Equal ( fs . GetValue ( ) , flags [ 0 ] . Value ) ;
103105 Assert . Equal ( fs . Feature . Name , flags [ 0 ] . GetFeatureName ( ) ) ;
104- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
106+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
105107 }
106108 [ Fact ]
107- public async Task TestGetIdentityFlagsCallsGetApiWhenNoLocalEnvironmentNoTraits ( )
109+ public async Task TestThatCacheDictionaryDoesNotThrowUnderLoad ( )
110+ {
111+ const int numberOfThreads = 500 ;
112+
113+ ThreadPool . SetMinThreads ( numberOfThreads , numberOfThreads ) ;
114+
115+ var mockHttpClient = HttpMocker . MockHttpResponse ( System . Net . HttpStatusCode . OK , Fixtures . ApiIdentityResponse , false ) ;
116+
117+ var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object , cacheConfig : new CacheConfig ( true ) ) ;
118+
119+ var token = new CancellationToken ( ) ;
120+ await Parallel . ForEachAsync ( Enumerable . Range ( 1 , numberOfThreads ) , token , async ( item , token ) =>
121+ {
122+ var flags = ( await flagsmithClientTest . GetIdentityFlags ( item . ToString ( ) ) ) . AllFlags ( ) ;
123+ } ) ;
124+ }
125+ [ Theory ]
126+ [ InlineData ( "identifier" , "{\" identifier\" :\" identifier\" ,\" traits\" :[],\" transient\" :false}" ) ]
127+ [ InlineData ( "identifier&h=g" , "{\" identifier\" :\" identifier&h=g\" ,\" traits\" :[],\" transient\" :false}" ) ]
128+ public async Task TestGetIdentityFlagsCallsPostApiWhenNoLocalEnvironmentNoTraits ( string identifier , string expectedJson )
108129 {
109- string identifier = "identifier" ;
110130 var mockHttpClient = HttpMocker . MockHttpResponse ( new HttpResponseMessage
111131 {
112132 StatusCode = System . Net . HttpStatusCode . OK ,
@@ -117,8 +137,8 @@ public async Task TestGetIdentityFlagsCallsGetApiWhenNoLocalEnvironmentNoTraits(
117137 Assert . True ( flags [ 0 ] . Enabled ) ;
118138 Assert . Equal ( "some-value" , flags [ 0 ] . Value ) ;
119139 Assert . Equal ( "some_feature" , flags [ 0 ] . GetFeatureName ( ) ) ;
120- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/identities/" , Times . Once , new Dictionary < string , string > { { "identifier" , identifier } } ) ;
121140
141+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , expectedJson ) ;
122142 }
123143 [ Fact ]
124144 public async Task TestGetIdentityFlagsCallsPostApiWhenNoLocalEnvironmentWithTraits ( )
@@ -136,7 +156,7 @@ public async Task TestGetIdentityFlagsCallsPostApiWhenNoLocalEnvironmentWithTrai
136156 Assert . True ( flags [ 0 ] . Enabled ) ;
137157 Assert . Equal ( "some-value" , flags [ 0 ] . Value ) ;
138158 Assert . Equal ( "some_feature" , flags [ 0 ] . GetFeatureName ( ) ) ;
139- mockHttpClient . verifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once ) ;
159+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once ) ;
140160
141161 }
142162 [ Fact ]
@@ -151,11 +171,11 @@ public async Task TestGetIdentityFlagsUsesLocalEnvironmentWhenAvailable()
151171 var traits = new List < ITrait > { new Trait ( "foo" , "bar" ) } ;
152172
153173 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , enableClientSideEvaluation : true , httpClient : mockHttpClient . Object ) ;
154- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
174+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
155175
156176 _ = await flagsmithClientTest . GetIdentityFlags ( "identifier" , new List < ITrait > ( ) { new Trait ( "foo" , "bar" ) } ) ;
157177
158- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
178+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
159179 }
160180 [ Fact ]
161181 public async Task TestGetIdentityFlagsUsesLocalIdentityOverridesWhenAvailable ( )
@@ -169,7 +189,7 @@ public async Task TestGetIdentityFlagsUsesLocalIdentityOverridesWhenAvailable()
169189 var traits = new List < ITrait > { new Trait ( "foo" , "bar" ) } ;
170190
171191 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , enableClientSideEvaluation : true , httpClient : mockHttpClient . Object ) ;
172- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
192+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/environment-document/" , Times . Once ) ;
173193
174194 var flags = await flagsmithClientTest . GetIdentityFlags ( "overridden-id" , traits ) ;
175195 var flag = await flags . GetFlag ( "some_feature" ) ;
@@ -282,7 +302,7 @@ public async Task TestGetIdentityFlagsSendsTraits()
282302
283303 var flags = await flagsmithClient . GetIdentityFlags ( identifier , traits ) ;
284304
285- mockHttpClient . verifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once ) ;
305+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once ) ;
286306 // TODO: verify the body is correct - I've verified manually but can't verify programmatically
287307 }
288308
@@ -456,7 +476,7 @@ public async Task TestFlagsmithUsesTheAPIResponseEvenIfTheOfflineHandlerIsSet()
456476
457477 // Then
458478 var environmentFlags = await flagsmithClientTest . GetEnvironmentFlags ( ) ;
459- mockHttpClient . verifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Once ) ;
479+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Get , "/api/v1/flags/" , Times . Once ) ;
460480 Assert . True ( await environmentFlags . IsFeatureEnabled ( "some_feature" ) ) ;
461481 Assert . NotEqual ( "offline-value" , await environmentFlags . GetFeatureValue ( "some_feature" ) ) ;
462482 Assert . Equal ( "some-value" , await environmentFlags . GetFeatureValue ( "some_feature" ) ) ;
@@ -521,13 +541,14 @@ public async Task TestAnalyticsDataConsistencyWithConcurrentCallsToGetFlags()
521541 } ) ;
522542 var flagsmithClientTest = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object , enableAnalytics : true ) ;
523543 var flags = await flagsmithClientTest . GetEnvironmentFlags ( ) ;
524-
525- Dictionary < string , int > featuresDictionary = new Dictionary < string , int > ( ) ;
544+ var featuresDictionary = new Dictionary < string , int > ( ) ;
526545
527546 const int numberOfFeatures = 10 ;
528547 const int numberOfThreads = 1000 ;
529548 const int callsPerThread = 1000 ;
530549
550+ ThreadPool . SetMinThreads ( numberOfThreads , numberOfThreads ) ;
551+
531552 for ( int i = 1 ; i <= numberOfFeatures ; i ++ )
532553 {
533554 featuresDictionary . TryAdd ( $ "Feature_{ i } ", 0 ) ;
@@ -594,7 +615,7 @@ public async Task TestGetIdentityFlagsTransientIdentityCallsExpected()
594615 var flagsmithClient = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object ) ;
595616 var identityFlags = await flagsmithClient . GetIdentityFlags ( identifier , traits , transient ) ;
596617 // Then
597- mockHttpClient . verifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , "{\" identifier\" :\" transient_identity\" ,\" traits\" :[{\" trait_key\" :\" some_trait\" ,\" trait_value\" :\" some_value\" ,\" transient\" :false}],\" transient\" :true}" ) ;
618+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , "{\" identifier\" :\" transient_identity\" ,\" traits\" :[{\" trait_key\" :\" some_trait\" ,\" trait_value\" :\" some_value\" ,\" transient\" :false}],\" transient\" :true}" ) ;
598619 Assert . True ( await identityFlags . IsFeatureEnabled ( "some_feature" ) ) ;
599620 Assert . Equal ( "some-identity-trait-value" , await identityFlags . GetFeatureValue ( "some_feature" ) ) ;
600621 }
@@ -615,12 +636,12 @@ public async Task TestGetIdentityFlagsTransientTraitKeysCallsExpected()
615636 var flagsmithClient = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object ) ;
616637 var identityFlags = await flagsmithClient . GetIdentityFlags ( identifier , traits ) ;
617638 // Then
618- mockHttpClient . verifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , "{\" identifier\" :\" test_identity_with_transient_traits\" ,\" traits\" :[{\" trait_key\" :\" transient_trait\" ,\" trait_value\" :\" transient_trait_value\" ,\" transient\" :true}],\" transient\" :false}" ) ;
639+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , "{\" identifier\" :\" test_identity_with_transient_traits\" ,\" traits\" :[{\" trait_key\" :\" transient_trait\" ,\" trait_value\" :\" transient_trait_value\" ,\" transient\" :true}],\" transient\" :false}" ) ;
619640 Assert . True ( await identityFlags . IsFeatureEnabled ( "some_feature" ) ) ;
620641 Assert . Equal ( "some-transient-trait-value" , await identityFlags . GetFeatureValue ( "some_feature" ) ) ;
621642 }
622643 [ Fact ]
623- public async Task TestGetIdentityFlagsTransientIdentityWitoutTraitCallsExpected ( )
644+ public async Task TestGetIdentityFlagsTransientIdentityWithoutTraitCallsExpected ( )
624645 {
625646 // Given
626647 string identifier = "transient_identity" ;
@@ -639,7 +660,7 @@ public async Task TestGetIdentityFlagsTransientIdentityWitoutTraitCallsExpected(
639660 var flagsmithClient = new FlagsmithClient ( Fixtures . ApiKey , httpClient : mockHttpClient . Object ) ;
640661 var identityFlags = await flagsmithClient . GetIdentityFlags ( identifier , null , transient ) ;
641662 // Then
642- mockHttpClient . verifyHttpRequestWithParams ( HttpMethod . Get , "/api/v1/identities/" , Times . Once , ( Dictionary < string , string > ) queryParams ) ;
663+ mockHttpClient . VerifyHttpRequest ( HttpMethod . Post , "/api/v1/identities/" , Times . Once , "{ \" identifier \" : \" transient_identity \" , \" traits \" :[], \" transient \" :true}" ) ;
643664 Assert . True ( await identityFlags . IsFeatureEnabled ( "some_feature" ) ) ;
644665 Assert . Equal ( "some-identity-trait-value" , await identityFlags . GetFeatureValue ( "some_feature" ) ) ;
645666 }
0 commit comments