using System.Security.Cryptography; using System.Text.Json; using FluentAssertions; using Xunit; namespace StellaOps.Reachability.FixtureTests; public class SamplesPublicFixtureTests { private static readonly string RepoRoot = ReachbenchFixtureTests.LocateRepoRoot(); private static readonly string SamplesPublicRoot = Path.Combine(RepoRoot, "tests", "reachability", "samples-public"); private static readonly string SamplesRoot = Path.Combine(SamplesPublicRoot, "samples"); private static readonly string[] RequiredFiles = [ "callgraph.static.json", "ground-truth.json", "sbom.cdx.json", "vex.openvex.json", "repro.sh" ]; [Fact] public void ManifestExistsAndIsSorted() { var manifestPath = Path.Combine(SamplesPublicRoot, "manifest.json"); File.Exists(manifestPath).Should().BeTrue("samples-public manifest should exist"); using var stream = File.OpenRead(manifestPath); using var doc = JsonDocument.Parse(stream); doc.RootElement.ValueKind.Should().Be(JsonValueKind.Array); var keys = doc.RootElement.EnumerateArray() .Select(entry => $"{entry.GetProperty("language").GetString()}/{entry.GetProperty("id").GetString()}") .ToArray(); keys.Should().NotBeEmpty("samples-public manifest should have entries"); keys.Should().BeInAscendingOrder(StringComparer.Ordinal); } [Fact] public void SamplesPublicEntriesMatchManifestHashes() { var manifestPath = Path.Combine(SamplesPublicRoot, "manifest.json"); var manifest = JsonDocument.Parse(File.ReadAllBytes(manifestPath)).RootElement.EnumerateArray().ToArray(); manifest.Should().NotBeEmpty("samples-public manifest must have entries"); foreach (var entry in manifest) { var id = entry.GetProperty("id").GetString(); var language = entry.GetProperty("language").GetString(); var files = entry.GetProperty("files"); id.Should().NotBeNullOrEmpty(); language.Should().NotBeNullOrEmpty(); files.ValueKind.Should().Be(JsonValueKind.Object); var caseDir = Path.Combine(SamplesRoot, language!, id!); Directory.Exists(caseDir).Should().BeTrue($"case folder missing: {caseDir}"); foreach (var filename in RequiredFiles) { files.TryGetProperty(filename, out var expectedHashProp).Should().BeTrue($"{id} manifest missing {filename}"); var expectedHash = expectedHashProp.GetString(); expectedHash.Should().NotBeNullOrEmpty($"{id} expected hash missing for {filename}"); var filePath = Path.Combine(caseDir, filename); File.Exists(filePath).Should().BeTrue($"{id} missing {filename}"); var actualHash = BitConverter.ToString(SHA256.HashData(File.ReadAllBytes(filePath))).Replace("-", "").ToLowerInvariant(); actualHash.Should().Be(expectedHash, $"{id} hash mismatch for {filename}"); } } } }