up
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Mirror Thin Bundle Sign & Verify / mirror-sign (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-11-26 07:47:08 +02:00
parent 56e2f64d07
commit 1c782897f7
184 changed files with 8991 additions and 649 deletions

View File

@@ -0,0 +1,85 @@
using System.Text.Json;
using FluentAssertions;
using Xunit;
namespace StellaOps.Reachability.FixtureTests;
public class ReachbenchEvaluationHarnessTests
{
private static readonly string RepoRoot = LocateRepoRoot();
private static readonly string CasesRoot = Path.Combine(
RepoRoot,
"tests",
"reachability",
"fixtures",
"reachbench-2025-expanded",
"cases");
public static IEnumerable<object[]> CaseIds()
{
return Directory.EnumerateDirectories(CasesRoot)
.OrderBy(path => path, StringComparer.Ordinal)
.Select(path => new object[] { Path.GetFileName(path)! });
}
[Theory]
[MemberData(nameof(CaseIds))]
public void GroundTruthStatusesMatchVariantIntent(string caseId)
{
var caseJsonPath = Path.Combine(CasesRoot, caseId, "case.json");
File.Exists(caseJsonPath).Should().BeTrue();
using var caseDoc = JsonDocument.Parse(File.ReadAllBytes(caseJsonPath));
var groundTruth = caseDoc.RootElement.GetProperty("ground_truth");
groundTruth.GetProperty("reachable_variant")
.GetProperty("status")
.GetString()
.Should()
.Be("affected", $"{caseId} reachable variant should be marked affected for evaluation harness");
groundTruth.GetProperty("unreachable_variant")
.GetProperty("status")
.GetString()
.Should()
.Be("not_affected", $"{caseId} unreachable variant should be marked not_affected for evaluation harness");
}
[Theory]
[MemberData(nameof(CaseIds))]
public void TruthGraphsAlignWithExpectedReachability(string caseId)
{
var reachablePaths = CountTruthPaths(caseId, "reachable");
reachablePaths.Should().BeGreaterThan(0, $"{caseId} reachable variant should expose at least one execution path");
var unreachablePaths = CountTruthPaths(caseId, "unreachable");
unreachablePaths.Should().Be(0, $"{caseId} unreachable variant should have no execution paths");
}
private static int CountTruthPaths(string caseId, string variant)
{
var truthPath = Path.Combine(CasesRoot, caseId, "images", variant, "reachgraph.truth.json");
File.Exists(truthPath).Should().BeTrue();
using var truthDoc = JsonDocument.Parse(File.ReadAllBytes(truthPath));
var paths = truthDoc.RootElement.GetProperty("paths");
paths.ValueKind.Should().Be(JsonValueKind.Array, $"{caseId}:{variant} should list truth paths as an array");
return paths.GetArrayLength();
}
private static string LocateRepoRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)
{
if (File.Exists(Path.Combine(current.FullName, "Directory.Build.props")))
{
return current.FullName;
}
current = current.Parent;
}
throw new InvalidOperationException("Cannot locate repository root (missing Directory.Build.props).");
}
}

View File

@@ -122,7 +122,7 @@ public class ReachbenchFixtureTests
paths.ValueKind.Should().Be(JsonValueKind.Array);
}
private static string LocateRepoRoot()
internal static string LocateRepoRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current != null)

View File

@@ -0,0 +1,34 @@
using System.Text.Json;
using FluentAssertions;
using StellaOps.Replay.Core;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
public sealed class CanonicalJsonTests
{
[Fact]
public void CanonicalJson_OrdersPropertiesLexicographically()
{
var payload = new
{
zeta = 1,
alpha = new { z = 9, m = 7 },
list = new[] { new { y = 2, x = 1 } }
};
var canonical = CanonicalJson.Serialize(payload);
canonical.Should().Be("{\"alpha\":{\"m\":7,\"z\":9},\"list\":[{\"x\":1,\"y\":2}],\"zeta\":1}");
}
[Fact]
public void CanonicalJson_PreservesNumbersAndBooleans()
{
var payload = JsonSerializer.Deserialize<JsonElement>("{\"b\":true,\"a\":1.25}");
var canonical = CanonicalJson.Serialize(payload);
canonical.Should().Be("{\"a\":1.25,\"b\":true}");
}
}

View File

@@ -0,0 +1,30 @@
using System.Text;
using FluentAssertions;
using StellaOps.Replay.Core;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
public sealed class DeterministicHashTests
{
[Fact]
public void Sha256Hex_ComputesLowercaseDigest()
{
var digest = DeterministicHash.Sha256Hex("replay-core");
digest.Should().Be("a914f5ac6a57aab0189bb55bcb0ef6bcdbd86f77198c8669eab5ae38a325e41d");
}
[Fact]
public void MerkleRootHex_IsDeterministic()
{
var leaves = new[] { "alpha", "beta", "gamma" }
.Select(Encoding.UTF8.GetBytes)
.ToList();
var root = DeterministicHash.MerkleRootHex(leaves);
root.Should().Be("50298939464ed02cbf2b587250a55746b3422e133ac4f09b7e2b07869023bc9e");
DeterministicHash.MerkleRootHex(leaves).Should().Be(root);
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Text;
using FluentAssertions;
using StellaOps.Replay.Core;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
public sealed class DsseEnvelopeTests
{
[Fact]
public void BuildUnsigned_ProducesCanonicalPayload()
{
var manifest = new ReplayManifest
{
Scan = new ReplayScanMetadata
{
Id = "scan-123",
Time = DateTimeOffset.UnixEpoch
}
};
var envelope = DssePayloadBuilder.BuildUnsigned(manifest);
envelope.PayloadType.Should().Be(DssePayloadBuilder.ReplayPayloadType);
envelope.Signatures.Should().BeEmpty();
var payload = Convert.FromBase64String(envelope.Payload);
var json = Encoding.UTF8.GetString(payload);
json.Should().Be("{\"reachability\":{\"graphs\":[],\"runtimeTraces\":[]},\"scan\":{\"id\":\"scan-123\",\"time\":\"1970-01-01T00:00:00+00:00\"},\"schemaVersion\":\"1.0\"}");
envelope.DigestSha256.Should().Be(DeterministicHash.Sha256Hex(payload));
}
}

View File

@@ -0,0 +1,64 @@
using System.Collections.Generic;
using System.Formats.Tar;
using System.IO;
using FluentAssertions;
using StellaOps.Replay.Core;
using ZstdSharp;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
public sealed class ReplayBundleWriterTests
{
[Fact]
public async Task WriteTarZstAsync_IsDeterministicAndSorted()
{
var entries = new[]
{
new ReplayBundleEntry("b.txt", "beta"u8.ToArray()),
new ReplayBundleEntry("a.txt", "alpha"u8.ToArray())
};
await using var buffer = new MemoryStream();
var first = await ReplayBundleWriter.WriteTarZstAsync(entries, buffer, compressionLevel: 3);
var firstBytes = buffer.ToArray();
await using var buffer2 = new MemoryStream();
var second = await ReplayBundleWriter.WriteTarZstAsync(entries.Reverse(), buffer2, compressionLevel: 3);
first.ZstSha256.Should().Be(second.ZstSha256);
first.TarSha256.Should().Be(second.TarSha256);
firstBytes.Should().Equal(buffer2.ToArray());
// Decompress and verify ordering/content
buffer.Position = 0;
await using var decompressed = new MemoryStream();
await using (var decompress = new DecompressionStream(buffer, 16 * 1024, leaveOpen: true, enableMultiThreaded: false))
{
await decompress.CopyToAsync(decompressed);
}
decompressed.Position = 0;
var reader = new TarReader(decompressed, leaveOpen: true);
var names = new List<string>();
TarEntry? entry;
while ((entry = reader.GetNextEntry()) != null)
{
names.Add(entry.Name);
using var ms = new MemoryStream();
entry.DataStream!.CopyTo(ms);
var text = System.Text.Encoding.UTF8.GetString(ms.ToArray());
text.Should().Be(entry.Name.StartsWith("a") ? "alpha" : "beta");
}
names.Should().BeEquivalentTo(new[] { "a.txt", "b.txt" }, opts => opts.WithStrictOrdering());
}
[Fact]
public void BuildCasUri_UsesPrefixAndShard()
{
ReplayBundleWriter.BuildCasUri("abcdef", null).Should().Be("cas://replay/ab/abcdef.tar.zst");
ReplayBundleWriter.BuildCasUri("1234", "custom").Should().Be("cas://custom/12/1234.tar.zst");
}
}

View File

@@ -0,0 +1,57 @@
using FluentAssertions;
using MongoDB.Bson.Serialization;
using StellaOps.Replay.Core;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
public sealed class ReplayMongoModelsTests
{
[Fact]
public void ReplayRunRecord_SerializesWithExpectedFields()
{
var record = new ReplayRunRecord
{
Id = "scan-1",
ManifestHash = "sha256:abc",
Status = "verified",
Outputs = new ReplayRunOutputs { Sbom = "sha256:sbom", Findings = "sha256:findings", Vex = "sha256:vex" },
Signatures = new() { new ReplaySignatureRecord { Profile = "FIPS", Verified = true } }
};
var bson = record.ToBsonDocument();
bson.Should().ContainKey("_id");
bson["manifestHash"].AsString.Should().Be("sha256:abc");
bson["status"].AsString.Should().Be("verified");
bson["outputs"].AsBsonDocument["sbom"].AsString.Should().Be("sha256:sbom");
bson["signatures"].AsBsonArray.Should().HaveCount(1);
}
[Fact]
public void ReplayBundleRecord_UsesIdAsDigest()
{
var record = new ReplayBundleRecord { Id = "abc", Type = "input", Size = 10, Location = "cas://replay/ab/abc.tar.zst" };
var bson = record.ToBsonDocument();
bson["_id"].AsString.Should().Be("abc");
bson["type"].AsString.Should().Be("input");
}
[Fact]
public void ReplaySubjectRecord_StoresLayers()
{
var record = new ReplaySubjectRecord
{
OciDigest = "sha256:img",
Layers = new()
{
new ReplayLayerRecord { LayerDigest = "l1", MerkleRoot = "m1", LeafCount = 2 },
new ReplayLayerRecord { LayerDigest = "l2", MerkleRoot = "m2", LeafCount = 3 }
}
};
var doc = record.ToBsonDocument();
doc["layers"].AsBsonArray.Should().HaveCount(2);
}
}