up
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Some checks failed
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
using StellaOps.Policy.Engine.Materialization;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Policy.Engine.Tests.Materialization;
|
||||
|
||||
public sealed class MaterializationTests
|
||||
{
|
||||
#region EffectiveFinding.CreateId Tests
|
||||
|
||||
[Fact]
|
||||
public void CreateId_IsDeterministic()
|
||||
{
|
||||
var id1 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash@4.17.21", "CVE-2021-12345");
|
||||
var id2 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash@4.17.21", "CVE-2021-12345");
|
||||
|
||||
id1.Should().Be(id2);
|
||||
id1.Should().StartWith("sha256:");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateId_NormalizesTenant()
|
||||
{
|
||||
var id1 = EffectiveFinding.CreateId("TENANT1", "policy-1", "pkg:npm/lodash", "CVE-2021-12345");
|
||||
var id2 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash", "CVE-2021-12345");
|
||||
|
||||
id1.Should().Be(id2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateId_NormalizesPurl()
|
||||
{
|
||||
var id1 = EffectiveFinding.CreateId("tenant1", "policy-1", "PKG:NPM/LODASH", "CVE-2021-12345");
|
||||
var id2 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash", "CVE-2021-12345");
|
||||
|
||||
id1.Should().Be(id2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateId_DiffersForDifferentInput()
|
||||
{
|
||||
var id1 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash", "CVE-2021-12345");
|
||||
var id2 = EffectiveFinding.CreateId("tenant1", "policy-1", "pkg:npm/lodash", "CVE-2021-99999");
|
||||
|
||||
id1.Should().NotBe(id2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateId_HandlesNullValues()
|
||||
{
|
||||
var id = EffectiveFinding.CreateId(null!, "policy", "purl", "advisory");
|
||||
|
||||
id.Should().StartWith("sha256:");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EffectiveFinding.ComputeContentHash Tests
|
||||
|
||||
[Fact]
|
||||
public void ComputeContentHash_IsDeterministic()
|
||||
{
|
||||
var hash1 = EffectiveFinding.ComputeContentHash("affected", "High", "severity-rule", "not_affected", null);
|
||||
var hash2 = EffectiveFinding.ComputeContentHash("affected", "High", "severity-rule", "not_affected", null);
|
||||
|
||||
hash1.Should().Be(hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeContentHash_DiffersForDifferentStatus()
|
||||
{
|
||||
var hash1 = EffectiveFinding.ComputeContentHash("affected", "High", null, null, null);
|
||||
var hash2 = EffectiveFinding.ComputeContentHash("suppressed", "High", null, null, null);
|
||||
|
||||
hash1.Should().NotBe(hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeContentHash_DiffersForDifferentSeverity()
|
||||
{
|
||||
var hash1 = EffectiveFinding.ComputeContentHash("affected", "High", null, null, null);
|
||||
var hash2 = EffectiveFinding.ComputeContentHash("affected", "Critical", null, null, null);
|
||||
|
||||
hash1.Should().NotBe(hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeContentHash_IncludesAnnotations()
|
||||
{
|
||||
var annotations = new Dictionary<string, string> { ["key"] = "value" };
|
||||
var hash1 = EffectiveFinding.ComputeContentHash("affected", "High", null, null, annotations);
|
||||
var hash2 = EffectiveFinding.ComputeContentHash("affected", "High", null, null, null);
|
||||
|
||||
hash1.Should().NotBe(hash2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ComputeContentHash_SortsAnnotationsDeterministically()
|
||||
{
|
||||
var annotations1 = new Dictionary<string, string> { ["a"] = "1", ["b"] = "2" };
|
||||
var annotations2 = new Dictionary<string, string> { ["b"] = "2", ["a"] = "1" };
|
||||
|
||||
var hash1 = EffectiveFinding.ComputeContentHash("affected", null, null, null, annotations1);
|
||||
var hash2 = EffectiveFinding.ComputeContentHash("affected", null, null, null, annotations2);
|
||||
|
||||
hash1.Should().Be(hash2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EffectiveFindingHistoryEntry Tests
|
||||
|
||||
[Fact]
|
||||
public void HistoryEntry_CreateId_IsDeterministic()
|
||||
{
|
||||
var id1 = EffectiveFindingHistoryEntry.CreateId("finding-1", 5);
|
||||
var id2 = EffectiveFindingHistoryEntry.CreateId("finding-1", 5);
|
||||
|
||||
id1.Should().Be(id2);
|
||||
id1.Should().Be("finding-1:v5");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HistoryEntry_CreateId_DiffersForDifferentVersion()
|
||||
{
|
||||
var id1 = EffectiveFindingHistoryEntry.CreateId("finding-1", 1);
|
||||
var id2 = EffectiveFindingHistoryEntry.CreateId("finding-1", 2);
|
||||
|
||||
id1.Should().NotBe(id2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MaterializeFindingInput Tests
|
||||
|
||||
[Fact]
|
||||
public void MaterializeFindingInput_CanBeCreated()
|
||||
{
|
||||
var input = new MaterializeFindingInput
|
||||
{
|
||||
TenantId = "tenant-1",
|
||||
PolicyId = "policy-1",
|
||||
PolicyVersion = 1,
|
||||
ComponentPurl = "pkg:npm/lodash@4.17.21",
|
||||
ComponentName = "lodash",
|
||||
ComponentVersion = "4.17.21",
|
||||
AdvisoryId = "CVE-2021-12345",
|
||||
AdvisorySource = "nvd",
|
||||
Status = "affected",
|
||||
Severity = "High",
|
||||
RuleName = "severity-rule",
|
||||
VexStatus = "not_affected",
|
||||
VexJustification = "vulnerable_code_not_in_execute_path",
|
||||
Annotations = ImmutableDictionary<string, string>.Empty.Add("key", "value"),
|
||||
PolicyRunId = "run-123",
|
||||
TraceId = "trace-abc",
|
||||
SpanId = "span-def"
|
||||
};
|
||||
|
||||
input.TenantId.Should().Be("tenant-1");
|
||||
input.PolicyId.Should().Be("policy-1");
|
||||
input.PolicyVersion.Should().Be(1);
|
||||
input.ComponentPurl.Should().Be("pkg:npm/lodash@4.17.21");
|
||||
input.Status.Should().Be("affected");
|
||||
input.VexStatus.Should().Be("not_affected");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MaterializeFindingResult Tests
|
||||
|
||||
[Fact]
|
||||
public void MaterializeFindingResult_TracksCreation()
|
||||
{
|
||||
var result = new MaterializeFindingResult
|
||||
{
|
||||
FindingId = "sha256:abc123",
|
||||
WasCreated = true,
|
||||
WasUpdated = false,
|
||||
HistoryVersion = 1,
|
||||
ChangeType = EffectiveFindingChangeType.Created
|
||||
};
|
||||
|
||||
result.WasCreated.Should().BeTrue();
|
||||
result.WasUpdated.Should().BeFalse();
|
||||
result.ChangeType.Should().Be(EffectiveFindingChangeType.Created);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MaterializeFindingResult_TracksUpdate()
|
||||
{
|
||||
var result = new MaterializeFindingResult
|
||||
{
|
||||
FindingId = "sha256:abc123",
|
||||
WasCreated = false,
|
||||
WasUpdated = true,
|
||||
HistoryVersion = 2,
|
||||
ChangeType = EffectiveFindingChangeType.StatusChanged
|
||||
};
|
||||
|
||||
result.WasCreated.Should().BeFalse();
|
||||
result.WasUpdated.Should().BeTrue();
|
||||
result.ChangeType.Should().Be(EffectiveFindingChangeType.StatusChanged);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MaterializeBatchResult Tests
|
||||
|
||||
[Fact]
|
||||
public void MaterializeBatchResult_AggregatesCorrectly()
|
||||
{
|
||||
var results = ImmutableArray.Create(
|
||||
new MaterializeFindingResult
|
||||
{
|
||||
FindingId = "id1",
|
||||
WasCreated = true,
|
||||
WasUpdated = false,
|
||||
HistoryVersion = 1,
|
||||
ChangeType = EffectiveFindingChangeType.Created
|
||||
},
|
||||
new MaterializeFindingResult
|
||||
{
|
||||
FindingId = "id2",
|
||||
WasCreated = false,
|
||||
WasUpdated = true,
|
||||
HistoryVersion = 2,
|
||||
ChangeType = EffectiveFindingChangeType.StatusChanged
|
||||
}
|
||||
);
|
||||
|
||||
var batchResult = new MaterializeBatchResult
|
||||
{
|
||||
TotalInputs = 3,
|
||||
Created = 1,
|
||||
Updated = 1,
|
||||
Unchanged = 1,
|
||||
Errors = 0,
|
||||
ProcessingTimeMs = 100,
|
||||
Results = results
|
||||
};
|
||||
|
||||
batchResult.TotalInputs.Should().Be(3);
|
||||
batchResult.Created.Should().Be(1);
|
||||
batchResult.Updated.Should().Be(1);
|
||||
batchResult.Unchanged.Should().Be(1);
|
||||
batchResult.Results.Should().HaveCount(2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EffectiveFindingChangeType Tests
|
||||
|
||||
[Theory]
|
||||
[InlineData(EffectiveFindingChangeType.Created, "Created")]
|
||||
[InlineData(EffectiveFindingChangeType.StatusChanged, "StatusChanged")]
|
||||
[InlineData(EffectiveFindingChangeType.SeverityChanged, "SeverityChanged")]
|
||||
[InlineData(EffectiveFindingChangeType.VexApplied, "VexApplied")]
|
||||
[InlineData(EffectiveFindingChangeType.AnnotationsChanged, "AnnotationsChanged")]
|
||||
[InlineData(EffectiveFindingChangeType.PolicyVersionChanged, "PolicyVersionChanged")]
|
||||
public void EffectiveFindingChangeType_HasExpectedValues(EffectiveFindingChangeType changeType, string expectedName)
|
||||
{
|
||||
changeType.ToString().Should().Be(expectedName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user