using System; using System.Text.Json; using System.Threading.Tasks; using StellaOps.Cryptography; using StellaOps.Scanner.Reachability; using Xunit; namespace StellaOps.Scanner.Reachability.Tests; public class RichGraphPublisherTests { [Trait("Category", TestCategories.Unit)] [Fact] public async Task PublishesGraphToCas() { var writer = new RichGraphWriter(CryptoHashFactory.CreateDefault()); var publisher = new ReachabilityRichGraphPublisher(writer); var cas = new FakeFileContentAddressableStore(); using var temp = new TempDir(); var union = new ReachabilityUnionGraph( Nodes: new[] { new ReachabilityUnionNode("sym:node:a", "node", "module") }, Edges: new ReachabilityUnionEdge[0]); var rich = RichGraphBuilder.FromUnion(union, "test", "1.0.0"); var result = await publisher.PublishAsync(rich, "scan-1", cas, temp.Path); Assert.Contains(":", result.GraphHash); // hash format: algorithm:digest Assert.StartsWith("cas://reachability/graphs/", result.CasUri); Assert.StartsWith("cas://reachability/graphs/", result.DsseCasUri); Assert.EndsWith(".dsse", result.DsseCasUri, StringComparison.Ordinal); Assert.StartsWith("sha256:", result.DsseDigest, StringComparison.Ordinal); Assert.Equal(1, result.NodeCount); var casKey = result.CasUri[(result.CasUri.LastIndexOf('/') + 1)..]; var dsseKey = $"{casKey}.dsse"; var dsseBytes = cas.GetBytes(dsseKey); Assert.NotNull(dsseBytes); using var dsseDoc = JsonDocument.Parse(dsseBytes!); Assert.Equal( "application/vnd.stellaops.graph.predicate+json", dsseDoc.RootElement.GetProperty("payloadType").GetString()); var payloadBase64Url = dsseDoc.RootElement.GetProperty("payload").GetString(); Assert.False(string.IsNullOrWhiteSpace(payloadBase64Url)); var payloadBytes = Base64UrlDecode(payloadBase64Url!); using var payloadDoc = JsonDocument.Parse(payloadBytes); using StellaOps.TestKit; Assert.Equal( result.GraphHash, payloadDoc.RootElement.GetProperty("hashes").GetProperty("graphHash").GetString()); Assert.Equal( result.CasUri, payloadDoc.RootElement.GetProperty("cas").GetProperty("location").GetString()); var signature = dsseDoc.RootElement.GetProperty("signatures")[0]; Assert.Equal("scanner-deterministic", signature.GetProperty("keyid").GetString()); } private static byte[] Base64UrlDecode(string value) { var normalized = value.Replace('-', '+').Replace('_', '/'); var remainder = normalized.Length % 4; if (remainder != 0) { normalized = normalized.PadRight(normalized.Length + (4 - remainder), '='); } return Convert.FromBase64String(normalized); } }