mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
fix(ffi): eliminate aliasing UB + add Azure Policy JSON compilation FFI (#727)
* fix(ffi): eliminate aliasing UB via to_shared_ref migration Add to_shared_ref() helper that creates &T (shared reference) from raw pointers instead of &mut T. This eliminates undefined behavior caused by violating Rust's aliasing invariant when C# SafeHandle permits concurrent FFI calls on the same handle. With &mut T, the compiler may assume exclusive (noalias) access and reorder or elide reads/writes — a miscompilation risk when another thread holds a reference to the same object. Switching to &T removes that assumption; actual mutation is mediated by the interior RwLock inside Handle<T>, which is the sole synchronization mechanism. Migrated sites: - rvm.rs: 20 non-drop call sites - engine.rs: 30 non-drop call sites + with_unwind_guard for timer fns - compiled_policy.rs: 2 call sites - Fix null-data UB in regorus_program_deserialize_binary Drop paths retain to_ref() where exclusive access is guaranteed by the caller contract (preventing use-after-free). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ffi): add Azure Policy JSON compilation FFI and C# bindings - AliasRegistry builder pattern: RegorusAliasRegistryBuilder (mutable, single-threaded) + RegorusAliasRegistry (immutable, Arc-wrapped) - Azure Policy JSON compilation: regorus_compile_azure_policy_rule and regorus_compile_azure_policy_definition with alias registry support - regorus_rvm_set_context for host-supplied ambient data - C# AliasRegistryBuilder and AliasRegistry classes with convenience factories (FromJson, FromManifest, Empty) - C# AzurePolicyCompiler static class for policy rule/definition compilation - Compile functions take *const RegorusAliasRegistry (read-only via to_shared_ref for concurrent compilation safety) - Fix pre-existing clippy warnings across multiple crates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
5467cd9e69
commit
86b4a279fa
@@ -43,31 +43,28 @@ public class AliasRegistryTests
|
||||
[TestMethod]
|
||||
public void Create_and_dispose_succeeds()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
using var registry = AliasRegistry.Empty();
|
||||
Assert.AreEqual(0, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadJson_populates_registry()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(AliasesJson);
|
||||
Assert.AreEqual(1, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LoadManifest_populates_registry()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadManifest(ManifestJson);
|
||||
using var registry = AliasRegistry.FromManifest(ManifestJson);
|
||||
Assert.AreEqual(1, registry.Length);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NormalizeAndWrap_produces_envelope()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
@@ -93,8 +90,7 @@ public class AliasRegistryTests
|
||||
[TestMethod]
|
||||
public void NormalizeAndWrap_with_context_and_parameters()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
@@ -115,8 +111,7 @@ public class AliasRegistryTests
|
||||
[TestMethod]
|
||||
public void Denormalize_restores_properties()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(AliasesJson);
|
||||
|
||||
var normalized = @"{
|
||||
""name"": ""acct1"",
|
||||
@@ -137,8 +132,7 @@ public class AliasRegistryTests
|
||||
[TestMethod]
|
||||
public void Round_trip_normalize_then_denormalize()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(AliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(AliasesJson);
|
||||
|
||||
var resource = @"{
|
||||
""name"": ""acct1"",
|
||||
@@ -166,8 +160,7 @@ public class AliasRegistryTests
|
||||
[TestMethod]
|
||||
public void DataPlane_manifest_normalize()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadManifest(ManifestJson);
|
||||
using var registry = AliasRegistry.FromManifest(ManifestJson);
|
||||
|
||||
var resource = @"{
|
||||
""type"": ""Microsoft.KeyVault.Data/vaults/certificates"",
|
||||
@@ -185,7 +178,7 @@ public class AliasRegistryTests
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void LoadJson_invalid_throws()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson("not valid json");
|
||||
using var builder = new AliasRegistryBuilder();
|
||||
builder.LoadJson("not valid json");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Text.Json.Nodes;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Regorus;
|
||||
|
||||
namespace Regorus.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="AzurePolicyCompiler"/> — compiling Azure Policy JSON
|
||||
/// policyRule and policyDefinition into RVM programs and evaluating them.
|
||||
/// </summary>
|
||||
[TestClass]
|
||||
public class AzurePolicyCompilerTests
|
||||
{
|
||||
// -----------------------------------------------------------------------
|
||||
// Test data
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
private const string StorageAliasesJson = @"[{
|
||||
""namespace"": ""Microsoft.Storage"",
|
||||
""resourceTypes"": [{
|
||||
""resourceType"": ""storageAccounts"",
|
||||
""capabilities"": ""SupportsTags, SupportsLocation"",
|
||||
""aliases"": [
|
||||
{
|
||||
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
|
||||
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
|
||||
""paths"": []
|
||||
},
|
||||
{
|
||||
""name"": ""Microsoft.Storage/storageAccounts/minimumTlsVersion"",
|
||||
""defaultPath"": ""properties.minimumTlsVersion"",
|
||||
""paths"": []
|
||||
}
|
||||
]
|
||||
}]
|
||||
}]";
|
||||
|
||||
/// <summary>Simple policy rule that checks the resource type.</summary>
|
||||
private const string SimpleAuditRule = @"{
|
||||
""if"": {
|
||||
""field"": ""type"",
|
||||
""equals"": ""Microsoft.Storage/storageAccounts""
|
||||
},
|
||||
""then"": { ""effect"": ""audit"" }
|
||||
}";
|
||||
|
||||
/// <summary>Policy rule that uses an alias to check HTTPS-only.</summary>
|
||||
private const string HttpsDenyRule = @"{
|
||||
""if"": {
|
||||
""allOf"": [
|
||||
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
|
||||
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
|
||||
]
|
||||
},
|
||||
""then"": { ""effect"": ""deny"" }
|
||||
}";
|
||||
|
||||
/// <summary>Full policy definition with parameters.</summary>
|
||||
private const string PolicyDefinitionWithParams = @"{
|
||||
""displayName"": ""Require HTTPS for storage accounts"",
|
||||
""policyType"": ""Custom"",
|
||||
""mode"": ""Indexed"",
|
||||
""parameters"": {
|
||||
""effect"": {
|
||||
""type"": ""String"",
|
||||
""defaultValue"": ""deny""
|
||||
}
|
||||
},
|
||||
""policyRule"": {
|
||||
""if"": {
|
||||
""allOf"": [
|
||||
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
|
||||
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
|
||||
]
|
||||
},
|
||||
""then"": { ""effect"": ""[parameters('effect')]"" }
|
||||
}
|
||||
}";
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Helper
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Wrap a normalized resource JSON and parameters into the input envelope
|
||||
/// expected by compiled Azure Policy RVM programs.
|
||||
/// </summary>
|
||||
private static string WrapInput(string resourceJson, string parametersJson = "{}")
|
||||
{
|
||||
return $@"{{""resource"": {resourceJson}, ""parameters"": {parametersJson}}}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compile a policy rule, load it into an RVM, set input, and execute.
|
||||
/// Returns the result string from <c>ExecuteEntryPoint("main")</c>.
|
||||
/// </summary>
|
||||
private static string? CompileAndEval(
|
||||
AliasRegistry? registry,
|
||||
string policyRuleJson,
|
||||
string inputJson)
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, policyRuleJson);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
vm.SetInputJson(inputJson);
|
||||
return vm.ExecuteEntryPoint("main");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CompilePolicyRule tests
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[TestMethod]
|
||||
public void CompilePolicyRule_no_aliases_succeeds()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
|
||||
Assert.IsNotNull(program);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompilePolicyRule_with_aliases_succeeds()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
|
||||
Assert.IsNotNull(program);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void CompilePolicyRule_null_json_throws()
|
||||
{
|
||||
AzurePolicyCompiler.CompilePolicyRule(null, null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void CompilePolicyRule_invalid_json_throws()
|
||||
{
|
||||
AzurePolicyCompiler.CompilePolicyRule(null, "not valid json");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// CompilePolicyDefinition tests
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[TestMethod]
|
||||
public void CompilePolicyDefinition_no_aliases_succeeds()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyDefinition(null, PolicyDefinitionWithParams);
|
||||
Assert.IsNotNull(program);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CompilePolicyDefinition_with_aliases_succeeds()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyDefinition(registry, PolicyDefinitionWithParams);
|
||||
Assert.IsNotNull(program);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void CompilePolicyDefinition_null_json_throws()
|
||||
{
|
||||
AzurePolicyCompiler.CompilePolicyDefinition(null, null!);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void CompilePolicyDefinition_invalid_json_throws()
|
||||
{
|
||||
AzurePolicyCompiler.CompilePolicyDefinition(null, @"{""not"": ""a definition""}");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// End-to-end evaluation tests
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_simple_rule_matching_resource_returns_effect()
|
||||
{
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts""}");
|
||||
|
||||
var result = CompileAndEval(null, SimpleAuditRule, input);
|
||||
Assert.IsNotNull(result, "expected a result for matching resource");
|
||||
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("audit", doc["effect"]?.GetValue<string>(),
|
||||
$"expected 'audit' effect, got: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_simple_rule_non_matching_resource_returns_undefined()
|
||||
{
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.compute/virtualmachines""}");
|
||||
|
||||
var result = CompileAndEval(null, SimpleAuditRule, input);
|
||||
Assert.IsNotNull(result);
|
||||
StringAssert.Contains(result!, "undefined",
|
||||
"expected undefined for non-matching resource type");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_alias_rule_non_compliant_returns_deny()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
// Non-compliant: HTTPS not enabled (normalized/lowercased form)
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": false}");
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
|
||||
$"expected 'deny' for non-compliant resource, got: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_alias_rule_compliant_returns_undefined()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
// Compliant: HTTPS enabled
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": true}");
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
StringAssert.Contains(result!, "undefined",
|
||||
"expected undefined for compliant resource");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_definition_with_default_parameters()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyDefinition(
|
||||
registry, PolicyDefinitionWithParams);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
|
||||
// Non-compliant resource
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts"", ""supportshttpstrafficonly"": false}");
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
// Default parameter value is "deny"
|
||||
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
|
||||
$"expected default 'deny' effect, got: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_with_normalized_arm_resource_end_to_end()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
// Simulate the full production flow:
|
||||
// 1. Start with an ARM resource
|
||||
var armResource = @"{
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""mystorage"",
|
||||
""location"": ""eastus"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": false,
|
||||
""minimumTlsVersion"": ""TLS1_0""
|
||||
}
|
||||
}";
|
||||
|
||||
// 2. Normalize via AliasRegistry
|
||||
var normalizedEnvelope = registry.NormalizeAndWrap(
|
||||
armResource,
|
||||
apiVersion: null,
|
||||
contextJson: "{}",
|
||||
parametersJson: "{}");
|
||||
Assert.IsNotNull(normalizedEnvelope);
|
||||
|
||||
// 3. Compile the policy rule
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
|
||||
|
||||
// 4. Execute
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
vm.SetInputJson(normalizedEnvelope!);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
|
||||
$"expected 'deny' for non-HTTPS storage account, got: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_normalized_compliant_resource_end_to_end()
|
||||
{
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
var armResource = @"{
|
||||
""type"": ""Microsoft.Storage/storageAccounts"",
|
||||
""name"": ""secureastorage"",
|
||||
""location"": ""westus"",
|
||||
""properties"": {
|
||||
""supportsHttpsTrafficOnly"": true,
|
||||
""minimumTlsVersion"": ""TLS1_2""
|
||||
}
|
||||
}";
|
||||
|
||||
var normalizedEnvelope = registry.NormalizeAndWrap(
|
||||
armResource,
|
||||
apiVersion: null,
|
||||
contextJson: "{}",
|
||||
parametersJson: "{}");
|
||||
Assert.IsNotNull(normalizedEnvelope);
|
||||
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(registry, HttpsDenyRule);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
vm.SetInputJson(normalizedEnvelope!);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
StringAssert.Contains(result!, "undefined",
|
||||
"expected undefined for compliant HTTPS storage account");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Program_can_be_serialized_and_reloaded()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
|
||||
|
||||
// Serialize to binary
|
||||
var binary = program.SerializeBinary();
|
||||
Assert.IsTrue(binary.Length > 0, "serialized program should not be empty");
|
||||
|
||||
// Deserialize and run
|
||||
using var restored = Program.DeserializeBinary(binary, out var isPartial);
|
||||
Assert.IsFalse(isPartial, "program should not be partial");
|
||||
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(restored);
|
||||
var input = WrapInput(@"{""type"": ""microsoft.storage/storageaccounts""}");
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("audit", doc["effect"]?.GetValue<string>());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Program_generates_listing()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(null, SimpleAuditRule);
|
||||
var listing = program.GenerateListing();
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(listing),
|
||||
"generated listing should not be empty");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Context-dependent policy tests
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
/// Policy rule that uses subscription() context function.
|
||||
private const string ContextPolicyRule = @"{
|
||||
""if"": {
|
||||
""allOf"": [
|
||||
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
|
||||
{ ""value"": ""[subscription().subscriptionId]"", ""equals"": ""sub-123"" }
|
||||
]
|
||||
},
|
||||
""then"": { ""effect"": ""deny"" }
|
||||
}";
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_context_policy_with_set_context_returns_effect()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(null, ContextPolicyRule);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
|
||||
vm.SetContextJson(@"{""subscription"": {""subscriptionId"": ""sub-123""}}");
|
||||
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts""}");
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
var doc = JsonNode.Parse(result!)!;
|
||||
Assert.AreEqual("deny", doc["effect"]?.GetValue<string>(),
|
||||
$"expected 'deny' with matching context, got: {result}");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Eval_context_policy_without_context_returns_undefined()
|
||||
{
|
||||
using var program = AzurePolicyCompiler.CompilePolicyRule(null, ContextPolicyRule);
|
||||
using var vm = new Rvm();
|
||||
vm.LoadProgram(program);
|
||||
|
||||
// No context set — subscription() will be undefined
|
||||
var input = WrapInput(
|
||||
@"{""type"": ""microsoft.storage/storageaccounts""}");
|
||||
vm.SetInputJson(input);
|
||||
|
||||
var result = vm.ExecuteEntryPoint("main");
|
||||
Assert.IsNotNull(result);
|
||||
StringAssert.Contains(result!, "undefined",
|
||||
"expected undefined without context set");
|
||||
}
|
||||
}
|
||||
@@ -62,8 +62,7 @@ public class AzurePolicyTests
|
||||
[TestMethod]
|
||||
public void AliasRegistry_NormalizeAndWrap_produces_input_envelope()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(StorageAliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
var result = registry.NormalizeAndWrap(
|
||||
StorageResourceJson,
|
||||
@@ -84,8 +83,7 @@ public class AzurePolicyTests
|
||||
[TestMethod]
|
||||
public void AliasRegistry_NormalizeAndWrap_flattens_properties()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(StorageAliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
var result = registry.NormalizeAndWrap(StorageResourceJson);
|
||||
Assert.IsNotNull(result);
|
||||
@@ -107,8 +105,7 @@ public class AzurePolicyTests
|
||||
[TestMethod]
|
||||
public void AliasRegistry_NormalizeAndWrap_preserves_type_field()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(StorageAliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
var result = registry.NormalizeAndWrap(StorageResourceJson);
|
||||
var doc = JsonNode.Parse(result!);
|
||||
@@ -125,8 +122,7 @@ public class AzurePolicyTests
|
||||
[TestMethod]
|
||||
public void AliasRegistry_NormalizeAndWrap_includes_parameters()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(StorageAliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
var parametersJson = @"{ ""effect"": ""Deny"" }";
|
||||
var result = registry.NormalizeAndWrap(
|
||||
@@ -143,8 +139,7 @@ public class AzurePolicyTests
|
||||
[TestMethod]
|
||||
public void AliasRegistry_Denormalize_roundtrips_correctly()
|
||||
{
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(StorageAliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(StorageAliasesJson);
|
||||
|
||||
// Normalize the ARM resource.
|
||||
var envelope = registry.NormalizeAndWrap(StorageResourceJson);
|
||||
@@ -177,8 +172,7 @@ public class AzurePolicyTests
|
||||
}
|
||||
|
||||
var aliasesJson = File.ReadAllText(aliasesPath);
|
||||
using var registry = new AliasRegistry();
|
||||
registry.LoadJson(aliasesJson);
|
||||
using var registry = AliasRegistry.FromJson(aliasesJson);
|
||||
|
||||
// The test_aliases.json file contains multiple providers.
|
||||
Assert.IsTrue(registry.Length > 0,
|
||||
|
||||
Reference in New Issue
Block a user