release orchestrator v1 draft and build fixes

This commit is contained in:
master
2026-01-12 12:24:17 +02:00
parent f3de858c59
commit 9873f80830
1598 changed files with 240385 additions and 5944 deletions

View File

@@ -0,0 +1,293 @@
// -----------------------------------------------------------------------------
// ChangeTracePredicateTests.cs
// Sprint: SPRINT_20260112_200_005_ATTEST_predicate
// Description: Tests for ChangeTracePredicate serialization.
// -----------------------------------------------------------------------------
using System.Collections.Immutable;
using System.Text.Json;
using FluentAssertions;
using StellaOps.Attestor.ProofChain.Predicates;
namespace StellaOps.Attestor.ProofChain.Tests.ChangeTrace;
/// <summary>
/// Tests for ChangeTracePredicate serialization and deserialization.
/// </summary>
[Trait("Category", "Unit")]
public sealed class ChangeTracePredicateTests
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
[Fact]
public void PredicateTypeUri_IsCorrect()
{
// Assert
ChangeTracePredicate.PredicateTypeUri.Should().Be("stella.ops/changetrace@v1");
}
[Fact]
public void Serialize_MinimalPredicate_ProducesValidJson()
{
// Arrange
var predicate = new ChangeTracePredicate
{
FromDigest = "sha256:abc123",
ToDigest = "sha256:def456",
TenantId = "tenant-1",
Summary = new ChangeTracePredicateSummary
{
ChangedPackages = 5,
ChangedSymbols = 20,
ChangedBytes = 1024,
RiskDelta = -0.25,
Verdict = "risk_down"
},
TrustDelta = new TrustDeltaRecord
{
Score = -0.25,
ReachabilityImpact = "reduced",
ExploitabilityImpact = "down"
},
AnalyzedAt = new DateTimeOffset(2026, 1, 12, 14, 30, 0, TimeSpan.Zero)
};
// Act
var json = JsonSerializer.Serialize(predicate, JsonOptions);
// Assert
json.Should().NotBeNullOrEmpty();
json.Should().Contain("\"fromDigest\"");
json.Should().Contain("\"toDigest\"");
json.Should().Contain("\"tenantId\"");
json.Should().Contain("\"summary\"");
json.Should().Contain("\"trustDelta\"");
}
[Fact]
public void RoundTrip_FullPredicate_PreservesAllData()
{
// Arrange
var predicate = new ChangeTracePredicate
{
FromDigest = "sha256:abc123",
ToDigest = "sha256:def456",
TenantId = "tenant-1",
Deltas =
[
new ChangeTraceDeltaEntry
{
Purl = "pkg:deb/debian/openssl@3.0.9",
FromVersion = "3.0.9",
ToVersion = "3.0.9-1+deb12u3",
ChangeType = "Modified",
Explain = "VendorBackport",
SymbolsChanged = 10,
BytesChanged = 2048,
Confidence = 0.95,
TrustDeltaScore = -0.3,
CveIds = ["CVE-2026-12345"],
Functions = ["ssl3_get_record"]
}
],
Summary = new ChangeTracePredicateSummary
{
ChangedPackages = 1,
ChangedSymbols = 10,
ChangedBytes = 2048,
RiskDelta = -0.3,
Verdict = "risk_down"
},
TrustDelta = new TrustDeltaRecord
{
Score = -0.3,
BeforeScore = 0.5,
AfterScore = 0.8,
ReachabilityImpact = "reduced",
ExploitabilityImpact = "down"
},
ProofSteps = ["CVE patched", "Function verified"],
DiffMethods = ["pkg", "symbol"],
Policies = ["lattice:default@v3"],
AnalyzedAt = new DateTimeOffset(2026, 1, 12, 14, 30, 0, TimeSpan.Zero),
AlgorithmVersion = "1.0.0",
CommitmentHash = "a1b2c3d4e5f6"
};
// Act
var json = JsonSerializer.Serialize(predicate, JsonOptions);
var deserialized = JsonSerializer.Deserialize<ChangeTracePredicate>(json, JsonOptions);
// Assert
deserialized.Should().NotBeNull();
deserialized!.FromDigest.Should().Be(predicate.FromDigest);
deserialized.ToDigest.Should().Be(predicate.ToDigest);
deserialized.TenantId.Should().Be(predicate.TenantId);
deserialized.Deltas.Should().HaveCount(1);
deserialized.Deltas[0].Purl.Should().Be("pkg:deb/debian/openssl@3.0.9");
deserialized.Summary.ChangedPackages.Should().Be(1);
deserialized.TrustDelta.Score.Should().Be(-0.3);
deserialized.ProofSteps.Should().HaveCount(2);
deserialized.DiffMethods.Should().HaveCount(2);
deserialized.Policies.Should().HaveCount(1);
}
[Fact]
public void Serialize_DeltaEntry_ContainsAllFields()
{
// Arrange
var entry = new ChangeTraceDeltaEntry
{
Purl = "pkg:npm/lodash@4.17.21",
FromVersion = "4.17.20",
ToVersion = "4.17.21",
ChangeType = "Upgraded",
Explain = "UpstreamUpgrade",
SymbolsChanged = 5,
BytesChanged = 512,
Confidence = 0.88,
TrustDeltaScore = -0.1,
CveIds = ["CVE-2026-00001"],
Functions = ["merge", "clone"]
};
// Act
var json = JsonSerializer.Serialize(entry, JsonOptions);
// Assert
json.Should().Contain("\"purl\"");
json.Should().Contain("\"fromVersion\"");
json.Should().Contain("\"toVersion\"");
json.Should().Contain("\"changeType\"");
json.Should().Contain("\"explain\"");
json.Should().Contain("\"symbolsChanged\"");
json.Should().Contain("\"bytesChanged\"");
json.Should().Contain("\"confidence\"");
json.Should().Contain("\"trustDeltaScore\"");
json.Should().Contain("\"cveIds\"");
json.Should().Contain("\"functions\"");
}
[Fact]
public void Serialize_Summary_ContainsAllFields()
{
// Arrange
var summary = new ChangeTracePredicateSummary
{
ChangedPackages = 3,
ChangedSymbols = 25,
ChangedBytes = 8192,
RiskDelta = 0.15,
Verdict = "neutral"
};
// Act
var json = JsonSerializer.Serialize(summary, JsonOptions);
// Assert
json.Should().Contain("\"changedPackages\"");
json.Should().Contain("\"changedSymbols\"");
json.Should().Contain("\"changedBytes\"");
json.Should().Contain("\"riskDelta\"");
json.Should().Contain("\"verdict\"");
}
[Fact]
public void Serialize_TrustDeltaRecord_ContainsAllFields()
{
// Arrange
var trustDelta = new TrustDeltaRecord
{
Score = -0.45,
BeforeScore = 0.4,
AfterScore = 0.85,
ReachabilityImpact = "eliminated",
ExploitabilityImpact = "eliminated"
};
// Act
var json = JsonSerializer.Serialize(trustDelta, JsonOptions);
// Assert
json.Should().Contain("\"score\"");
json.Should().Contain("\"beforeScore\"");
json.Should().Contain("\"afterScore\"");
json.Should().Contain("\"reachabilityImpact\"");
json.Should().Contain("\"exploitabilityImpact\"");
}
[Fact]
public void Serialize_EmptyDeltas_ProducesEmptyArray()
{
// Arrange
var predicate = new ChangeTracePredicate
{
FromDigest = "sha256:abc",
ToDigest = "sha256:def",
TenantId = "test",
Deltas = ImmutableArray<ChangeTraceDeltaEntry>.Empty,
Summary = new ChangeTracePredicateSummary
{
ChangedPackages = 0,
ChangedSymbols = 0,
ChangedBytes = 0,
RiskDelta = 0,
Verdict = "neutral"
},
TrustDelta = new TrustDeltaRecord
{
Score = 0,
ReachabilityImpact = "unchanged",
ExploitabilityImpact = "unchanged"
},
AnalyzedAt = DateTimeOffset.UtcNow
};
// Act
var json = JsonSerializer.Serialize(predicate, JsonOptions);
// Assert
json.Should().Contain("\"deltas\": []");
}
[Fact]
public void Deserialize_JsonWithMissingOptionalFields_Succeeds()
{
// Arrange
var json = """
{
"fromDigest": "sha256:abc",
"toDigest": "sha256:def",
"tenantId": "test",
"summary": {
"changedPackages": 1,
"changedSymbols": 5,
"changedBytes": 256,
"riskDelta": -0.1,
"verdict": "risk_down"
},
"trustDelta": {
"score": -0.1,
"reachabilityImpact": "reduced",
"exploitabilityImpact": "down"
},
"analyzedAt": "2026-01-12T14:30:00Z"
}
""";
// Act
var predicate = JsonSerializer.Deserialize<ChangeTracePredicate>(json, JsonOptions);
// Assert
predicate.Should().NotBeNull();
predicate!.Deltas.Should().BeEmpty();
predicate.ProofSteps.Should().BeEmpty();
predicate.DiffMethods.Should().BeEmpty();
predicate.Policies.Should().BeEmpty();
predicate.CommitmentHash.Should().BeNull();
}
}