Skip to content
Merged
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
113 changes: 112 additions & 1 deletion src/UnitTests/TaskTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using AggregateConfigBuildTask.Contracts;
using AggregateConfigBuildTask.Contracts;
using Microsoft.Build.Framework;
using Moq;
using Newtonsoft.Json;
Expand Down Expand Up @@ -477,6 +477,117 @@ public void StressTest_ShouldAddSourcePropertyManyFiles()
}
}

[TestMethod]
[DataRow("arm", new[] { "json", "yml", "arm" }, DisplayName = "ARM -> JSON -> YAML -> ARM")]
[DataRow("arm", new[] { "yml", "json", "arm" }, DisplayName = "ARM -> YAML -> JSON -> ARM")]
[DataRow("json", new[] { "arm", "yml", "json" }, DisplayName = "JSON -> ARM -> YAML -> JSON")]
[DataRow("json", new[] { "yml", "arm", "json" }, DisplayName = "JSON -> YAML -> ARM -> JSON")]
[DataRow("yml", new[] { "arm", "json", "yml" }, DisplayName = "YAML -> ARM -> JSON -> YAML")]
[DataRow("yml", new[] { "json", "arm", "yml" }, DisplayName = "YAML -> JSON -> ARM -> YAML")]
[Description("Test that files are correctly translated between ARM, JSON, and YAML.")]
public void ShouldTranslateBetweenFormatsAndValidateNoDataLoss(string inputType, string[] steps)
{
Assert.IsTrue(steps?.Length > 0);

// Arrange: Prepare paths and sample data based on the input type.
var inputDir = $"{testPath}\\input";
virtualFileSystem.CreateDirectory(inputDir);

// Write the initial input file
var inputFilePath = $"{inputDir}\\input.{(inputType == "arm" ? "json" : inputType)}";
virtualFileSystem.WriteAllText(inputFilePath, GetSampleDataForType(inputType));

string previousInputPath = inputFilePath;
string previousOutputType = inputType;

// Execute the translation steps dynamically
for (int i = 0; i < steps.Length; i++)
{
var outputType = steps[i];
var stepDir = $"{testPath}\\step{i + 1}";
var stepOutputPath = $"{stepDir}\\output.{(outputType == "arm" ? "json" : outputType)}";

virtualFileSystem.CreateDirectory(stepDir);

// Execute translation for this step
ExecuteTranslationTask(previousOutputType, outputType, previousInputPath, stepOutputPath);

// Update paths for the next iteration
previousInputPath = stepOutputPath;
previousOutputType = outputType;
}

// Final step: Convert the final output back to the original input type
var finalDir = $"{testPath}\\final";
var finalOutputPath = $"{finalDir}\\final_output.{(inputType == "arm" ? "json" : inputType)}";
virtualFileSystem.CreateDirectory(finalDir);

ExecuteTranslationTask(previousOutputType, inputType, previousInputPath, finalOutputPath);

// Assert: Compare final output with original input to check no data loss
AssertNoDataLoss(inputFilePath, finalOutputPath, inputType);
}

private void ExecuteTranslationTask(string inputType, string outputType, string inputFilePath, string outputFilePath)
{
var task = new AggregateConfig(virtualFileSystem, mockLogger.Object)
{
InputDirectory = inputFilePath,
InputType = inputType,
OutputFile = outputFilePath,
OutputType = outputType,
BuildEngine = Mock.Of<IBuildEngine>()
};
bool result = task.Execute();
Assert.IsTrue(result, $"Failed translation: {inputType} -> {outputType}");
}

private void AssertNoDataLoss(string originalFilePath, string finalFilePath, string inputType)
{
string originalInput = virtualFileSystem.ReadAllText(originalFilePath);
string finalOutput = virtualFileSystem.ReadAllText(finalFilePath);
Assert.AreEqual(originalInput, finalOutput, $"Data mismatch after full conversion cycle for {inputType}");
}

private static string GetSampleDataForType(string type)
{
return type switch
{
"json" => @"{
""options"": [
{
""name"": ""Option 1"",
""description"": ""First option"",
""isTrue"": true,
""number"": 100
}
]
}",
"arm" => @"{
""$schema"": ""https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#"",
""contentVersion"": ""1.0.0.0"",
""parameters"": {
""options"": {
""type"": ""object"",
""value"": {
""name"": ""Option 1"",
""description"": ""First option"",
""isTrue"": true,
""number"": 100
}
}
}
}",
"yml" => @"options:
- name: Option 1
description: First option
isTrue: true
number: 100
",
_ => throw new InvalidOperationException("Unknown type")
};
}

/// <summary>
/// Check if an option exists with a given name and source
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/UnitTests/UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>disable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<NoWarn>CS1591,CA1707,CA5394,CA1305</NoWarn>
<NoWarn>CS1591,CA1707,CA5394,CA1305,CA1861</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
10 changes: 1 addition & 9 deletions src/UnitTests/VirtualFileSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace AggregateConfigBuildTask.Tests.Unit
internal sealed class VirtualFileSystem(bool isWindowsMode = true) : IFileSystem
{
private readonly bool isWindowsMode = isWindowsMode;
private ConcurrentDictionary<string, string> fileSystem = new(
private readonly ConcurrentDictionary<string, string> fileSystem = new(
isWindowsMode ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal);

private RegexOptions RegexOptions => isWindowsMode ? RegexOptions.IgnoreCase : RegexOptions.None;
Expand Down Expand Up @@ -132,14 +132,6 @@ public Stream OpenRead(string inputPath)
return new MemoryStream(byteArray);
}

/// <summary>
/// Delete all files on the virtual file system.
/// </summary>
public void FormatSystem()
{
fileSystem = new ConcurrentDictionary<string, string>();
}

/// <summary>
/// Ensures that the provided directory path ends with a directory separator character.
/// </summary>
Expand Down