22using System . Collections . Generic ;
33using System . IO ;
44using Calamari . Common . Features . Processes ;
5+ using System . Diagnostics ;
6+ using System . Linq ;
57using Calamari . Common . FeatureToggles ;
68using Calamari . Common . Plumbing ;
79using Calamari . Common . Plumbing . Variables ;
810using Calamari . Deployment ;
911using Calamari . Testing . Requirements ;
1012using Calamari . Tests . Helpers ;
13+ using FluentAssertions ;
1114using NUnit . Framework ;
1215
1316namespace Calamari . Tests . Fixtures . Bash
@@ -34,7 +37,7 @@ public void ShouldPrintEncodedVariable(FeatureToggle? featureToggle)
3437 [ RequiresBashDotExeIfOnWindows ]
3538 public void ShouldPrintSensitiveVariable ( FeatureToggle ? featureToggle )
3639 {
37- var ( output , _) = RunScript ( "print-sensitive-variable.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
40+ var ( output , _) = RunScript ( "print-sensitive-variable.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
3841
3942 Assert . Multiple ( ( ) =>
4043 {
@@ -48,7 +51,7 @@ public void ShouldPrintSensitiveVariable(FeatureToggle? featureToggle)
4851 [ RequiresBashDotExeIfOnWindows ]
4952 public void ShouldCreateArtifact ( FeatureToggle ? featureToggle )
5053 {
51- var ( output , _) = RunScript ( "create-artifact.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
54+ var ( output , _) = RunScript ( "create-artifact.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
5255
5356 Assert . Multiple ( ( ) =>
5457 {
@@ -62,7 +65,7 @@ public void ShouldCreateArtifact(FeatureToggle? featureToggle)
6265 [ RequiresBashDotExeIfOnWindows ]
6366 public void ShouldUpdateProgress ( FeatureToggle ? featureToggle )
6467 {
65- var ( output , _) = RunScript ( "update-progress.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
68+ var ( output , _) = RunScript ( "update-progress.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
6669
6770 Assert . Multiple ( ( ) =>
6871 {
@@ -160,7 +163,7 @@ public void ShouldConsumeParametersWithQuotes(FeatureToggle? featureToggle)
160163 {
161164 var ( output , _) = RunScript ( "parameters.sh" ,
162165 new Dictionary < string , string > ( )
163- { [ SpecialVariables . Action . Script . ScriptParameters ] = "\" Para meter0\" 'Para meter1'" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
166+ { [ SpecialVariables . Action . Script . ScriptParameters ] = "\" Para meter0\" 'Para meter1'" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
164167
165168 Assert . Multiple ( ( ) =>
166169 {
@@ -174,8 +177,10 @@ public void ShouldConsumeParametersWithQuotes(FeatureToggle? featureToggle)
174177 [ RequiresBashDotExeIfOnWindows ]
175178 public void ShouldNotReceiveParametersIfNoneProvided ( FeatureToggle ? featureToggle )
176179 {
177- var ( output , _) = RunScript ( "parameters.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
178- "5XETGOgqYR2bRhlfhDruEg==" ) ;
180+ var ( output , _) = RunScript ( "parameters.sh" ,
181+ new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
182+ sensitiveVariablesPassword :
183+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
179184
180185 Assert . Multiple ( ( ) =>
181186 {
@@ -197,7 +202,7 @@ public void ShouldCallHello(FeatureToggle? featureToggle)
197202 [ "Variable3" ] = "GHI" ,
198203 [ "Foo_bar" ] = "Hello" ,
199204 [ "Host" ] = "Never" ,
200- } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
205+ } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
201206
202207 Assert . Multiple ( ( ) =>
203208 {
@@ -213,8 +218,9 @@ public void ShouldCallHelloWithSensitiveVariable(FeatureToggle? featureToggle)
213218 {
214219 var ( output , _) = RunScript ( "hello.sh" ,
215220 new Dictionary < string , string > ( )
216- { [ "Name" ] = "NameToEncrypt" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
217- "5XETGOgqYR2bRhlfhDruEg==" ) ;
221+ { [ "Name" ] = "NameToEncrypt" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
222+ sensitiveVariablesPassword :
223+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
218224
219225 Assert . Multiple ( ( ) =>
220226 {
@@ -230,7 +236,7 @@ public void ShouldCallHelloWithNullVariable(FeatureToggle? featureToggle)
230236 {
231237 var ( output , _) = RunScript ( "hello.sh" ,
232238 new Dictionary < string , string > ( )
233- { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
239+ { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
234240
235241 Assert . Multiple ( ( ) =>
236242 {
@@ -246,8 +252,9 @@ public void ShouldCallHelloWithNullSensitiveVariable(FeatureToggle? featureToggl
246252 {
247253 var ( output , _) = RunScript ( "hello.sh" ,
248254 new Dictionary < string , string > ( )
249- { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
250- "5XETGOgqYR2bRhlfhDruEg==" ) ;
255+ { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
256+ sensitiveVariablesPassword :
257+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
251258
252259 Assert . Multiple ( ( ) =>
253260 {
@@ -262,7 +269,7 @@ public void ShouldCallHelloWithNullSensitiveVariable(FeatureToggle? featureToggl
262269 public void ShouldNotFailOnStdErr ( FeatureToggle ? featureToggle )
263270 {
264271 var ( output , _) = RunScript ( "stderr.sh" ,
265- new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
272+ new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
266273
267274 Assert . Multiple ( ( ) =>
268275 {
@@ -278,7 +285,7 @@ public void ShouldFailOnStdErrWithTreatScriptWarningsAsErrors(FeatureToggle? fea
278285 {
279286 var ( output , _) = RunScript ( "stderr.sh" ,
280287 new Dictionary < string , string > ( )
281- { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
288+ { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
282289
283290 Assert . Multiple ( ( ) =>
284291 {
@@ -294,7 +301,7 @@ public void ShouldNotFailOnStdErrFromServiceMessagesWithTreatScriptWarningsAsErr
294301 {
295302 var ( output , _) = RunScript ( "hello.sh" ,
296303 new Dictionary < string , string > ( )
297- { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
304+ { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
298305
299306 output . AssertSuccess ( ) ;
300307 }
@@ -304,7 +311,7 @@ public void ShouldNotFailOnStdErrFromServiceMessagesWithTreatScriptWarningsAsErr
304311 [ TestCase ( null ) ]
305312 public void ShouldSupportStrictVariableUnset ( FeatureToggle ? featureToggle )
306313 {
307- var ( output , _) = RunScript ( "strict-mode.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
314+ var ( output , _) = RunScript ( "strict-mode.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
308315
309316 Assert . Multiple ( ( ) =>
310317 {
@@ -313,7 +320,7 @@ public void ShouldSupportStrictVariableUnset(FeatureToggle? featureToggle)
313320 } ) ;
314321 }
315322
316- static string specialCharacters => "! \" # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \\ ] ^ _ ` { | } ~ \n \u00b1 \u00d7 \u00f7 \u2211 \u220f \u2202 \u221e \u222b \u2248 \u2260 \u2264 \u2265 \u221a \u221b \u2206 \u2207 \u221d \n $ \u00a2 \u00a3 \u00a5 \u20ac \u20b9 \u20a9 \u20b1 \u20aa \u20bf \n • ‣ … ′ ″ ‘ ’ “ ” ‽ ¡ ¿ – — ― \n ( ) [ ] { } ⟨ ⟩ « » ‘ ’ “ ” \n \u2190 \u2191 \u2192 \u2193 \u2194 \u2195 \u2196 \u2197 \u2198 \u2199 \u2b05 \u2b06 \u2b07 \u27a1 \u27f3 \n α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω \n \u00a9 \u00ae \u2122 § ¶ † ‡ µ #\n " ;
323+ static string specialCharacters => "! \" # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \\ ] ^ _ ` { | } ~ \n \u00b1 \u00d7 \u00f7 \u2211 \u220f \u2202 \u221e \u222b \u2248 \u2260 \u2264 \u2265 \u221a \u221b \u2206 \u2207 \u221d \n $ \u00a2 \u00a3 \u00a5 \u20ac \u20b9 \u20a9 \u20b1 \u20aa \u20bf \n • ‣ … ′ ″ ‘ ’ “ ” ‽ ¡ ¿ – — ― \n ( ) [ ] { } ⟨ ⟩ « » ‘ ’ “ ” \n \u2190 \u2191 \u2192 \u2193 \u2194 \u2195 \u2196 \u2197 \u2198 \u2199 \u2b05 \u2b06 \u2b07 \u27a1 \u27f3 \n α β γ δ ε ζ η θ ι κ λ μ ν ξ ο π ρ σ τ υ φ χ ψ ω \n \u00a9 \u00ae \u2122 § ¶ † ‡ µ #" ;
317324
318325 [ TestCase ( FeatureToggle . BashParametersArrayFeatureToggle ) ]
319326 [ TestCase ( null ) ]
@@ -336,8 +343,9 @@ public void ShouldBeAbleToEnumerateVariableValues(FeatureToggle? featureToggle)
336343 [ "VariableName \n 11" ] = "Value \n 11" ,
337344 [ "VariableName.prop.anotherprop 12" ] = "Value.prop.12" ,
338345 [ "VariableName`prop`anotherprop` 13" ] = "Value`prop`13" ,
346+ [ "VariableName 14 😭🙈👀" ] = "Value 14 😭🙈👀" ,
339347 [ specialCharacters ] = specialCharacters
340- } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
348+ } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
341349
342350 output . AssertSuccess ( ) ;
343351 if ( featureToggle == FeatureToggle . BashParametersArrayFeatureToggle )
@@ -366,9 +374,70 @@ public void ShouldBeAbleToEnumerateVariableValues(FeatureToggle? featureToggle)
366374
367375 output . AssertOutput ( "Key: VariableName.prop.anotherprop 12, Value: Value.prop.12" ) ;
368376 output . AssertOutput ( "Key: VariableName`prop`anotherprop` 13, Value: Value`prop`13" ) ;
377+ output . AssertOutput ( "Key: VariableName 14 😭🙈👀, Value: Value 14 😭🙈👀" ) ;
369378 output . AssertOutput ( $ "Key: { specialCharacters } , Value: { specialCharacters } ") ;
370379 }
371380 }
381+ [ TestCase ( FeatureToggle . BashParametersArrayFeatureToggle ) ]
382+ [ TestCase ( null ) ]
383+ [ RequiresBashDotExeIfOnWindows ]
384+ public void ShouldBeAbleToEnumerateLargeVariableSetsEfficiently ( FeatureToggle ? featureToggle )
385+ {
386+ // Create a dictionary with 10,000 variables with diverse characters
387+ var variables = new Dictionary < string , string > ( ) ;
388+ var random = new Random ( 42 ) ; // Seed for reproducibility
389+
390+ // Generate 10,000 unique variables with diverse content
391+ for ( int i = 0 ; i < 10000 ; i ++ )
392+ {
393+ string key = $ "Key{ i } _{ Guid . NewGuid ( ) . ToString ( "N" ) } ";
394+ string value = $ "Value{ i } _{ Convert . ToBase64String ( Guid . NewGuid ( ) . ToByteArray ( ) ) } ";
395+
396+ // Mix in some random Unicode characters
397+ if ( random . Next ( 5 ) == 0 )
398+ {
399+ key += ( char ) random . Next ( 0x1F600 , 0x1F64F ) ; // Emoji range
400+ value += Environment . NewLine + ( char ) random . Next ( 0x2600 , 0x26FF ) ; // Unicode symbols
401+ }
402+
403+ variables [ key ] = value ;
404+ }
405+
406+ var sw = Stopwatch . StartNew ( ) ;
407+ // Run the script with all these variables
408+ var ( output , _) = RunScript ( "enumerate-variables.sh" ,
409+ variables . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
410+ sw . Stop ( ) ;
411+ sw . Elapsed . TotalMilliseconds . Should ( ) . BeLessThan ( 2000 ) ;
412+
413+ output . AssertSuccess ( ) ;
414+ if ( featureToggle == FeatureToggle . BashParametersArrayFeatureToggle )
415+ {
416+ var fullOutput = string . Join ( Environment . NewLine , output . CapturedOutput . Infos ) ;
417+ if ( fullOutput . Contains ( "Bash version 4.2 or later is required to use octopus_parameters" ) )
418+ {
419+ output . AssertOutput ( "Still ran this script" ) ;
420+ return ;
421+ }
422+
423+ // Get all output lines that start with "Key: "
424+ var outputLines = output . CapturedOutput . Infos
425+ . Where ( line => line . StartsWith ( "Key: " ) )
426+ . ToList ( ) ;
427+
428+ // Verify count matches
429+ Assert . That ( outputLines . Count , Is . EqualTo ( variables . Count ) ,
430+ "Not all variables were processed" ) ;
431+
432+ // For each variable, construct the expected output format and verify it exists
433+ foreach ( var kvp in variables )
434+ {
435+ string expectedOutput = $ "Key: { kvp . Key } , Value: { kvp . Value } ";
436+ Assert . That ( outputLines . Contains ( expectedOutput ) ,
437+ $ "Expected output line not found: '{ expectedOutput } '") ;
438+ }
439+ }
440+ }
372441 }
373442
374443 public static class AdditionalVariablesExtensions
0 commit comments