| File: EnvVarEscaperTests.cs | Web Access |
| Project: src\tests\Aspire.Hosting.Docker.Tests\Aspire.Hosting.Docker.Tests.csproj (Aspire.Hosting.Docker.Tests) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text; namespace Aspire.Hosting.Docker.Tests; public class EnvVarEscaperTests { [Theory] [InlineData("$FOO", "$$FOO")] // Simple form [InlineData("${FOO}", "$${FOO}")] // Braced form [InlineData("${FOO:-default}", "$${FOO:-default}")] // Default value [InlineData("${ FOO }", "$${FOO}")] // Whitespace in braces [InlineData("$$FOO", "$$FOO")] // Already escaped [InlineData("$${FOO}", "$${FOO}")] // Already escaped braced [InlineData("", "")] // Empty string [InlineData("no vars here", "no vars here")] // No variables [InlineData("$FOO$BAR", "$$FOO$$BAR")] // Multiple variables [InlineData("${FOO}${BAR}", "$${FOO}$${BAR}")] // Multiple braced [InlineData("${FOO_BAR}", "$${FOO_BAR}")] // Underscore in name [InlineData("prefix$FOO", "prefix$$FOO")] // With prefix [InlineData("$FOO:5000", "$$FOO:5000")] // With port number public void EscapeUnescapedEnvVars_HandlesVariousPatterns(string input, string expected) { var result = EscapeUnescapedEnvVars(input); Assert.Equal(expected, result); } [Theory] [InlineData("$1FOO")] // Starts with number [InlineData("${1FOO}")] // Braced starts with number [InlineData("$FOO-BAR")] // Invalid character in simple [InlineData("${FOO-BAR}")] // Invalid character in braced [InlineData("${FOO{BAR}}")] // Nested braces public void EscapeUnescapedEnvVars_IgnoresInvalidNames(string input) { var result = EscapeUnescapedEnvVars(input); Assert.Equal(input, result); } [Theory] [InlineData("Value is ${FOO:-default}", "Value is $${FOO:-default}")] [InlineData("${FOO:-${BAR}}", "$${FOO:-$${BAR}}")] [InlineData("${FOO:-$$BAR}", "$${FOO:-$$BAR}")] public void EscapeUnescapedEnvVars_HandlesDefaultValues(string input, string expected) { var result = EscapeUnescapedEnvVars(input); Assert.Equal(expected, result); } [Theory] [InlineData("${FOO_$BAR}", "$${FOO_$$BAR}")] [InlineData("${FOO_${BAR}}", "$${FOO_$${BAR}}")] [InlineData("$FOO_$BAR", "$$FOO_$$BAR")] public void EscapeUnescapedEnvVars_HandlesNestedVariables(string input, string expected) { var result = EscapeUnescapedEnvVars(input); Assert.Equal(expected, result); } [Fact] public void EscapeUnescapedEnvVars_ThrowsOnExcessiveInputLength() { var input = new string('x', 1_000_001); var ex = Assert.Throws<ArgumentException>(() => EscapeUnescapedEnvVars(input)); Assert.Contains("exceeds maximum allowed length", ex.Message); } [Fact] public void EscapeUnescapedEnvVars_ThrowsOnExcessiveContentLength() { var content = new string('x', 100_001); var input = "${" + content + "}"; var ex = Assert.Throws<ArgumentException>(() => EscapeUnescapedEnvVars(input)); Assert.Contains("exceeds maximum allowed length", ex.Message); } [Fact] public void EscapeUnescapedEnvVars_ThrowsOnExcessiveRecursion() { var input = "${FOO:-${BAR:-${BAZ:-${QUX:-${AAA:-${BBB:-${CCC:-${DDD:-${EEE:-${FFF:-${GGG:-${HHH:-${III:-${JJJ:-${KKK:-${LLL:-${MMM:-${NNN:-${OOO:-${PPP:-${QQQ:-${RRR:-${SSS:-${TTT:-${UUU:-${VVV:-${WWW:-${XXX:-${YYY:-${ZZZ:-${AAA:-${BBB:-${CCC:-${DDD:-${EEE:-${FFF:-${GGG:-${HHH:-${III:-${JJJ:-${KKK:-${LLL:-${MMM:-${NNN:-${OOO:-${PPP:-${QQQ:-${RRR:-${SSS:-${TTT:-${UUU}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"; var ex = Assert.Throws<InvalidOperationException>(() => EscapeUnescapedEnvVars(input)); Assert.Contains("Maximum recursion depth exceeded", ex.Message); } /// <summary> /// Main entry point for escaping unescaped environment variables in a string. /// </summary> private static string EscapeUnescapedEnvVars(string input) { var result = new StringBuilder(); EnvVarEscaper.EscapeUnescapedEnvVars(input.AsSpan(), result); return result.ToString(); } }