using System; using System.Text; using System.Threading.Tasks; using System.Collections.Generic; using FluentAssertions; using StellaOps.Provenance.Attestation; using StellaOps.Cryptography; using Xunit; using StellaOps.TestKit; namespace StellaOps.Provenance.Attestation.Tests; public class SignerTests { [Trait("Category", TestCategories.Unit)] [Fact] public async Task HmacSigner_is_deterministic_for_same_input() { var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret")); var audit = new InMemoryAuditSink(); var signer = new HmacSigner(key, DefaultCryptoHmac.CreateForTests(), audit, TimeProvider.System); var request = new SignRequest(Encoding.UTF8.GetBytes("payload"), "application/json"); var r1 = await signer.SignAsync(request); var r2 = await signer.SignAsync(request); r1.Signature.Should().BeEquivalentTo(r2.Signature); r1.KeyId.Should().Be("test-key"); audit.Signed.Should().HaveCount(2); } [Trait("Category", TestCategories.Unit)] [Fact] public async Task HmacSigner_enforces_required_claims() { var key = new InMemoryKeyProvider("test-key", Encoding.UTF8.GetBytes("secret")); var audit = new InMemoryAuditSink(); var signer = new HmacSigner(key, DefaultCryptoHmac.CreateForTests(), audit, TimeProvider.System); var request = new SignRequest( Payload: Encoding.UTF8.GetBytes("payload"), ContentType: "application/json", Claims: new Dictionary { ["foo"] = "bar" }, RequiredClaims: new[] { "foo", "bar" }); var ex = await Assert.ThrowsAsync(() => signer.SignAsync(request)); ex.Message.Should().Contain("bar"); audit.Missing.Should().ContainSingle(m => m.claim == "bar"); } }