up
Some checks failed
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Simulation / policy-simulate (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Signals CI & Image / signals-ci (push) Has been cancelled
Signals Reachability Scoring & Events / reachability-smoke (push) Has been cancelled
Signals Reachability Scoring & Events / sign-and-upload (push) Has been cancelled
Docs CI / lint-and-preview (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
Scanner Analyzers / Discover Analyzers (push) Has been cancelled
Scanner Analyzers / Build Analyzers (push) Has been cancelled
Scanner Analyzers / Test Language Analyzers (push) Has been cancelled
Scanner Analyzers / Validate Test Fixtures (push) Has been cancelled
Scanner Analyzers / Verify Deterministic Output (push) Has been cancelled

This commit is contained in:
StellaOps Bot
2025-12-13 09:37:15 +02:00
parent e00f6365da
commit 6e45066e37
349 changed files with 17160 additions and 1867 deletions

View File

@@ -5,7 +5,7 @@ using Xunit;
public class ReplayManifestTests
{
[Fact]
public void SerializesWithNamespacesAndAnalysis()
public void SerializesWithNamespacesAndAnalysis_V1()
{
var manifest = new ReplayManifest
{
@@ -20,7 +20,9 @@ public class ReplayManifestTests
{
Kind = "static",
CasUri = "cas://reachability_graphs/aa/aagraph.tar.zst",
Sha256 = "aa",
Hash = "sha256:aa",
HashAlgorithm = "sha256",
Sha256 = "aa", // Legacy field for v1 compat
Namespace = "reachability_graphs",
CallgraphId = "cg-1",
Analyzer = "scanner",
@@ -31,7 +33,9 @@ public class ReplayManifestTests
{
Source = "runtime",
CasUri = "cas://runtime_traces/bb/bbtrace.tar.zst",
Sha256 = "bb",
Hash = "sha256:bb",
HashAlgorithm = "sha256",
Sha256 = "bb", // Legacy field for v1 compat
Namespace = "runtime_traces",
RecordedAt = System.DateTimeOffset.Parse("2025-11-26T00:00:00Z")
});
@@ -43,4 +47,36 @@ public class ReplayManifestTests
Assert.Contains("\"callgraphId\":\"cg-1\"", json);
Assert.Contains("\"namespace\":\"runtime_traces\"", json);
}
[Fact]
public void SerializesWithV2HashFields()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
AnalysisId = "analysis-v2"
}
};
manifest.AddReachabilityGraph(new ReplayReachabilityGraphReference
{
Kind = "static",
CasUri = "cas://reachability/graphs/blake3:abc123",
Hash = "blake3:abc123def456789012345678901234567890123456789012345678901234",
HashAlgorithm = "blake3-256",
Namespace = "reachability_graphs",
Analyzer = "scanner.java@10.0.0",
Version = "10.0.0"
});
var json = JsonSerializer.Serialize(manifest, new JsonSerializerOptions(JsonSerializerDefaults.Web));
Assert.Contains("\"schemaVersion\":\"2.0\"", json);
Assert.Contains("\"hash\":\"blake3:", json);
Assert.Contains("\"hashAlg\":\"blake3-256\"", json);
// v2 manifests should not emit legacy sha256 field (JsonIgnore when null)
Assert.DoesNotContain("\"sha256\":", json);
}
}

View File

@@ -0,0 +1,483 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using StellaOps.Replay.Core;
using Xunit;
namespace StellaOps.Replay.Core.Tests;
/// <summary>
/// Test vectors from replay-manifest-v2-acceptance.md
/// </summary>
public class ReplayManifestV2Tests
{
private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
{
WriteIndented = false
};
#region Section 4.1: Minimal Valid Manifest v2
[Fact]
public void MinimalValidManifestV2_SerializesCorrectly()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Scan = new ReplayScanMetadata
{
Id = "scan-test-001",
Time = DateTimeOffset.Parse("2025-12-13T10:00:00Z")
},
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Kind = "static",
Analyzer = "scanner.java@10.2.0",
Hash = "blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
}
},
RuntimeTraces = new List<ReplayReachabilityTraceReference>(),
CodeIdCoverage = new CodeIdCoverage
{
TotalNodes = 100,
NodesWithSymbolId = 100,
NodesWithCodeId = 0,
CoveragePercent = 100.0
}
}
};
var json = JsonSerializer.Serialize(manifest, JsonOptions);
Assert.Contains("\"schemaVersion\":\"2.0\"", json);
Assert.Contains("\"hash\":\"blake3:", json);
Assert.Contains("\"hashAlg\":\"blake3-256\"", json);
Assert.Contains("\"code_id_coverage\"", json);
Assert.Contains("\"total_nodes\":100", json);
}
#endregion
#region Section 4.2: Manifest with Runtime Traces
[Fact]
public void ManifestWithRuntimeTraces_SerializesCorrectly()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Scan = new ReplayScanMetadata
{
Id = "scan-test-002",
Time = DateTimeOffset.Parse("2025-12-13T11:00:00Z")
},
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Kind = "static",
Analyzer = "scanner.java@10.2.0",
Hash = "blake3:1111111111111111111111111111111111111111111111111111111111111111",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:1111111111111111111111111111111111111111111111111111111111111111"
}
},
RuntimeTraces = new List<ReplayReachabilityTraceReference>
{
new()
{
Source = "eventpipe",
Hash = "sha256:2222222222222222222222222222222222222222222222222222222222222222",
HashAlgorithm = "sha256",
CasUri = "cas://reachability/runtime/sha256:2222222222222222222222222222222222222222222222222222222222222222",
RecordedAt = DateTimeOffset.Parse("2025-12-13T10:30:00Z")
}
}
}
};
var json = JsonSerializer.Serialize(manifest, JsonOptions);
Assert.Contains("\"source\":\"eventpipe\"", json);
Assert.Contains("\"hash\":\"sha256:", json);
Assert.Contains("\"hashAlg\":\"sha256\"", json);
}
#endregion
#region Section 4.3: Sorting Validation
[Fact]
public void SortingValidation_UnsortedGraphs_FailsValidation()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Kind = "framework",
Hash = "blake3:zzzz1111111111111111111111111111111111111111111111111111111111",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:zzzz..."
},
new()
{
Kind = "static",
Hash = "blake3:aaaa1111111111111111111111111111111111111111111111111111111111",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:aaaa..."
}
}
}
};
var validator = new ReplayManifestValidator();
var result = validator.ValidateAsync(manifest).GetAwaiter().GetResult();
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.UnsortedEntries);
}
[Fact]
public void SortingValidation_SortedGraphs_PassesValidation()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Kind = "static",
Hash = "blake3:aaaa1111111111111111111111111111111111111111111111111111111111",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:aaaa..."
},
new()
{
Kind = "framework",
Hash = "blake3:zzzz1111111111111111111111111111111111111111111111111111111111",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:zzzz..."
}
}
}
};
var validator = new ReplayManifestValidator();
var result = validator.ValidateAsync(manifest).GetAwaiter().GetResult();
Assert.True(result.IsValid);
}
#endregion
#region Section 4.4: Invalid Manifest Vectors
[Fact]
public void InvalidManifest_MissingSchemaVersion_FailsValidation()
{
var manifest = new ReplayManifest
{
SchemaVersion = null!
};
var validator = new ReplayManifestValidator();
var result = validator.ValidateAsync(manifest).GetAwaiter().GetResult();
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.MissingVersion);
}
[Fact]
public void InvalidManifest_VersionMismatch_WhenV2Required_FailsValidation()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V1
};
var validator = new ReplayManifestValidator(requireV2: true);
var result = validator.ValidateAsync(manifest).GetAwaiter().GetResult();
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.VersionMismatch);
}
[Fact]
public void InvalidManifest_MissingHashAlg_InV2_FailsValidation()
{
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Hash = "blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
HashAlgorithm = null!, // Missing
CasUri = "cas://reachability/graphs/blake3:..."
}
}
}
};
var validator = new ReplayManifestValidator();
var result = validator.ValidateAsync(manifest).GetAwaiter().GetResult();
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.MissingHashAlg);
}
[Fact]
public async Task InvalidManifest_MissingCasReference_FailsValidation()
{
var casValidator = new InMemoryCasValidator();
// Don't register any objects
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Hash = "blake3:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:missing"
}
}
}
};
var validator = new ReplayManifestValidator(casValidator);
var result = await validator.ValidateAsync(manifest);
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.CasNotFound);
}
[Fact]
public async Task InvalidManifest_HashMismatch_FailsValidation()
{
var casValidator = new InMemoryCasValidator();
casValidator.Register(
"cas://reachability/graphs/blake3:actual",
"blake3:differenthash");
casValidator.Register(
"cas://reachability/graphs/blake3:actual.dsse",
"blake3:differenthash.dsse");
var manifest = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V2,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Hash = "blake3:expected",
HashAlgorithm = "blake3-256",
CasUri = "cas://reachability/graphs/blake3:actual"
}
}
}
};
var validator = new ReplayManifestValidator(casValidator);
var result = await validator.ValidateAsync(manifest);
Assert.False(result.IsValid);
Assert.Contains(result.Errors, e => e.ErrorCode == ReplayManifestErrorCodes.HashMismatch);
}
#endregion
#region Section 5: Migration Path
[Fact]
public void UpgradeToV2_ConvertsV1ManifestCorrectly()
{
var v1 = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V1,
Scan = new ReplayScanMetadata
{
Id = "scan-legacy"
},
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new()
{
Kind = "static",
Sha256 = "abc123",
CasUri = "cas://reachability/graphs/abc123"
}
}
}
};
var v2 = ReplayManifestValidator.UpgradeToV2(v1);
Assert.Equal(ReplayManifestVersions.V2, v2.SchemaVersion);
Assert.Single(v2.Reachability.Graphs);
Assert.Equal("sha256:abc123", v2.Reachability.Graphs[0].Hash);
Assert.Equal("sha256", v2.Reachability.Graphs[0].HashAlgorithm);
}
[Fact]
public void UpgradeToV2_SortsGraphsByUri()
{
var v1 = new ReplayManifest
{
SchemaVersion = ReplayManifestVersions.V1,
Reachability = new ReplayReachabilitySection
{
Graphs = new List<ReplayReachabilityGraphReference>
{
new() { Sha256 = "zzz", CasUri = "cas://graphs/zzz" },
new() { Sha256 = "aaa", CasUri = "cas://graphs/aaa" }
}
}
};
var v2 = ReplayManifestValidator.UpgradeToV2(v1);
Assert.Equal("cas://graphs/aaa", v2.Reachability.Graphs[0].CasUri);
Assert.Equal("cas://graphs/zzz", v2.Reachability.Graphs[1].CasUri);
}
#endregion
#region ReachabilityReplayWriter Tests
[Fact]
public void BuildManifestV2_WithValidGraphs_CreatesSortedManifest()
{
var scan = new ReplayScanMetadata { Id = "test-scan" };
var graphs = new[]
{
new ReplayReachabilityGraphReference
{
Hash = "blake3:zzzz",
CasUri = "cas://graphs/zzzz"
},
new ReplayReachabilityGraphReference
{
Hash = "blake3:aaaa",
CasUri = "cas://graphs/aaaa"
}
};
var manifest = ReachabilityReplayWriter.BuildManifestV2(
scan,
graphs,
Array.Empty<ReplayReachabilityTraceReference>());
Assert.Equal(ReplayManifestVersions.V2, manifest.SchemaVersion);
Assert.Equal("cas://graphs/aaaa", manifest.Reachability.Graphs[0].CasUri);
Assert.Equal("cas://graphs/zzzz", manifest.Reachability.Graphs[1].CasUri);
}
[Fact]
public void BuildManifestV2_WithLegacySha256_MigratesHashField()
{
var scan = new ReplayScanMetadata { Id = "test-scan" };
var graphs = new[]
{
new ReplayReachabilityGraphReference
{
Sha256 = "abc123",
CasUri = "cas://graphs/abc123"
}
};
var manifest = ReachabilityReplayWriter.BuildManifestV2(
scan,
graphs,
Array.Empty<ReplayReachabilityTraceReference>());
Assert.Equal("sha256:abc123", manifest.Reachability.Graphs[0].Hash);
Assert.Equal("sha256", manifest.Reachability.Graphs[0].HashAlgorithm);
}
[Fact]
public void BuildManifestV2_InfersHashAlgorithmFromPrefix()
{
var scan = new ReplayScanMetadata { Id = "test-scan" };
var graphs = new[]
{
new ReplayReachabilityGraphReference
{
Hash = "blake3:a1b2c3d4",
CasUri = "cas://graphs/a1b2c3d4"
}
};
var manifest = ReachabilityReplayWriter.BuildManifestV2(
scan,
graphs,
Array.Empty<ReplayReachabilityTraceReference>());
Assert.Equal("blake3-256", manifest.Reachability.Graphs[0].HashAlgorithm);
}
[Fact]
public void BuildManifestV2_RequiresAtLeastOneGraph()
{
var scan = new ReplayScanMetadata { Id = "test-scan" };
Assert.Throws<InvalidOperationException>(() =>
ReachabilityReplayWriter.BuildManifestV2(
scan,
Array.Empty<ReplayReachabilityGraphReference>(),
Array.Empty<ReplayReachabilityTraceReference>()));
}
#endregion
#region CodeIdCoverage Tests
[Fact]
public void CodeIdCoverage_SerializesWithSnakeCaseKeys()
{
var coverage = new CodeIdCoverage
{
TotalNodes = 1247,
NodesWithSymbolId = 1189,
NodesWithCodeId = 58,
CoveragePercent = 100.0
};
var json = JsonSerializer.Serialize(coverage, JsonOptions);
Assert.Contains("\"total_nodes\":1247", json);
Assert.Contains("\"nodes_with_symbol_id\":1189", json);
Assert.Contains("\"nodes_with_code_id\":58", json);
Assert.Contains("\"coverage_percent\":100", json);
}
#endregion
}