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,21 +65,21 @@ 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 {
6972 output . AssertSuccess ( ) ;
7073 output . AssertOutput ( "##octopus[progress percentage='NTA=' message='SGFsZiBXYXk=']" ) ;
7174 } ) ;
7275 }
73-
76+
7477 [ TestCase ( FeatureToggle . BashParametersArrayFeatureToggle ) ]
7578 [ TestCase ( null ) ]
7679 [ RequiresBashDotExeIfOnWindows ]
7780 public void ShouldReportKubernetesManifest ( FeatureToggle ? featureToggle )
7881 {
79- var ( output , _) = RunScript ( "report-kubernetes-manifest.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
82+ var ( output , _) = RunScript ( "report-kubernetes-manifest.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
8083
8184 Assert . Multiple ( ( ) =>
8285 {
@@ -87,7 +90,7 @@ public void ShouldReportKubernetesManifest(FeatureToggle? featureToggle)
8790 output . AssertOutput ( "##octopus[k8s-manifest-applied manifest='ImFwaVZlcnNpb24iOiAidjEiXG4ia2luZCI6ICJOYW1lc3BhY2UiXG4ibWV0YWRhdGEiOlxuICAibmFtZSI6ICJkaWZmcyJcbiJsYWJlbHMiOlxuICAgICJuYW1lIjogImRpZmZzIlxu' ns='bXk=']" ) ;
8891 } ) ;
8992 }
90-
93+
9194 [ TestCase ( FeatureToggle . BashParametersArrayFeatureToggle ) ]
9295 [ TestCase ( null ) ]
9396 [ RequiresBashDotExeIfOnWindows ]
@@ -107,20 +110,23 @@ public void ShouldReportKubernetesManifestFile(FeatureToggle? featureToggle)
107110 ""name"": ""diffs""
108111""labels"":
109112 ""name"": ""diffs""" . ReplaceLineEndings ( "\n " ) ;
110-
113+
111114 var filePath = Path . Combine ( tempPath , "ShouldWriteServiceMessageForKubernetesManifestFile.manifest.yaml" ) ;
112115 File . WriteAllText ( filePath , manifest ) ;
113116
114117 //if we are running on windows, we must be running via bash.exe, so we need to translate this to a wsl path
115118 var updatedFilePath = filePath ;
116119 if ( CalamariEnvironment . IsRunningOnWindows )
117120 {
118- var qualifiedPath = filePath . Replace ( @"\" , @"\\" ) ;
121+ var qualifiedPath = filePath . Replace ( @"\" , @"\\" ) ;
119122
120123 var path = string . Empty ;
121- var result = SilentProcessRunner . ExecuteCommand ( "wsl" , $ "wslpath -a -u { qualifiedPath } ", tempPath , output => path = output ,
124+ var result = SilentProcessRunner . ExecuteCommand ( "wsl" ,
125+ $ "wslpath -a -u { qualifiedPath } ",
126+ tempPath ,
127+ output => path = output ,
122128 _ => { } ) ;
123-
129+
124130 if ( result . ExitCode != 0 )
125131 {
126132 Assert . Fail ( "Failed to convert windows path to WSL path" ) ;
@@ -135,7 +141,6 @@ public void ShouldReportKubernetesManifestFile(FeatureToggle? featureToggle)
135141
136142 try
137143 {
138-
139144 var ( output , _) = RunScript ( "report-kubernetes-manifest-file.sh" , additionalVariables ) ;
140145
141146 Assert . Multiple ( ( ) =>
@@ -160,7 +165,7 @@ public void ShouldConsumeParametersWithQuotes(FeatureToggle? featureToggle)
160165 {
161166 var ( output , _) = RunScript ( "parameters.sh" ,
162167 new Dictionary < string , string > ( )
163- { [ SpecialVariables . Action . Script . ScriptParameters ] = "\" Para meter0\" 'Para meter1'" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
168+ { [ SpecialVariables . Action . Script . ScriptParameters ] = "\" Para meter0\" 'Para meter1'" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
164169
165170 Assert . Multiple ( ( ) =>
166171 {
@@ -174,8 +179,10 @@ public void ShouldConsumeParametersWithQuotes(FeatureToggle? featureToggle)
174179 [ RequiresBashDotExeIfOnWindows ]
175180 public void ShouldNotReceiveParametersIfNoneProvided ( FeatureToggle ? featureToggle )
176181 {
177- var ( output , _) = RunScript ( "parameters.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
178- "5XETGOgqYR2bRhlfhDruEg==" ) ;
182+ var ( output , _) = RunScript ( "parameters.sh" ,
183+ new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
184+ sensitiveVariablesPassword :
185+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
179186
180187 Assert . Multiple ( ( ) =>
181188 {
@@ -197,7 +204,7 @@ public void ShouldCallHello(FeatureToggle? featureToggle)
197204 [ "Variable3" ] = "GHI" ,
198205 [ "Foo_bar" ] = "Hello" ,
199206 [ "Host" ] = "Never" ,
200- } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
207+ } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
201208
202209 Assert . Multiple ( ( ) =>
203210 {
@@ -213,8 +220,9 @@ public void ShouldCallHelloWithSensitiveVariable(FeatureToggle? featureToggle)
213220 {
214221 var ( output , _) = RunScript ( "hello.sh" ,
215222 new Dictionary < string , string > ( )
216- { [ "Name" ] = "NameToEncrypt" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
217- "5XETGOgqYR2bRhlfhDruEg==" ) ;
223+ { [ "Name" ] = "NameToEncrypt" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
224+ sensitiveVariablesPassword :
225+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
218226
219227 Assert . Multiple ( ( ) =>
220228 {
@@ -230,7 +238,7 @@ public void ShouldCallHelloWithNullVariable(FeatureToggle? featureToggle)
230238 {
231239 var ( output , _) = RunScript ( "hello.sh" ,
232240 new Dictionary < string , string > ( )
233- { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
241+ { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
234242
235243 Assert . Multiple ( ( ) =>
236244 {
@@ -246,8 +254,9 @@ public void ShouldCallHelloWithNullSensitiveVariable(FeatureToggle? featureToggl
246254 {
247255 var ( output , _) = RunScript ( "hello.sh" ,
248256 new Dictionary < string , string > ( )
249- { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) , sensitiveVariablesPassword :
250- "5XETGOgqYR2bRhlfhDruEg==" ) ;
257+ { [ "Name" ] = null } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ,
258+ sensitiveVariablesPassword :
259+ "5XETGOgqYR2bRhlfhDruEg==" ) ;
251260
252261 Assert . Multiple ( ( ) =>
253262 {
@@ -262,7 +271,7 @@ public void ShouldCallHelloWithNullSensitiveVariable(FeatureToggle? featureToggl
262271 public void ShouldNotFailOnStdErr ( FeatureToggle ? featureToggle )
263272 {
264273 var ( output , _) = RunScript ( "stderr.sh" ,
265- new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
274+ new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
266275
267276 Assert . Multiple ( ( ) =>
268277 {
@@ -278,7 +287,7 @@ public void ShouldFailOnStdErrWithTreatScriptWarningsAsErrors(FeatureToggle? fea
278287 {
279288 var ( output , _) = RunScript ( "stderr.sh" ,
280289 new Dictionary < string , string > ( )
281- { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
290+ { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
282291
283292 Assert . Multiple ( ( ) =>
284293 {
@@ -294,7 +303,7 @@ public void ShouldNotFailOnStdErrFromServiceMessagesWithTreatScriptWarningsAsErr
294303 {
295304 var ( output , _) = RunScript ( "hello.sh" ,
296305 new Dictionary < string , string > ( )
297- { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
306+ { [ SpecialVariables . Action . FailScriptOnErrorOutput ] = "True" } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
298307
299308 output . AssertSuccess ( ) ;
300309 }
@@ -304,7 +313,7 @@ public void ShouldNotFailOnStdErrFromServiceMessagesWithTreatScriptWarningsAsErr
304313 [ TestCase ( null ) ]
305314 public void ShouldSupportStrictVariableUnset ( FeatureToggle ? featureToggle )
306315 {
307- var ( output , _) = RunScript ( "strict-mode.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
316+ var ( output , _) = RunScript ( "strict-mode.sh" , new Dictionary < string , string > ( ) . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
308317
309318 Assert . Multiple ( ( ) =>
310319 {
@@ -336,8 +345,9 @@ public void ShouldBeAbleToEnumerateVariableValues(FeatureToggle? featureToggle)
336345 [ "VariableName \n 11" ] = "Value \n 11" ,
337346 [ "VariableName.prop.anotherprop 12" ] = "Value.prop.12" ,
338347 [ "VariableName`prop`anotherprop` 13" ] = "Value`prop`13" ,
348+ [ "VariableName 14 😭🙈👀" ] = "Value 14 😭🙈👀" ,
339349 [ specialCharacters ] = specialCharacters
340- } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
350+ } . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
341351
342352 output . AssertSuccess ( ) ;
343353 if ( featureToggle == FeatureToggle . BashParametersArrayFeatureToggle )
@@ -366,9 +376,75 @@ public void ShouldBeAbleToEnumerateVariableValues(FeatureToggle? featureToggle)
366376
367377 output . AssertOutput ( "Key: VariableName.prop.anotherprop 12, Value: Value.prop.12" ) ;
368378 output . AssertOutput ( "Key: VariableName`prop`anotherprop` 13, Value: Value`prop`13" ) ;
379+ output . AssertOutput ( "Key: VariableName 14 😭🙈👀, Value: Value 14 😭🙈👀" ) ;
369380 output . AssertOutput ( $ "Key: { specialCharacters } , Value: { specialCharacters } ") ;
370381 }
371382 }
383+
384+ [ TestCase ( FeatureToggle . BashParametersArrayFeatureToggle ) ]
385+ [ TestCase ( null ) ]
386+ [ RequiresBashDotExeIfOnWindows ]
387+ public void ShouldBeAbleToEnumerateLargeVariableSetsEfficiently ( FeatureToggle ? featureToggle )
388+ {
389+ // Create a dictionary with 10,000 variables with diverse characters
390+ var variables = new Dictionary < string , string > ( ) ;
391+ var random = new Random ( 42 ) ; // Seed for reproducibility
392+
393+ // Generate 10,000 unique variables with diverse content
394+ for ( int i = 0 ; i < 10000 ; i ++ )
395+ {
396+ string key = $ "Key{ i } _{ Guid . NewGuid ( ) . ToString ( "N" ) } ";
397+ string value = $ "Value{ i } _{ Convert . ToBase64String ( Guid . NewGuid ( ) . ToByteArray ( ) ) } ";
398+
399+ // Mix in some random Unicode characters
400+ if ( random . Next ( 5 ) == 0 )
401+ {
402+ key += ( char ) random . Next ( 0x1F600 , 0x1F64F ) ; // Emoji range
403+ value += Environment . NewLine + ( char ) random . Next ( 0x2600 , 0x26FF ) ; // Unicode symbols
404+ }
405+
406+ variables [ key ] = value ;
407+ }
408+
409+ var sw = Stopwatch . StartNew ( ) ;
410+ // Run the script with all these variables
411+ var ( output , _) = RunScript ( "enumerate-variables.sh" ,
412+ variables . AddFeatureToggleToDictionary ( new List < FeatureToggle ? > { featureToggle } ) ) ;
413+ sw . Stop ( ) ;
414+ // This depends on the running machine, locally ~1000ms, in CI sometimes this is ~2000ms. We're being very conservative
415+ // but if there's a scenario where this test fails this should be increased. This test exists because there are
416+ // potential performance problems in encoding/decoding of variables in bash, this is a sanity check.
417+ sw . Elapsed . TotalMilliseconds . Should ( ) . BeLessThan ( 4000 ) ;
418+
419+ output . AssertSuccess ( ) ;
420+ if ( featureToggle == FeatureToggle . BashParametersArrayFeatureToggle )
421+ {
422+ var fullOutput = string . Join ( Environment . NewLine , output . CapturedOutput . Infos ) ;
423+ if ( fullOutput . Contains ( "Bash version 4.2 or later is required to use octopus_parameters" ) )
424+ {
425+ output . AssertOutput ( "Still ran this script" ) ;
426+ return ;
427+ }
428+
429+ // Get all output lines that start with "Key: "
430+ var outputLines = output . CapturedOutput . Infos
431+ . Where ( line => line . StartsWith ( "Key: " ) )
432+ . ToList ( ) ;
433+
434+ // Verify count matches
435+ Assert . That ( outputLines . Count ,
436+ Is . EqualTo ( variables . Count ) ,
437+ "Not all variables were processed" ) ;
438+
439+ // For each variable, construct the expected output format and verify it exists
440+ foreach ( var kvp in variables )
441+ {
442+ string expectedOutput = $ "Key: { kvp . Key } , Value: { kvp . Value } ";
443+ Assert . That ( outputLines . Contains ( expectedOutput ) ,
444+ $ "Expected output line not found: '{ expectedOutput } '") ;
445+ }
446+ }
447+ }
372448 }
373449
374450 public static class AdditionalVariablesExtensions
@@ -379,4 +455,4 @@ public static Dictionary<string, string> AddFeatureToggleToDictionary(this Dicti
379455 return variables ;
380456 }
381457 }
382- }
458+ }
0 commit comments