using System.Collections.Immutable; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Excititor.Attestation.Dsse; using StellaOps.Excititor.Attestation.Signing; using StellaOps.Excititor.Attestation.Transparency; using StellaOps.Excititor.Attestation.Verification; using StellaOps.Excititor.Core; namespace StellaOps.Excititor.Attestation.Tests; public sealed class VexAttestationClientTests { [Fact] public async Task SignAsync_ReturnsEnvelopeDigestAndDiagnostics() { var signer = new FakeSigner(); var builder = new VexDsseBuilder(signer, NullLogger.Instance); var options = Options.Create(new VexAttestationClientOptions()); var verifier = new FakeVerifier(); var client = new VexAttestationClient(builder, options, NullLogger.Instance, verifier); var request = new VexAttestationRequest( ExportId: "exports/456", QuerySignature: new VexQuerySignature("filters"), Artifact: new VexContentAddress("sha256", "deadbeef"), Format: VexExportFormat.Json, CreatedAt: DateTimeOffset.UtcNow, SourceProviders: ImmutableArray.Create("vendor"), Metadata: ImmutableDictionary.Empty); var response = await client.SignAsync(request, CancellationToken.None); Assert.NotNull(response.Attestation); Assert.NotNull(response.Attestation.EnvelopeDigest); Assert.True(response.Diagnostics.ContainsKey("envelope")); } [Fact] public async Task SignAsync_SubmitsToTransparencyLog_WhenConfigured() { var signer = new FakeSigner(); var builder = new VexDsseBuilder(signer, NullLogger.Instance); var options = Options.Create(new VexAttestationClientOptions()); var transparency = new FakeTransparencyLogClient(); var verifier = new FakeVerifier(); var client = new VexAttestationClient(builder, options, NullLogger.Instance, verifier, transparencyLogClient: transparency); var request = new VexAttestationRequest( ExportId: "exports/789", QuerySignature: new VexQuerySignature("filters"), Artifact: new VexContentAddress("sha256", "deadbeef"), Format: VexExportFormat.Json, CreatedAt: DateTimeOffset.UtcNow, SourceProviders: ImmutableArray.Create("vendor"), Metadata: ImmutableDictionary.Empty); var response = await client.SignAsync(request, CancellationToken.None); Assert.NotNull(response.Attestation.Rekor); Assert.True(response.Diagnostics.ContainsKey("rekorLocation")); Assert.True(transparency.SubmitCalled); } private sealed class FakeSigner : IVexSigner { public ValueTask SignAsync(ReadOnlyMemory payload, CancellationToken cancellationToken) => ValueTask.FromResult(new VexSignedPayload("signature", "key")); } private sealed class FakeTransparencyLogClient : ITransparencyLogClient { public bool SubmitCalled { get; private set; } public ValueTask SubmitAsync(DsseEnvelope envelope, CancellationToken cancellationToken) { SubmitCalled = true; return ValueTask.FromResult(new TransparencyLogEntry(Guid.NewGuid().ToString(), "https://rekor.example/entries/123", "23", null)); } public ValueTask VerifyAsync(string entryLocation, CancellationToken cancellationToken) => ValueTask.FromResult(true); } private sealed class FakeVerifier : IVexAttestationVerifier { public ValueTask VerifyAsync(VexAttestationVerificationRequest request, CancellationToken cancellationToken) => ValueTask.FromResult(new VexAttestationVerification(true, ImmutableDictionary.Empty)); } }