Skip to content

Commit 2212cd1

Browse files
authored
Add resourceInput integration tests and unify param diagnostics (#18376)
## Description - Refactor SemanticModel diagnostics collection for parameter assignments: - Create the diagnostics writer once and use TypeValidator.NarrowTypeAndCollectDiagnostics for both same-file and cross-file assignments. - Use assignmentSymbol.Context to access TypeManager, Binder, and ParsingErrorLookup for the cross-file path. - Yield collected diagnostics after the validation call to keep behavior consistent and consolidate logic. - Add integration tests for build-params handling of resourceInput parameter types Fixes #17900 Fixes #17947 ## Checklist - [x] I have read and adhere to the [contribution guide](https://github.com/Azure/bicep/blob/main/CONTRIBUTING.md). ###### Microsoft Reviewers: [Open in CodeFlow](https://microsoft.github.io/open-pr/?codeflow=https://github.com/Azure/bicep/pull/18376)
1 parent b8b1d6c commit 2212cd1

File tree

2 files changed

+178
-10
lines changed

2 files changed

+178
-10
lines changed

src/Bicep.Cli.IntegrationTests/BuildParamsCommandTests.cs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,5 +1119,169 @@ param myBool bool
11191119
error.Should().Contain("Error BCP033: Expected a value of type \"bool\" but the provided value is of type \"<empty array>\".");
11201120
result.Should().Be(1);
11211121
}
1122+
1123+
[TestMethod]
1124+
public async Task BuildParams_ResourceInputType_WithValidObject_Succeeds()
1125+
{
1126+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
1127+
FileHelper.SaveResultFile(TestContext, "main.bicep", """
1128+
@description('Parameter with resourceInput type')
1129+
param storageConfig resourceInput<'Microsoft.Storage/storageAccounts@2022-09-01'>.properties.encryption
1130+
1131+
output test string = 'success'
1132+
""", outputPath);
1133+
1134+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", """
1135+
using './main.bicep'
1136+
1137+
param storageConfig = {
1138+
services: {
1139+
blob: {
1140+
enabled: true
1141+
}
1142+
file: {
1143+
enabled: true
1144+
}
1145+
}
1146+
keySource: 'Microsoft.Storage'
1147+
}
1148+
""", outputPath);
1149+
1150+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
1151+
File.Exists(expectedOutputFile).Should().BeFalse();
1152+
1153+
var (output, error, result) = await Bicep(["build-params", inputFile]);
1154+
1155+
result.Should().Be(0);
1156+
error.Should().NotContain("Error");
1157+
File.Exists(expectedOutputFile).Should().BeTrue();
1158+
1159+
var parametersFile = File.ReadAllText(expectedOutputFile);
1160+
var parametersObject = JObject.Parse(parametersFile);
1161+
((JToken)parametersObject).Should().NotBeNull();
1162+
var storageConfigValue = parametersObject["parameters"]?["storageConfig"]?["value"];
1163+
storageConfigValue.Should().NotBeNull();
1164+
}
1165+
1166+
[TestMethod]
1167+
public async Task BuildParams_ResourceInputType_NestedProperty_Succeeds()
1168+
{
1169+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
1170+
FileHelper.SaveResultFile(TestContext, "main.bicep", """
1171+
@description('Parameter with nested resourceInput type')
1172+
param encryptionServices resourceInput<'Microsoft.Storage/storageAccounts@2022-09-01'>.properties.encryption.services
1173+
1174+
output test string = 'success'
1175+
""", outputPath);
1176+
1177+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", """
1178+
using './main.bicep'
1179+
1180+
param encryptionServices = {
1181+
blob: {
1182+
enabled: true
1183+
keyType: 'Account'
1184+
}
1185+
file: {
1186+
enabled: false
1187+
}
1188+
}
1189+
""", outputPath);
1190+
1191+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
1192+
File.Exists(expectedOutputFile).Should().BeFalse();
1193+
1194+
var (output, error, result) = await Bicep(["build-params", inputFile]);
1195+
1196+
result.Should().Be(0);
1197+
error.Should().NotContain("Error");
1198+
File.Exists(expectedOutputFile).Should().BeTrue();
1199+
}
1200+
1201+
[TestMethod]
1202+
public async Task BuildParams_ResourceInputType_ArrayOfResources_Succeeds()
1203+
{
1204+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
1205+
FileHelper.SaveResultFile(TestContext, "main.bicep", """
1206+
@description('Parameter with array of resourceInput type')
1207+
param subnets resourceInput<'Microsoft.Network/virtualNetworks/subnets@2023-09-01'>.properties[]
1208+
1209+
output test string = 'success'
1210+
""", outputPath);
1211+
1212+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", """
1213+
using './main.bicep'
1214+
1215+
param subnets = [
1216+
{
1217+
addressPrefix: '10.0.1.0/24'
1218+
privateEndpointNetworkPolicies: 'Disabled'
1219+
}
1220+
{
1221+
addressPrefix: '10.0.2.0/24'
1222+
delegations: []
1223+
}
1224+
]
1225+
""", outputPath);
1226+
1227+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
1228+
File.Exists(expectedOutputFile).Should().BeFalse();
1229+
1230+
var (output, error, result) = await Bicep(["build-params", inputFile]);
1231+
1232+
result.Should().Be(0);
1233+
error.Should().NotContain("Error");
1234+
File.Exists(expectedOutputFile).Should().BeTrue();
1235+
1236+
var parametersFile = File.ReadAllText(expectedOutputFile);
1237+
var parametersObject = JObject.Parse(parametersFile);
1238+
var subnetsArray = parametersObject["parameters"]?["subnets"]?["value"] as JArray;
1239+
subnetsArray.Should().NotBeNull();
1240+
subnetsArray!.Count.Should().Be(2);
1241+
}
1242+
1243+
[TestMethod]
1244+
public async Task BuildParams_ResourceInputType_ComplexNestedObject_Succeeds()
1245+
{
1246+
var outputPath = FileHelper.GetUniqueTestOutputPath(TestContext);
1247+
FileHelper.SaveResultFile(TestContext, "main.bicep", """
1248+
@description('Parameter with complex resourceInput type')
1249+
param organizationProfile resourceInput<'Microsoft.DevOpsInfrastructure/pools@2024-10-19'>.properties.organizationProfile
1250+
1251+
output test string = 'success'
1252+
""", outputPath);
1253+
1254+
var inputFile = FileHelper.SaveResultFile(TestContext, "main.bicepparam", """
1255+
using './main.bicep'
1256+
1257+
param organizationProfile = {
1258+
kind: 'AzureDevOps'
1259+
organizations: [
1260+
{
1261+
url: 'https://dev.azure.com/my-org'
1262+
projects: []
1263+
parallelism: 1
1264+
}
1265+
]
1266+
permissionProfile: {
1267+
kind: 'CreatorOnly'
1268+
}
1269+
}
1270+
""", outputPath);
1271+
1272+
var expectedOutputFile = FileHelper.GetResultFilePath(TestContext, "main.json", outputPath);
1273+
File.Exists(expectedOutputFile).Should().BeFalse();
1274+
1275+
var (output, error, result) = await Bicep(["build-params", inputFile]);
1276+
1277+
result.Should().Be(0);
1278+
error.Should().NotContain("Error");
1279+
File.Exists(expectedOutputFile).Should().BeTrue();
1280+
1281+
var parametersFile = File.ReadAllText(expectedOutputFile);
1282+
var parametersObject = JObject.Parse(parametersFile);
1283+
var kindValue = parametersObject["parameters"]?["organizationProfile"]?["value"]?["kind"]?.ToString();
1284+
kindValue.Should().Be("AzureDevOps");
1285+
}
11221286
}
11231287
}

src/Bicep.Core/Semantics/SemanticModel.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -708,22 +708,26 @@ private IEnumerable<IDiagnostic> GatherTypeMismatchDiagnostics()
708708
assignmentSymbol.Type is not NullType && // `param x = null` is equivalent to skipping the assignment altogether
709709
TypeManager.GetDeclaredType(assignmentSymbol.DeclaringSyntax) is { } declaredType)
710710
{
711+
var diagnostics = ToListDiagnosticWriter.Create();
712+
711713
if (isFromSameFile)
712714
{
713-
var diagnostics = ToListDiagnosticWriter.Create();
714715
TypeValidator.NarrowTypeAndCollectDiagnostics(TypeManager, Binder, ParsingErrorLookup, diagnostics, assignmentSymbol.DeclaringParameterAssignment.Value, declaredType);
715-
foreach (var diagnostic in diagnostics.GetDiagnostics())
716-
{
717-
yield return diagnostic;
718-
}
719716
}
720717
else
721718
{
722-
var areTypesAssignable = TypeValidator.AreTypesAssignable(assignmentSymbol.Type, declaredType);
723-
if (!areTypesAssignable)
724-
{
725-
yield return DiagnosticBuilder.ForPosition(assignmentSymbol.DeclaringSyntax).ExpectedValueTypeMismatch(false, declaredType, assignmentSymbol.Type);
726-
}
719+
TypeValidator.NarrowTypeAndCollectDiagnostics(
720+
assignmentSymbol.Context.TypeManager,
721+
assignmentSymbol.Context.Binder,
722+
assignmentSymbol.Context.SourceFile.ParsingErrorLookup,
723+
diagnostics,
724+
assignmentSymbol.DeclaringParameterAssignment.Value,
725+
declaredType);
726+
}
727+
728+
foreach (var diagnostic in diagnostics.GetDiagnostics())
729+
{
730+
yield return diagnostic;
727731
}
728732
}
729733
}

0 commit comments

Comments
 (0)