in src/Bicep.Core.IntegrationTests/TypeSystem/TypeValidationTests.cs [180:359]
public void Type_validation_narrowing_on_discriminated_object_types(TypeSymbolValidationFlags validationFlags, DiagnosticLevel expectedDiagnosticLevel)
{
var customTypes = new[] {
TestTypeHelper.CreateCustomResourceType("My.Rp/myType", "2020-01-01", validationFlags,
new NamedTypeProperty("myDisc1", new DiscriminatedObjectType("myDisc1", validationFlags, "discKey", new [] {
new ObjectType("choiceA", validationFlags, new [] {
new NamedTypeProperty("discKey", TypeFactory.CreateStringLiteralType("choiceA"), TypePropertyFlags.Required),
new NamedTypeProperty("valueA", LanguageConstants.String, TypePropertyFlags.Required),
}, null),
new ObjectType("choiceB", validationFlags, new [] {
new NamedTypeProperty("discKey", TypeFactory.CreateStringLiteralType("choiceB"), TypePropertyFlags.Required),
new NamedTypeProperty("valueB", LanguageConstants.String, TypePropertyFlags.Required),
}, null),
}
))),
};
{
// missing discriminator key
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
valueA: 'abc'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP078", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" requires a value of type \"'choiceA' | 'choiceB'\", but none was supplied.")
);
}
{
// incorrect discriminator key case
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
DiscKey: 'choiceA'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP078", expectedDiagnosticLevel).And.HaveMessage("The property \"discKey\" requires a value of type \"'choiceA' | 'choiceB'\", but none was supplied."),
x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"DiscKey\" is not allowed on objects of type \"'choiceA' | 'choiceB'\". Did you mean \"discKey\"?")
);
}
{
// incorrect discriminator key
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'foo'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP036", DiagnosticLevel.Warning).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'foo'\". If this is a resource type definition inaccuracy, report it using https://aka.ms/bicep-type-issues.")
);
}
{
// incorrect discriminator key with suggestion
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceC'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP088", DiagnosticLevel.Warning).And.HaveMessage("The property \"discKey\" expected a value of type \"'choiceA' | 'choiceB'\" but the provided value is of type \"'choiceC'\". Did you mean \"'choiceA'\"?")
);
}
{
// discriminator key supplied, required value omitted
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceA'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"valueA\". If this is a resource type definition inaccuracy, report it using https://aka.ms/bicep-type-issues.")
);
}
{
// discriminator key supplied, case of required property is incorrect
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceA'
ValueA: 'hello'
}
}
}
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP035", DiagnosticLevel.Warning).And.HaveMessage("The specified \"object\" declaration is missing the following required properties: \"valueA\". If this is a resource type definition inaccuracy, report it using https://aka.ms/bicep-type-issues."),
x => x.Should().HaveCodeAndSeverity("BCP089", expectedDiagnosticLevel).And.HaveMessage("The property \"ValueA\" is not allowed on objects of type \"choiceA\". Did you mean \"valueA\"?")
);
}
{
// all good, incorrect property access
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceA'
valueA: 'hello'
}
}
}
output valueA string = myRes.properties.myDisc1.valueA
output valueB string = myRes.properties.myDisc1.valuuuueB
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP053", expectedDiagnosticLevel).And.HaveMessage("The type \"choiceA\" does not contain property \"valuuuueB\". Available properties include \"discKey\", \"valueA\".")
);
}
{
// all good, incorrect property access with suggestion
var program = @"
resource myRes 'My.Rp/myType@2020-01-01' = {
name: 'steve'
properties: {
myDisc1: {
discKey: 'choiceA'
valueA: 'hello'
}
}
}
output valueA string = myRes.properties.myDisc1.valueA
output valueB string = myRes.properties.myDisc1.valueB
";
var model = GetSemanticModelForTest(program, customTypes);
model.GetAllDiagnostics().Should().SatisfyRespectively(
x => x.Should().HaveCodeAndSeverity("BCP083", expectedDiagnosticLevel).And.HaveMessage("The type \"choiceA\" does not contain property \"valueB\". Did you mean \"valueA\"?")
);
}
}