From d934cb8d2fabb12fc5fe47764e988858ffc89cd2 Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Thu, 23 Oct 2025 14:36:12 +0800 Subject: [PATCH 1/8] add those functions --- .../api/Azure.Provisioning.net8.0.cs | 2 + .../api/Azure.Provisioning.netstandard2.0.cs | 2 + .../src/Expressions/BicepFunction.cs | 97 ++++++++++++++++++- 3 files changed, 96 insertions(+), 5 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net8.0.cs b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net8.0.cs index 543483f7e43b..c81afcb29c65 100644 --- a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net8.0.cs +++ b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.net8.0.cs @@ -589,7 +589,9 @@ public static partial class BicepFunction public static Azure.Provisioning.BicepValue Concat(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.BicepValue CreateGuid(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.ArmDeployment GetDeployment() { throw null; } + public static Azure.Provisioning.BicepValue GetExtensionResourceId(Azure.Provisioning.BicepValue resourceId, Azure.Provisioning.BicepValue resourceType, params Azure.Provisioning.BicepValue[] resourceNames) { throw null; } public static Azure.Provisioning.Resources.ResourceGroup GetResourceGroup() { throw null; } + public static Azure.Provisioning.BicepValue GetResourceId(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.Subscription GetSubscription() { throw null; } public static Azure.Provisioning.BicepValue GetSubscriptionResourceId(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.Tenant GetTenant() { throw null; } diff --git a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs index 91bc209c5029..b54a6db93c65 100644 --- a/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs +++ b/sdk/provisioning/Azure.Provisioning/api/Azure.Provisioning.netstandard2.0.cs @@ -589,7 +589,9 @@ public static partial class BicepFunction public static Azure.Provisioning.BicepValue Concat(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.BicepValue CreateGuid(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.ArmDeployment GetDeployment() { throw null; } + public static Azure.Provisioning.BicepValue GetExtensionResourceId(Azure.Provisioning.BicepValue resourceId, Azure.Provisioning.BicepValue resourceType, params Azure.Provisioning.BicepValue[] resourceNames) { throw null; } public static Azure.Provisioning.Resources.ResourceGroup GetResourceGroup() { throw null; } + public static Azure.Provisioning.BicepValue GetResourceId(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.Subscription GetSubscription() { throw null; } public static Azure.Provisioning.BicepValue GetSubscriptionResourceId(params Azure.Provisioning.BicepValue[] values) { throw null; } public static Azure.Provisioning.Resources.Tenant GetTenant() { throw null; } diff --git a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs index cc30cc500428..ebecec55a3b6 100644 --- a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs +++ b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs @@ -5,7 +5,6 @@ using System.Linq; using Azure.Core; using Azure.Provisioning.Resources; -using Azure.Provisioning.Utilities; namespace Azure.Provisioning.Expressions; @@ -49,7 +48,8 @@ public static class BicepFunction /// public static BicepValue GetUniqueString(params BicepValue[] values) { - if (values.Length < 1) { throw new ArgumentException($"{nameof(GetUniqueString)} requires at least one value.", nameof(values)); } + if (values.Length < 1) + { throw new ArgumentException($"{nameof(GetUniqueString)} requires at least one value.", nameof(values)); } return BicepSyntax.Call("uniqueString", values.Select(v => v.Compile()).ToArray()); } @@ -110,7 +110,10 @@ public static BicepValue Take(BicepValue text, BicepValue s /// public static BicepValue CreateGuid(params BicepValue[] values) { - if (values.Length < 1) { throw new ArgumentException($"{nameof(CreateGuid)} requires at least one value.", nameof(values)); } + if (values.Length < 1) + { + throw new ArgumentException($"{nameof(CreateGuid)} requires at least one value.", nameof(values)); + } return BicepSyntax.Call("guid", values.Select(v => v.Compile()).ToArray()); } @@ -143,12 +146,93 @@ public static BicepValue CreateGuid(params BicepValue[] values) /// Bicep Functions Reference for more. /// /// + /// When is empty or contains fewer than two elements. public static BicepValue GetSubscriptionResourceId(params BicepValue[] values) { - if (values.Length < 2) { throw new ArgumentException($"{nameof(GetSubscriptionResourceId)} requires at least two values.", nameof(values)); } + if (values.Length < 2) + { + throw new ArgumentException($"{nameof(GetSubscriptionResourceId)} requires at least two values.", nameof(values)); + } return BicepSyntax.Call("subscriptionResourceId", values.Select(v => v.Compile()).ToArray()); } + /// + /// Returns the unique identifier of a resource. This represents the resourceId + /// Bicep function. + /// + /// + /// Optional subscription id, resource group name, resource types, and resource names used to + /// construct the resource ID. At least two values are required. + /// + /// + /// The unique identifier of the resource. + /// + /// + /// + /// You use this function when the resource name is ambiguous or not provisioned within the same Bicep file. + /// The format of the returned identifier varies based on whether the deployment happens at the scope of + /// a resource group, subscription, management group, or tenant. + /// + /// + /// See the + /// + /// Bicep Functions Reference for more. + /// + /// + /// When is empty or contains fewer than two elements. + public static BicepValue GetResourceId(params BicepValue[] values) + { + if (values.Length < 2) + { + throw new ArgumentException($"{nameof(GetResourceId)} requires at least two values.", nameof(values)); + } + return BicepSyntax.Call("resourceId", values.Select(v => v.Compile()).ToArray()); + } + + /// + /// Returns the resource ID for an extension resource. An extension resource is a resource type + /// that is applied to another resource to add to its capabilities. + /// This represents the extensionResourceId Bicep function. + /// + /// The resource ID to be extended on. + /// The resource type. + /// The resource names. + /// + /// The resource ID for an extension resource. + /// + /// + /// + /// The basic format of the resource ID returned by this function is: + /// {scope}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{ extensionResourceName} + /// The scope segment varies by the resource being extended. + /// When the extension resource is applied to a resource, the resource ID is returned in the following format: + /// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{baseResourceProviderNamespace}/{baseResourceType}/{baseResourceName}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} + /// When the extension resource is applied to a resource group, the format is: + /// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} + /// When the extension resource is applied to a subscription, the format is: + /// /subscriptions/{subscriptionId}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} + /// When the extension resource is applied to a management group, the format is: + /// /providers/Microsoft.Management/managementGroups/{managementGroupName}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} + /// + /// + /// See the + /// + /// Bicep Functions Reference for more. + /// + /// + /// When is empty. + public static BicepValue GetExtensionResourceId( + BicepValue resourceId, + BicepValue resourceType, + params BicepValue[] resourceNames) + { + if (resourceNames.Length < 1) + { + throw new ArgumentException($"{nameof(GetExtensionResourceId)} requires at least one element for resourceNames parameter.", nameof(resourceNames)); + } + return BicepSyntax.Call("extensionResourceId", [resourceId.Compile(), resourceType.Compile(), .. resourceNames.Select(v => v.Compile())]); + } + /// /// Returns information about the current deployment operation. This /// represents the deployment Bicep function. @@ -305,7 +389,10 @@ public static BicepValue ToUpper(BicepValue value) => /// public static BicepValue Concat(params BicepValue[] values) { - if (values.Length < 1) { throw new ArgumentException($"{nameof(Concat)} requires at least one value.", nameof(values)); } + if (values.Length < 1) + { + throw new ArgumentException($"{nameof(Concat)} requires at least one value.", nameof(values)); + } return BicepSyntax.Call("concat", values.Select(v => v.Compile()).ToArray()); } From 3741aeaabf6c6018c1042f97be9ff82641d0f346 Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 23 Oct 2025 14:45:21 +0800 Subject: [PATCH 2/8] Update sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Azure.Provisioning/src/Expressions/BicepFunction.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs index ebecec55a3b6..b1b94d53a3b4 100644 --- a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs +++ b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs @@ -203,7 +203,7 @@ public static BicepValue GetResourceId(params BicepValue /// /// The basic format of the resource ID returned by this function is: - /// {scope}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{ extensionResourceName} + /// {scope}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} /// The scope segment varies by the resource being extended. /// When the extension resource is applied to a resource, the resource ID is returned in the following format: /// /subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/{baseResourceProviderNamespace}/{baseResourceType}/{baseResourceName}/providers/{extensionResourceProviderNamespace}/{extensionResourceType}/{extensionResourceName} From 14d7d36a19fc1c201a4b8d1fb1a9918c57f0c77d Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Thu, 23 Oct 2025 14:46:12 +0800 Subject: [PATCH 3/8] clean up --- .../Azure.Provisioning/src/Expressions/BicepFunction.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs index b1b94d53a3b4..576fc57c2176 100644 --- a/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs +++ b/sdk/provisioning/Azure.Provisioning/src/Expressions/BicepFunction.cs @@ -49,7 +49,9 @@ public static class BicepFunction public static BicepValue GetUniqueString(params BicepValue[] values) { if (values.Length < 1) - { throw new ArgumentException($"{nameof(GetUniqueString)} requires at least one value.", nameof(values)); } + { + throw new ArgumentException($"{nameof(GetUniqueString)} requires at least one value.", nameof(values)); + } return BicepSyntax.Call("uniqueString", values.Select(v => v.Compile()).ToArray()); } From 6c5f7697720872b2622345197ec865841700e497 Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Fri, 24 Oct 2025 13:41:00 +0800 Subject: [PATCH 4/8] update test case --- .../tests/Expressions/BicepFunctionTests.cs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs index 8bda6d034080..cab491afaf90 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs @@ -16,7 +16,7 @@ public void Interpolate_WithLiteralText_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Hello, World!"); // Assert - Assert.AreEqual("'Hello, World!'", result.ToString()); + TestHelpers.AssertExpression("'Hello, World!'", result); } [Test] @@ -29,7 +29,7 @@ public void Interpolate_WithSingleVariable_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Value: {variable}"); // Assert - Assert.AreEqual("'Value: ${myVar}'", result.ToString()); + TestHelpers.AssertExpression("'Value: ${myVar}'", result); } [Test] @@ -43,7 +43,7 @@ public void Interpolate_WithMultipleVariables_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Name: {nameVar}, Value: {valueVar}"); // Assert - Assert.AreEqual("'Name: ${name}, Value: ${value}'", result.ToString()); + TestHelpers.AssertExpression("'Name: ${name}, Value: ${value}'", result); } [Test] @@ -56,7 +56,7 @@ public void Interpolate_WithBicepExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Resource Group: {expression}"); // Assert - Assert.AreEqual("'Resource Group: ${resourceGroup}'", result.ToString()); + TestHelpers.AssertExpression("'Resource Group: ${resourceGroup}'", result); } [Test] @@ -69,7 +69,7 @@ public void Interpolate_WithBicepValue_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Static: {bicepValue}"); // Assert - Literal BicepValues get compiled as string literals, not expressions - Assert.AreEqual("'Static: test-value'", result.ToString()); + TestHelpers.AssertExpression("'Static: test-value'", result); } [Test] @@ -84,7 +84,7 @@ public void Interpolate_WithMixedContent_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Prefix-{variable}-{expression}-{bicepValue}"); // Assert - Literal BicepValues get compiled as string literals, not expressions - Assert.AreEqual("'Prefix-${location}-${resourceGroup}-suffix'", result.ToString()); + TestHelpers.AssertExpression("'Prefix-${location}-${resourceGroup}-suffix'", result); } [Test] @@ -98,7 +98,7 @@ public void Interpolate_WithNestedFormattableString_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Outer: {nested}"); // Assert - Assert.AreEqual("'Outer: nested-${name}'", result.ToString()); + TestHelpers.AssertExpression("'Outer: nested-${name}'", result); } [Test] @@ -111,7 +111,7 @@ public void Interpolate_WithNullValue_HandlesGracefully() var result = BicepFunction.Interpolate($"Value: {nullValue}"); // Assert - Assert.AreEqual("'Value: '", result.ToString()); + TestHelpers.AssertExpression("'Value: '", result); } [Test] @@ -127,7 +127,7 @@ public void Interpolate_WithComplexExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Endpoint: {indexExpression}"); // Assert - Assert.AreEqual("'Endpoint: ${properties['endpoint']}'", result.ToString()); + TestHelpers.AssertExpression("'Endpoint: ${properties['endpoint']}'", result); } [Test] @@ -140,7 +140,7 @@ public void Interpolate_FormattableStringOverload_WithLiteralText_ReturnsCorrect var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'Hello, World!'", result.ToString()); + TestHelpers.AssertExpression("'Hello, World!'", result); } [Test] @@ -154,7 +154,7 @@ public void Interpolate_FormattableStringOverload_WithVariable_ReturnsCorrectFor var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'Variable: ${testVar}'", result.ToString()); + TestHelpers.AssertExpression("'Variable: ${testVar}'", result); } [Test] @@ -164,7 +164,7 @@ public void Interpolate_EmptyString_ReturnsCorrectFormat() var result = BicepFunction.Interpolate((FormattableString)$""); // Assert - Assert.AreEqual("''", result.ToString()); + TestHelpers.AssertExpression("''", result); } [Test] @@ -177,7 +177,7 @@ public void Interpolate_OnlyVariables_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"{variable}"); // Assert - Assert.AreEqual("'${onlyVar}'", result.ToString()); + TestHelpers.AssertExpression("'${onlyVar}'", result); } [Test] @@ -190,7 +190,7 @@ public void Interpolate_WithSpecialCharacters_EscapesCorrectly() var result = BicepFunction.Interpolate($"Path: {variable}\\folder\\file.txt"); // Assert - Assert.AreEqual("'Path: ${path}\\\\folder\\\\file.txt'", result.ToString()); + TestHelpers.AssertExpression("'Path: ${path}\\\\folder\\\\file.txt'", result); } [Test] @@ -203,7 +203,7 @@ public void Interpolate_WithQuotes_EscapesCorrectly() var result = BicepFunction.Interpolate($"Message: '{variable}' is quoted"); // Assert - Assert.AreEqual("'Message: \\'${message}\\' is quoted'", result.ToString()); + TestHelpers.AssertExpression("'Message: \\'${message}\\' is quoted'", result); } [Test] @@ -217,7 +217,7 @@ public void Interpolate_WithCSharpExpressions_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Count: {variable} x {multiplier}"); // Assert - Assert.AreEqual("'Count: ${count} x 2'", result.ToString()); + TestHelpers.AssertExpression("'Count: ${count} x 2'", result); } [Test] @@ -234,7 +234,7 @@ public void Interpolate_FormattableStringWithComplexArguments_ReturnsCorrectForm var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'RG: ${rgName} in ${location}'", result.ToString()); + TestHelpers.AssertExpression("'RG: ${rgName} in ${location}'", result); } [Test] @@ -247,7 +247,7 @@ public void Interpolate_WithBicepValueExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Dynamic: {bicepValue}"); // Assert - Assert.AreEqual("'Dynamic: ${dynamicValue}'", result.ToString()); + TestHelpers.AssertExpression("'Dynamic: ${dynamicValue}'", result); } [Test] @@ -260,7 +260,7 @@ public void Interpolate_WithSimpleInterpolation_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Hello {myVariable}!"); // Assert - Assert.AreEqual("'Hello ${myVariable}!'", result.ToString()); + TestHelpers.AssertExpression("'Hello ${myVariable}!'", result); } [Test] @@ -274,7 +274,7 @@ public void Interpolate_WithMultipleInterpolatedValues_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"{prefix}-{suffix}"); // Assert - Assert.AreEqual("'${prefix}-${suffix}'", result.ToString()); + TestHelpers.AssertExpression("'${prefix}-${suffix}'", result); } [Test] @@ -288,7 +288,7 @@ public void Interpolate_WithResourceReferences_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Creating resource '{resourceName}' in location '{location}'"); // Assert - Assert.AreEqual("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result.ToString()); + TestHelpers.AssertExpression("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result); } [Test] @@ -302,7 +302,7 @@ public void Interpolate_WithNumericInterpolation_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Server running on port {port} with timeout {timeout}s"); // Assert - Assert.AreEqual("'Server running on port ${port} with timeout 30s'", result.ToString()); + TestHelpers.AssertExpression("'Server running on port ${port} with timeout 30s'", result); } [Test] @@ -318,7 +318,7 @@ public void Interpolate_WithConditionalExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Config: {conditionalExpr}"); // Assert - Assert.AreEqual("'Config: ${isDevelopment ? devConfig : prodConfig}'", result.ToString()); + TestHelpers.AssertExpression("'Config: ${isDevelopment ? devConfig : prodConfig}'", result); } [Test] @@ -337,7 +337,7 @@ public void Interpolate_WithNestedFormattableInLiteralInterpolation_ReturnsCorre var result = BicepFunction.Interpolate($"Outer: {outerVar} -> {middleFormattable}"); // Assert - Assert.AreEqual("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result.ToString()); + TestHelpers.AssertExpression("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result); } [Test] @@ -357,7 +357,7 @@ public void Interpolate_WithFormattableStringContainingNestedFormattable_Returns var result = BicepFunction.Interpolate(level1Formattable); // Assert - Assert.AreEqual("'L1:${level1}->L2:${level2}->L3:${level3}'", result.ToString()); + TestHelpers.AssertExpression("'L1:${level1}->L2:${level2}->L3:${level3}'", result); } } } From 53c23157e3c2ac0946dec2c6d797383dcc7ffe57 Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Fri, 24 Oct 2025 13:41:00 +0800 Subject: [PATCH 5/8] update test case --- .../tests/Expressions/BicepFunctionTests.cs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs index 8bda6d034080..cab491afaf90 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs @@ -16,7 +16,7 @@ public void Interpolate_WithLiteralText_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Hello, World!"); // Assert - Assert.AreEqual("'Hello, World!'", result.ToString()); + TestHelpers.AssertExpression("'Hello, World!'", result); } [Test] @@ -29,7 +29,7 @@ public void Interpolate_WithSingleVariable_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Value: {variable}"); // Assert - Assert.AreEqual("'Value: ${myVar}'", result.ToString()); + TestHelpers.AssertExpression("'Value: ${myVar}'", result); } [Test] @@ -43,7 +43,7 @@ public void Interpolate_WithMultipleVariables_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Name: {nameVar}, Value: {valueVar}"); // Assert - Assert.AreEqual("'Name: ${name}, Value: ${value}'", result.ToString()); + TestHelpers.AssertExpression("'Name: ${name}, Value: ${value}'", result); } [Test] @@ -56,7 +56,7 @@ public void Interpolate_WithBicepExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Resource Group: {expression}"); // Assert - Assert.AreEqual("'Resource Group: ${resourceGroup}'", result.ToString()); + TestHelpers.AssertExpression("'Resource Group: ${resourceGroup}'", result); } [Test] @@ -69,7 +69,7 @@ public void Interpolate_WithBicepValue_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Static: {bicepValue}"); // Assert - Literal BicepValues get compiled as string literals, not expressions - Assert.AreEqual("'Static: test-value'", result.ToString()); + TestHelpers.AssertExpression("'Static: test-value'", result); } [Test] @@ -84,7 +84,7 @@ public void Interpolate_WithMixedContent_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Prefix-{variable}-{expression}-{bicepValue}"); // Assert - Literal BicepValues get compiled as string literals, not expressions - Assert.AreEqual("'Prefix-${location}-${resourceGroup}-suffix'", result.ToString()); + TestHelpers.AssertExpression("'Prefix-${location}-${resourceGroup}-suffix'", result); } [Test] @@ -98,7 +98,7 @@ public void Interpolate_WithNestedFormattableString_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Outer: {nested}"); // Assert - Assert.AreEqual("'Outer: nested-${name}'", result.ToString()); + TestHelpers.AssertExpression("'Outer: nested-${name}'", result); } [Test] @@ -111,7 +111,7 @@ public void Interpolate_WithNullValue_HandlesGracefully() var result = BicepFunction.Interpolate($"Value: {nullValue}"); // Assert - Assert.AreEqual("'Value: '", result.ToString()); + TestHelpers.AssertExpression("'Value: '", result); } [Test] @@ -127,7 +127,7 @@ public void Interpolate_WithComplexExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Endpoint: {indexExpression}"); // Assert - Assert.AreEqual("'Endpoint: ${properties['endpoint']}'", result.ToString()); + TestHelpers.AssertExpression("'Endpoint: ${properties['endpoint']}'", result); } [Test] @@ -140,7 +140,7 @@ public void Interpolate_FormattableStringOverload_WithLiteralText_ReturnsCorrect var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'Hello, World!'", result.ToString()); + TestHelpers.AssertExpression("'Hello, World!'", result); } [Test] @@ -154,7 +154,7 @@ public void Interpolate_FormattableStringOverload_WithVariable_ReturnsCorrectFor var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'Variable: ${testVar}'", result.ToString()); + TestHelpers.AssertExpression("'Variable: ${testVar}'", result); } [Test] @@ -164,7 +164,7 @@ public void Interpolate_EmptyString_ReturnsCorrectFormat() var result = BicepFunction.Interpolate((FormattableString)$""); // Assert - Assert.AreEqual("''", result.ToString()); + TestHelpers.AssertExpression("''", result); } [Test] @@ -177,7 +177,7 @@ public void Interpolate_OnlyVariables_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"{variable}"); // Assert - Assert.AreEqual("'${onlyVar}'", result.ToString()); + TestHelpers.AssertExpression("'${onlyVar}'", result); } [Test] @@ -190,7 +190,7 @@ public void Interpolate_WithSpecialCharacters_EscapesCorrectly() var result = BicepFunction.Interpolate($"Path: {variable}\\folder\\file.txt"); // Assert - Assert.AreEqual("'Path: ${path}\\\\folder\\\\file.txt'", result.ToString()); + TestHelpers.AssertExpression("'Path: ${path}\\\\folder\\\\file.txt'", result); } [Test] @@ -203,7 +203,7 @@ public void Interpolate_WithQuotes_EscapesCorrectly() var result = BicepFunction.Interpolate($"Message: '{variable}' is quoted"); // Assert - Assert.AreEqual("'Message: \\'${message}\\' is quoted'", result.ToString()); + TestHelpers.AssertExpression("'Message: \\'${message}\\' is quoted'", result); } [Test] @@ -217,7 +217,7 @@ public void Interpolate_WithCSharpExpressions_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Count: {variable} x {multiplier}"); // Assert - Assert.AreEqual("'Count: ${count} x 2'", result.ToString()); + TestHelpers.AssertExpression("'Count: ${count} x 2'", result); } [Test] @@ -234,7 +234,7 @@ public void Interpolate_FormattableStringWithComplexArguments_ReturnsCorrectForm var result = BicepFunction.Interpolate(formattable); // Assert - Assert.AreEqual("'RG: ${rgName} in ${location}'", result.ToString()); + TestHelpers.AssertExpression("'RG: ${rgName} in ${location}'", result); } [Test] @@ -247,7 +247,7 @@ public void Interpolate_WithBicepValueExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Dynamic: {bicepValue}"); // Assert - Assert.AreEqual("'Dynamic: ${dynamicValue}'", result.ToString()); + TestHelpers.AssertExpression("'Dynamic: ${dynamicValue}'", result); } [Test] @@ -260,7 +260,7 @@ public void Interpolate_WithSimpleInterpolation_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Hello {myVariable}!"); // Assert - Assert.AreEqual("'Hello ${myVariable}!'", result.ToString()); + TestHelpers.AssertExpression("'Hello ${myVariable}!'", result); } [Test] @@ -274,7 +274,7 @@ public void Interpolate_WithMultipleInterpolatedValues_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"{prefix}-{suffix}"); // Assert - Assert.AreEqual("'${prefix}-${suffix}'", result.ToString()); + TestHelpers.AssertExpression("'${prefix}-${suffix}'", result); } [Test] @@ -288,7 +288,7 @@ public void Interpolate_WithResourceReferences_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Creating resource '{resourceName}' in location '{location}'"); // Assert - Assert.AreEqual("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result.ToString()); + TestHelpers.AssertExpression("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result); } [Test] @@ -302,7 +302,7 @@ public void Interpolate_WithNumericInterpolation_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Server running on port {port} with timeout {timeout}s"); // Assert - Assert.AreEqual("'Server running on port ${port} with timeout 30s'", result.ToString()); + TestHelpers.AssertExpression("'Server running on port ${port} with timeout 30s'", result); } [Test] @@ -318,7 +318,7 @@ public void Interpolate_WithConditionalExpression_ReturnsCorrectFormat() var result = BicepFunction.Interpolate($"Config: {conditionalExpr}"); // Assert - Assert.AreEqual("'Config: ${isDevelopment ? devConfig : prodConfig}'", result.ToString()); + TestHelpers.AssertExpression("'Config: ${isDevelopment ? devConfig : prodConfig}'", result); } [Test] @@ -337,7 +337,7 @@ public void Interpolate_WithNestedFormattableInLiteralInterpolation_ReturnsCorre var result = BicepFunction.Interpolate($"Outer: {outerVar} -> {middleFormattable}"); // Assert - Assert.AreEqual("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result.ToString()); + TestHelpers.AssertExpression("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result); } [Test] @@ -357,7 +357,7 @@ public void Interpolate_WithFormattableStringContainingNestedFormattable_Returns var result = BicepFunction.Interpolate(level1Formattable); // Assert - Assert.AreEqual("'L1:${level1}->L2:${level2}->L3:${level3}'", result.ToString()); + TestHelpers.AssertExpression("'L1:${level1}->L2:${level2}->L3:${level3}'", result); } } } From f287930800e2ad6d2aa030615535bd4e358e9e01 Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Fri, 24 Oct 2025 13:43:59 +0800 Subject: [PATCH 6/8] refactor the structure --- .../tests/Expressions/BicepFunctionTests.cs | 363 ------------------ .../BicepFunctionTests/InterpolateTests.cs | 362 +++++++++++++++++ 2 files changed, 362 insertions(+), 363 deletions(-) delete mode 100644 sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs create mode 100644 sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs deleted file mode 100644 index cab491afaf90..000000000000 --- a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests.cs +++ /dev/null @@ -1,363 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using Azure.Provisioning.Expressions; -using NUnit.Framework; - -namespace Azure.Provisioning.Tests.Expressions -{ - public class BicepFunctionTests - { - [Test] - public void Interpolate_WithLiteralText_ReturnsCorrectFormat() - { - // Act - test literal interpolated string (cast to FormattableString to resolve ambiguity) - var result = BicepFunction.Interpolate($"Hello, World!"); - - // Assert - TestHelpers.AssertExpression("'Hello, World!'", result); - } - - [Test] - public void Interpolate_WithSingleVariable_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("myVar", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"Value: {variable}"); - - // Assert - TestHelpers.AssertExpression("'Value: ${myVar}'", result); - } - - [Test] - public void Interpolate_WithMultipleVariables_ReturnsCorrectFormat() - { - // Arrange - var nameVar = new ProvisioningVariable("name", typeof(string)); - var valueVar = new ProvisioningVariable("value", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"Name: {nameVar}, Value: {valueVar}"); - - // Assert - TestHelpers.AssertExpression("'Name: ${name}, Value: ${value}'", result); - } - - [Test] - public void Interpolate_WithBicepExpression_ReturnsCorrectFormat() - { - // Arrange - var expression = new IdentifierExpression("resourceGroup"); - - // Act - var result = BicepFunction.Interpolate($"Resource Group: {expression}"); - - // Assert - TestHelpers.AssertExpression("'Resource Group: ${resourceGroup}'", result); - } - - [Test] - public void Interpolate_WithBicepValue_ReturnsCorrectFormat() - { - // Arrange - BicepValue with literal strings are compiled as string literals in interpolation - var bicepValue = new BicepValue("test-value"); - - // Act - var result = BicepFunction.Interpolate($"Static: {bicepValue}"); - - // Assert - Literal BicepValues get compiled as string literals, not expressions - TestHelpers.AssertExpression("'Static: test-value'", result); - } - - [Test] - public void Interpolate_WithMixedContent_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("location", typeof(string)); - var expression = new IdentifierExpression("resourceGroup"); - var bicepValue = new BicepValue("suffix"); - - // Act - var result = BicepFunction.Interpolate($"Prefix-{variable}-{expression}-{bicepValue}"); - - // Assert - Literal BicepValues get compiled as string literals, not expressions - TestHelpers.AssertExpression("'Prefix-${location}-${resourceGroup}-suffix'", result); - } - - [Test] - public void Interpolate_WithNestedFormattableString_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("name", typeof(string)); - FormattableString nested = $"nested-{variable}"; - - // Act - var result = BicepFunction.Interpolate($"Outer: {nested}"); - - // Assert - TestHelpers.AssertExpression("'Outer: nested-${name}'", result); - } - - [Test] - public void Interpolate_WithNullValue_HandlesGracefully() - { - // Arrange - string? nullValue = null; - - // Act - var result = BicepFunction.Interpolate($"Value: {nullValue}"); - - // Assert - TestHelpers.AssertExpression("'Value: '", result); - } - - [Test] - public void Interpolate_WithComplexExpression_ReturnsCorrectFormat() - { - // Arrange - var indexExpression = new IndexExpression( - new IdentifierExpression("properties"), - new StringLiteralExpression("endpoint") - ); - - // Act - var result = BicepFunction.Interpolate($"Endpoint: {indexExpression}"); - - // Assert - TestHelpers.AssertExpression("'Endpoint: ${properties['endpoint']}'", result); - } - - [Test] - public void Interpolate_FormattableStringOverload_WithLiteralText_ReturnsCorrectFormat() - { - // Arrange - FormattableString formattable = $"Hello, World!"; - - // Act - explicitly call the FormattableString overload - var result = BicepFunction.Interpolate(formattable); - - // Assert - TestHelpers.AssertExpression("'Hello, World!'", result); - } - - [Test] - public void Interpolate_FormattableStringOverload_WithVariable_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("testVar", typeof(string)); - FormattableString formattable = $"Variable: {variable}"; - - // Act - explicitly call the FormattableString overload - var result = BicepFunction.Interpolate(formattable); - - // Assert - TestHelpers.AssertExpression("'Variable: ${testVar}'", result); - } - - [Test] - public void Interpolate_EmptyString_ReturnsCorrectFormat() - { - // Act - cast to FormattableString to resolve ambiguity - var result = BicepFunction.Interpolate((FormattableString)$""); - - // Assert - TestHelpers.AssertExpression("''", result); - } - - [Test] - public void Interpolate_OnlyVariables_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("onlyVar", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"{variable}"); - - // Assert - TestHelpers.AssertExpression("'${onlyVar}'", result); - } - - [Test] - public void Interpolate_WithSpecialCharacters_EscapesCorrectly() - { - // Arrange - var variable = new ProvisioningVariable("path", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"Path: {variable}\\folder\\file.txt"); - - // Assert - TestHelpers.AssertExpression("'Path: ${path}\\\\folder\\\\file.txt'", result); - } - - [Test] - public void Interpolate_WithQuotes_EscapesCorrectly() - { - // Arrange - var variable = new ProvisioningVariable("message", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"Message: '{variable}' is quoted"); - - // Assert - TestHelpers.AssertExpression("'Message: \\'${message}\\' is quoted'", result); - } - - [Test] - public void Interpolate_WithCSharpExpressions_ReturnsCorrectFormat() - { - // Arrange - var variable = new ProvisioningVariable("count", typeof(int)); - int multiplier = 2; - - // Act - var result = BicepFunction.Interpolate($"Count: {variable} x {multiplier}"); - - // Assert - TestHelpers.AssertExpression("'Count: ${count} x 2'", result); - } - - [Test] - public void Interpolate_FormattableStringWithComplexArguments_ReturnsCorrectFormat() - { - // Arrange - var resourceGroup = new ProvisioningVariable("rgName", typeof(string)); - var location = new ProvisioningVariable("location", typeof(string)); - - // Create a more complex FormattableString - FormattableString formattable = $"RG: {resourceGroup} in {location}"; - - // Act - var result = BicepFunction.Interpolate(formattable); - - // Assert - TestHelpers.AssertExpression("'RG: ${rgName} in ${location}'", result); - } - - [Test] - public void Interpolate_WithBicepValueExpression_ReturnsCorrectFormat() - { - // Arrange - test BicepValue created from expression rather than literal - var bicepValue = new BicepValue(new IdentifierExpression("dynamicValue")); - - // Act - var result = BicepFunction.Interpolate($"Dynamic: {bicepValue}"); - - // Assert - TestHelpers.AssertExpression("'Dynamic: ${dynamicValue}'", result); - } - - [Test] - public void Interpolate_WithSimpleInterpolation_ReturnsCorrectFormat() - { - // Arrange - test the specific use case from the user's request - var myVariable = new ProvisioningVariable("myVariable", typeof(string)); - - // Act - use natural interpolated string syntax - var result = BicepFunction.Interpolate($"Hello {myVariable}!"); - - // Assert - TestHelpers.AssertExpression("'Hello ${myVariable}!'", result); - } - - [Test] - public void Interpolate_WithMultipleInterpolatedValues_ReturnsCorrectFormat() - { - // Arrange - var prefix = new ProvisioningVariable("prefix", typeof(string)); - var suffix = new ProvisioningVariable("suffix", typeof(string)); - - // Act - var result = BicepFunction.Interpolate($"{prefix}-{suffix}"); - - // Assert - TestHelpers.AssertExpression("'${prefix}-${suffix}'", result); - } - - [Test] - public void Interpolate_WithResourceReferences_ReturnsCorrectFormat() - { - // Arrange - var resourceName = new ProvisioningVariable("resourceName", typeof(string)); - var location = new ProvisioningVariable("location", typeof(string)); - - // Act - test a more realistic scenario - var result = BicepFunction.Interpolate($"Creating resource '{resourceName}' in location '{location}'"); - - // Assert - TestHelpers.AssertExpression("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result); - } - - [Test] - public void Interpolate_WithNumericInterpolation_ReturnsCorrectFormat() - { - // Arrange - var port = new ProvisioningVariable("port", typeof(int)); - var timeout = 30; - - // Act - var result = BicepFunction.Interpolate($"Server running on port {port} with timeout {timeout}s"); - - // Assert - TestHelpers.AssertExpression("'Server running on port ${port} with timeout 30s'", result); - } - - [Test] - public void Interpolate_WithConditionalExpression_ReturnsCorrectFormat() - { - // Arrange - var condition = new IdentifierExpression("isDevelopment"); - var prodValue = new IdentifierExpression("prodConfig"); - var devValue = new IdentifierExpression("devConfig"); - var conditionalExpr = new ConditionalExpression(condition, devValue, prodValue); - - // Act - var result = BicepFunction.Interpolate($"Config: {conditionalExpr}"); - - // Assert - TestHelpers.AssertExpression("'Config: ${isDevelopment ? devConfig : prodConfig}'", result); - } - - [Test] - public void Interpolate_WithNestedFormattableInLiteralInterpolation_ReturnsCorrectFormat() - { - // Arrange - test case 1: literal interpolated string with FormattableString as argument - var outerVar = new ProvisioningVariable("outerVar", typeof(string)); - var innerVar = new ProvisioningVariable("innerVar", typeof(string)); - var middleVar = new ProvisioningVariable("middleVar", typeof(string)); - - // Create nested FormattableString - FormattableString innerFormattable = $"inner[{innerVar}]"; - FormattableString middleFormattable = $"middle({middleVar}|{innerFormattable})"; - - // Act - Use literal interpolated string with nested FormattableString - var result = BicepFunction.Interpolate($"Outer: {outerVar} -> {middleFormattable}"); - - // Assert - TestHelpers.AssertExpression("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result); - } - - [Test] - public void Interpolate_WithFormattableStringContainingNestedFormattable_ReturnsCorrectFormat() - { - // Arrange - test case 2: FormattableString as argument with nested FormattableString - var level1Var = new ProvisioningVariable("level1", typeof(string)); - var level2Var = new ProvisioningVariable("level2", typeof(string)); - var level3Var = new ProvisioningVariable("level3", typeof(string)); - - // Create deeply nested FormattableStrings - FormattableString level3Formattable = $"L3:{level3Var}"; - FormattableString level2Formattable = $"L2:{level2Var}->{level3Formattable}"; - FormattableString level1Formattable = $"L1:{level1Var}->{level2Formattable}"; - - // Act - Pass FormattableString containing nested FormattableStrings to Interpolate - var result = BicepFunction.Interpolate(level1Formattable); - - // Assert - TestHelpers.AssertExpression("'L1:${level1}->L2:${level2}->L3:${level3}'", result); - } - } -} diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs new file mode 100644 index 000000000000..ff3e9eb72c4d --- /dev/null +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs @@ -0,0 +1,362 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using Azure.Provisioning.Expressions; +using NUnit.Framework; + +namespace Azure.Provisioning.Tests.Expressions.BicepFunctionTests; + +public class InterpolateTests +{ + [Test] + public void Interpolate_WithLiteralText_ReturnsCorrectFormat() + { + // Act - test literal interpolated string (cast to FormattableString to resolve ambiguity) + var result = BicepFunction.Interpolate($"Hello, World!"); + + // Assert + TestHelpers.AssertExpression("'Hello, World!'", result); + } + + [Test] + public void Interpolate_WithSingleVariable_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("myVar", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"Value: {variable}"); + + // Assert + TestHelpers.AssertExpression("'Value: ${myVar}'", result); + } + + [Test] + public void Interpolate_WithMultipleVariables_ReturnsCorrectFormat() + { + // Arrange + var nameVar = new ProvisioningVariable("name", typeof(string)); + var valueVar = new ProvisioningVariable("value", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"Name: {nameVar}, Value: {valueVar}"); + + // Assert + TestHelpers.AssertExpression("'Name: ${name}, Value: ${value}'", result); + } + + [Test] + public void Interpolate_WithBicepExpression_ReturnsCorrectFormat() + { + // Arrange + var expression = new IdentifierExpression("resourceGroup"); + + // Act + var result = BicepFunction.Interpolate($"Resource Group: {expression}"); + + // Assert + TestHelpers.AssertExpression("'Resource Group: ${resourceGroup}'", result); + } + + [Test] + public void Interpolate_WithBicepValue_ReturnsCorrectFormat() + { + // Arrange - BicepValue with literal strings are compiled as string literals in interpolation + var bicepValue = new BicepValue("test-value"); + + // Act + var result = BicepFunction.Interpolate($"Static: {bicepValue}"); + + // Assert - Literal BicepValues get compiled as string literals, not expressions + TestHelpers.AssertExpression("'Static: test-value'", result); + } + + [Test] + public void Interpolate_WithMixedContent_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("location", typeof(string)); + var expression = new IdentifierExpression("resourceGroup"); + var bicepValue = new BicepValue("suffix"); + + // Act + var result = BicepFunction.Interpolate($"Prefix-{variable}-{expression}-{bicepValue}"); + + // Assert - Literal BicepValues get compiled as string literals, not expressions + TestHelpers.AssertExpression("'Prefix-${location}-${resourceGroup}-suffix'", result); + } + + [Test] + public void Interpolate_WithNestedFormattableString_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("name", typeof(string)); + FormattableString nested = $"nested-{variable}"; + + // Act + var result = BicepFunction.Interpolate($"Outer: {nested}"); + + // Assert + TestHelpers.AssertExpression("'Outer: nested-${name}'", result); + } + + [Test] + public void Interpolate_WithNullValue_HandlesGracefully() + { + // Arrange + string? nullValue = null; + + // Act + var result = BicepFunction.Interpolate($"Value: {nullValue}"); + + // Assert + TestHelpers.AssertExpression("'Value: '", result); + } + + [Test] + public void Interpolate_WithComplexExpression_ReturnsCorrectFormat() + { + // Arrange + var indexExpression = new IndexExpression( + new IdentifierExpression("properties"), + new StringLiteralExpression("endpoint") + ); + + // Act + var result = BicepFunction.Interpolate($"Endpoint: {indexExpression}"); + + // Assert + TestHelpers.AssertExpression("'Endpoint: ${properties['endpoint']}'", result); + } + + [Test] + public void Interpolate_FormattableStringOverload_WithLiteralText_ReturnsCorrectFormat() + { + // Arrange + FormattableString formattable = $"Hello, World!"; + + // Act - explicitly call the FormattableString overload + var result = BicepFunction.Interpolate(formattable); + + // Assert + TestHelpers.AssertExpression("'Hello, World!'", result); + } + + [Test] + public void Interpolate_FormattableStringOverload_WithVariable_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("testVar", typeof(string)); + FormattableString formattable = $"Variable: {variable}"; + + // Act - explicitly call the FormattableString overload + var result = BicepFunction.Interpolate(formattable); + + // Assert + TestHelpers.AssertExpression("'Variable: ${testVar}'", result); + } + + [Test] + public void Interpolate_EmptyString_ReturnsCorrectFormat() + { + // Act - cast to FormattableString to resolve ambiguity + var result = BicepFunction.Interpolate((FormattableString)$""); + + // Assert + TestHelpers.AssertExpression("''", result); + } + + [Test] + public void Interpolate_OnlyVariables_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("onlyVar", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"{variable}"); + + // Assert + TestHelpers.AssertExpression("'${onlyVar}'", result); + } + + [Test] + public void Interpolate_WithSpecialCharacters_EscapesCorrectly() + { + // Arrange + var variable = new ProvisioningVariable("path", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"Path: {variable}\\folder\\file.txt"); + + // Assert + TestHelpers.AssertExpression("'Path: ${path}\\\\folder\\\\file.txt'", result); + } + + [Test] + public void Interpolate_WithQuotes_EscapesCorrectly() + { + // Arrange + var variable = new ProvisioningVariable("message", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"Message: '{variable}' is quoted"); + + // Assert + TestHelpers.AssertExpression("'Message: \\'${message}\\' is quoted'", result); + } + + [Test] + public void Interpolate_WithCSharpExpressions_ReturnsCorrectFormat() + { + // Arrange + var variable = new ProvisioningVariable("count", typeof(int)); + int multiplier = 2; + + // Act + var result = BicepFunction.Interpolate($"Count: {variable} x {multiplier}"); + + // Assert + TestHelpers.AssertExpression("'Count: ${count} x 2'", result); + } + + [Test] + public void Interpolate_FormattableStringWithComplexArguments_ReturnsCorrectFormat() + { + // Arrange + var resourceGroup = new ProvisioningVariable("rgName", typeof(string)); + var location = new ProvisioningVariable("location", typeof(string)); + + // Create a more complex FormattableString + FormattableString formattable = $"RG: {resourceGroup} in {location}"; + + // Act + var result = BicepFunction.Interpolate(formattable); + + // Assert + TestHelpers.AssertExpression("'RG: ${rgName} in ${location}'", result); + } + + [Test] + public void Interpolate_WithBicepValueExpression_ReturnsCorrectFormat() + { + // Arrange - test BicepValue created from expression rather than literal + var bicepValue = new BicepValue(new IdentifierExpression("dynamicValue")); + + // Act + var result = BicepFunction.Interpolate($"Dynamic: {bicepValue}"); + + // Assert + TestHelpers.AssertExpression("'Dynamic: ${dynamicValue}'", result); + } + + [Test] + public void Interpolate_WithSimpleInterpolation_ReturnsCorrectFormat() + { + // Arrange - test the specific use case from the user's request + var myVariable = new ProvisioningVariable("myVariable", typeof(string)); + + // Act - use natural interpolated string syntax + var result = BicepFunction.Interpolate($"Hello {myVariable}!"); + + // Assert + TestHelpers.AssertExpression("'Hello ${myVariable}!'", result); + } + + [Test] + public void Interpolate_WithMultipleInterpolatedValues_ReturnsCorrectFormat() + { + // Arrange + var prefix = new ProvisioningVariable("prefix", typeof(string)); + var suffix = new ProvisioningVariable("suffix", typeof(string)); + + // Act + var result = BicepFunction.Interpolate($"{prefix}-{suffix}"); + + // Assert + TestHelpers.AssertExpression("'${prefix}-${suffix}'", result); + } + + [Test] + public void Interpolate_WithResourceReferences_ReturnsCorrectFormat() + { + // Arrange + var resourceName = new ProvisioningVariable("resourceName", typeof(string)); + var location = new ProvisioningVariable("location", typeof(string)); + + // Act - test a more realistic scenario + var result = BicepFunction.Interpolate($"Creating resource '{resourceName}' in location '{location}'"); + + // Assert + TestHelpers.AssertExpression("'Creating resource \\'${resourceName}\\' in location \\'${location}\\''", result); + } + + [Test] + public void Interpolate_WithNumericInterpolation_ReturnsCorrectFormat() + { + // Arrange + var port = new ProvisioningVariable("port", typeof(int)); + var timeout = 30; + + // Act + var result = BicepFunction.Interpolate($"Server running on port {port} with timeout {timeout}s"); + + // Assert + TestHelpers.AssertExpression("'Server running on port ${port} with timeout 30s'", result); + } + + [Test] + public void Interpolate_WithConditionalExpression_ReturnsCorrectFormat() + { + // Arrange + var condition = new IdentifierExpression("isDevelopment"); + var prodValue = new IdentifierExpression("prodConfig"); + var devValue = new IdentifierExpression("devConfig"); + var conditionalExpr = new ConditionalExpression(condition, devValue, prodValue); + + // Act + var result = BicepFunction.Interpolate($"Config: {conditionalExpr}"); + + // Assert + TestHelpers.AssertExpression("'Config: ${isDevelopment ? devConfig : prodConfig}'", result); + } + + [Test] + public void Interpolate_WithNestedFormattableInLiteralInterpolation_ReturnsCorrectFormat() + { + // Arrange - test case 1: literal interpolated string with FormattableString as argument + var outerVar = new ProvisioningVariable("outerVar", typeof(string)); + var innerVar = new ProvisioningVariable("innerVar", typeof(string)); + var middleVar = new ProvisioningVariable("middleVar", typeof(string)); + + // Create nested FormattableString + FormattableString innerFormattable = $"inner[{innerVar}]"; + FormattableString middleFormattable = $"middle({middleVar}|{innerFormattable})"; + + // Act - Use literal interpolated string with nested FormattableString + var result = BicepFunction.Interpolate($"Outer: {outerVar} -> {middleFormattable}"); + + // Assert + TestHelpers.AssertExpression("'Outer: ${outerVar} -> middle(${middleVar}|inner[${innerVar}])'", result); + } + + [Test] + public void Interpolate_WithFormattableStringContainingNestedFormattable_ReturnsCorrectFormat() + { + // Arrange - test case 2: FormattableString as argument with nested FormattableString + var level1Var = new ProvisioningVariable("level1", typeof(string)); + var level2Var = new ProvisioningVariable("level2", typeof(string)); + var level3Var = new ProvisioningVariable("level3", typeof(string)); + + // Create deeply nested FormattableStrings + FormattableString level3Formattable = $"L3:{level3Var}"; + FormattableString level2Formattable = $"L2:{level2Var}->{level3Formattable}"; + FormattableString level1Formattable = $"L1:{level1Var}->{level2Formattable}"; + + // Act - Pass FormattableString containing nested FormattableStrings to Interpolate + var result = BicepFunction.Interpolate(level1Formattable); + + // Assert + TestHelpers.AssertExpression("'L1:${level1}->L2:${level2}->L3:${level3}'", result); + } +} From 11f624881243d15e25a48f631b56cf99bd08add9 Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Fri, 24 Oct 2025 14:06:04 +0800 Subject: [PATCH 7/8] introduce test cases --- .../ResourceIdFunctionsTests.cs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs new file mode 100644 index 000000000000..74389bfaaf16 --- /dev/null +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Storage; +using NUnit.Framework; + +namespace Azure.Provisioning.Tests.Expressions.BicepFunctionTests; + +public class ResourceIdFunctionsTests +{ + [Test] + public void TestGetResourceId() + { + var id1 = BicepFunction.GetResourceId("00000000-0000-0000-0000-000000000000", "myResourceGroup", "Microsoft.Storage/storageAccounts", "myStorageAccount"); + TestHelpers.AssertExpression("resourceId('00000000-0000-0000-0000-000000000000', 'myResourceGroup', 'Microsoft.Storage/storageAccounts', 'myStorageAccount')", id1); + + var id2 = BicepFunction.GetResourceId("Microsoft.Network/virtualNetworks/subnets", "myVnet", "mySubnet"); + TestHelpers.AssertExpression("resourceId('Microsoft.Network/virtualNetworks/subnets', 'myVnet', 'mySubnet')", id2); + } + + [Test] + public void TestGetSubscriptionResourceId() + { + var id1 = BicepFunction.GetSubscriptionResourceId("Microsoft.Storage/storageAccounts", "myStorageAccount"); + TestHelpers.AssertExpression("subscriptionResourceId('Microsoft.Storage/storageAccounts', 'myStorageAccount')", id1); + + var id2 = BicepFunction.GetSubscriptionResourceId("00000000-0000-0000-0000-000000000000", "Microsoft.Resources/resourceGroups", "myResourceGroup"); + TestHelpers.AssertExpression("subscriptionResourceId('00000000-0000-0000-0000-000000000000', 'Microsoft.Resources/resourceGroups', 'myResourceGroup')", id2); + } + + [Test] + public void TestGetExtensionResourceId() + { + var scope = BicepFunction.GetResourceId("Microsoft.Compute/virtualMachines", "myVm"); + var id1 = BicepFunction.GetExtensionResourceId(scope, "Microsoft.GuestConfigurations/configurations", "myConfiguration"); + TestHelpers.AssertExpression("extensionResourceId(resourceId('Microsoft.Compute/virtualMachines', 'myVm'), 'Microsoft.GuestConfigurations/configurations', 'myConfiguration')", id1); + + var storageAccount = new StorageAccount("account"); + var id2 = BicepFunction.GetExtensionResourceId(storageAccount.Id, "Microsoft.Authorization/policyDefinitions", "myDef"); + TestHelpers.AssertExpression("extensionResourceId(account.id, 'Microsoft.Authorization/policyDefinitions', 'myDef')", id2); + } +} From 54b69a3d59726682582ec767d71335c5f2d0828c Mon Sep 17 00:00:00 2001 From: Arcturus Zhang Date: Fri, 24 Oct 2025 14:06:39 +0800 Subject: [PATCH 8/8] rename namespaces --- .../tests/Expressions/BicepFunctionTests/InterpolateTests.cs | 2 +- .../Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs index ff3e9eb72c4d..9c044740975e 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/InterpolateTests.cs @@ -5,7 +5,7 @@ using Azure.Provisioning.Expressions; using NUnit.Framework; -namespace Azure.Provisioning.Tests.Expressions.BicepFunctionTests; +namespace Azure.Provisioning.Tests.Expressions.BicepFunctions; public class InterpolateTests { diff --git a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs index 74389bfaaf16..369eef73c52d 100644 --- a/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs +++ b/sdk/provisioning/Azure.Provisioning/tests/Expressions/BicepFunctionTests/ResourceIdFunctionsTests.cs @@ -5,7 +5,7 @@ using Azure.Provisioning.Storage; using NUnit.Framework; -namespace Azure.Provisioning.Tests.Expressions.BicepFunctionTests; +namespace Azure.Provisioning.Tests.Expressions.BicepFunctions; public class ResourceIdFunctionsTests {