Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Text.Json.Nodes;
using Azure.ResourceManager.Resources.Models;
using Bicep.Cli.Helpers.Deploy;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Newtonsoft.Json.Linq;

namespace Bicep.Cli.UnitTests.Helpers.Deploy
{
[TestClass]
public class DeploymentProcessorErrorTests
{
[TestMethod]
public void FormatError_formats_correctly_with_symbolicName()
{
var result = DeploymentProcessor.FormatError(
symbolicName: "symName",
resourceType: "Microsoft.Foo/bar",
resourceName: "myResource",
errorCode: "BadRequest",
errorMessage: "Something went wrong"
);

result.Should().Be("Resource 'symName' (Microsoft.Foo/bar 'myResource'): BadRequest: Something went wrong");
}

[TestMethod]
public void FormatError_formats_correctly_without_symbolicName()
{
var result = DeploymentProcessor.FormatError(
symbolicName: null,
resourceType: "Microsoft.Foo/bar",
resourceName: "myResource",
errorCode: "BadRequest",
errorMessage: "Something went wrong"
);

result.Should().Be("Resource Microsoft.Foo/bar 'myResource': BadRequest: Something went wrong");
}

[TestMethod]
public void FormatError_formats_correctly_with_no_resource_info()
{
var result = DeploymentProcessor.FormatError(
symbolicName: null,
resourceType: null,
resourceName: null,
errorCode: "BadRequest",
errorMessage: "Something went wrong"
);

result.Should().Be("BadRequest: Something went wrong");
}

[TestMethod]
public void GetError_from_ArmDeploymentOperation_returns_null_if_no_error()
{
var operationMock = new Mock<ArmDeploymentOperation>();
operationMock.Setup(o => o.Properties.StatusMessage).Returns((ArmDeploymentStatusMessage)null);

DeploymentProcessor.GetError(operationMock.Object).Should().BeNull();
}

[TestMethod]
public void GetError_from_ArmDeploymentOperation_returns_formatted_error()
{
var targetResource = new ArmDeploymentTargetResource
{
SymbolicName = "sym",
ResourceName = "myResource",
ResourceType = "Microsoft.Foo/bar"
};

var statusMessage = new ArmDeploymentStatusMessage
{
Error = new ArmDeploymentError
{
Code = "BadRequest",
Message = "Something went wrong"
}
};

var operationMock = new Mock<ArmDeploymentOperation>();
operationMock.Setup(o => o.Properties.TargetResource).Returns(targetResource);
operationMock.Setup(o => o.Properties.StatusMessage).Returns(statusMessage);

var error = DeploymentProcessor.GetError(operationMock.Object);
error.Should().Be("Resource 'sym' (Microsoft.Foo/bar 'myResource'): BadRequest: Something went wrong");
}

[TestMethod]
public void GetError_from_DeploymentOperationDefinition_returns_null_if_no_error()
{
var operation = new DeploymentOperationDefinition
{
Properties = new DeploymentOperationProperties
{
StatusMessage = JObject.Parse("""{}"""),
TargetResource = new DeploymentTargetResource()
}
};

DeploymentProcessor.GetError(operation).Should().BeNull();
}

[TestMethod]
public void GetError_from_DeploymentOperationDefinition_returns_formatted_error()
{
var operation = new DeploymentOperationDefinition
{
Properties = new DeploymentOperationProperties
{
StatusMessage = JObject.Parse("""
{
"error": {
"code": "BadRequest",
"message": "Something went wrong"
}
}
"""),
TargetResource = new DeploymentTargetResource
{
SymbolicName = "sym",
ResourceName = "myResource",
ResourceType = "Microsoft.Foo/bar"
}
}
};

var error = DeploymentProcessor.GetError(operation);
error.Should().Be("Resource 'sym' (Microsoft.Foo/bar 'myResource'): BadRequest: Something went wrong");
}
}
}
36 changes: 32 additions & 4 deletions src/Bicep.Cli/Helpers/Deploy/DeploymentProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -353,24 +353,52 @@ private static async Task<DeploymentView> GetDeployment(ArmClient armClient, str
return GetDeploymentView(deployment, operations);
}

private static string? GetError(ArmDeploymentOperation operation)
internal static string FormatError(string? symbolicName, string? resourceType, string? resourceName, string? errorCode, string? errorMessage)
{
var resourceContext = string.Empty;

if (resourceType is not null && resourceName is not null)
{
resourceContext = !string.IsNullOrEmpty(symbolicName)
? $"Resource '{symbolicName}' ({resourceType} '{resourceName}'): "
: $"Resource {resourceType} '{resourceName}': ";
}

return $"{resourceContext}{errorCode}: {errorMessage}";
}

internal static string? GetError(ArmDeploymentOperation operation)
{
if (operation.Properties.StatusMessage?.Error is not { } error)
{
return null;
}

return $"{error.Code}: {error.Message}";
var tr = operation.Properties.TargetResource;

return FormatError(
tr?.SymbolicName,
tr?.ResourceType,
tr?.ResourceName,
error.Code,
error.Message);
}

private static string? GetError(DeploymentOperationDefinition operation)
internal static string? GetError(DeploymentOperationDefinition operation)
{
if (operation.Properties.StatusMessage?.GetProperty("error") is not { } error)
{
return null;
}

return $"{error.GetProperty("code")}: {error.GetProperty("message")}";
var tr = operation.Properties.TargetResource;

return FormatError(
tr?.SymbolicName,
tr?.ResourceType,
tr?.ResourceName,
error.GetProperty("code").ToString(),
error.GetProperty("message").ToString());
}

private static string? GetError(ArmDeploymentData deployment)
Expand Down