semi implemented and features implemented save checkpoint

This commit is contained in:
master
2026-02-08 18:00:49 +02:00
parent 04360dff63
commit 1bf6bbf395
20895 changed files with 716795 additions and 64 deletions

View File

@@ -16,6 +16,7 @@ using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.X509;
using StellaOps.Attestor.Core.Rekor;
using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Infrastructure.Rekor;
using StellaOps.TestKit;
@@ -25,6 +26,40 @@ namespace StellaOps.Attestor.Infrastructure.Tests;
public sealed class HttpRekorClientTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_HashPrecheckHit_ReusesExistingEntryWithoutNewSubmission()
{
var handler = new HashPrecheckHitHandler();
var client = CreateClient(handler);
var backend = CreateBackend();
var response = await client.SubmitAsync(CreateSubmissionRequest(), backend, CancellationToken.None);
response.Uuid.Should().Be("11111111-1111-1111-1111-111111111111");
response.Index.Should().Be(777);
response.Status.Should().Be("included");
handler.SubmitCalls.Should().Be(0);
handler.IndexLookupCalls.Should().BeGreaterThan(0);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_HashPrecheckUnsupported_FallsBackToSubmission()
{
var handler = new HashPrecheckUnsupportedHandler();
var client = CreateClient(handler);
var backend = CreateBackend();
var response = await client.SubmitAsync(CreateSubmissionRequest(), backend, CancellationToken.None);
response.Uuid.Should().Be("new-uuid");
response.Index.Should().Be(42);
response.Status.Should().Be("included");
handler.SubmitCalls.Should().Be(1);
handler.IndexLookupCalls.Should().BeGreaterThan(0);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyInclusionAsync_MissingLogIndex_ReturnsFailure()
@@ -201,6 +236,40 @@ public sealed class HttpRekorClientTests
return new HttpRekorClient(httpClient, NullLogger<HttpRekorClient>.Instance);
}
private static AttestorSubmissionRequest CreateSubmissionRequest()
{
return new AttestorSubmissionRequest
{
Bundle = new AttestorSubmissionRequest.SubmissionBundle
{
Dsse = new AttestorSubmissionRequest.DsseEnvelope
{
PayloadType = "application/vnd.in-toto+json",
PayloadBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes("{}")),
Signatures =
{
new AttestorSubmissionRequest.DsseSignature
{
KeyId = "test-key",
Signature = "sig"
}
}
}
},
Meta = new AttestorSubmissionRequest.SubmissionMeta
{
BundleSha256 = new string('a', 64),
Artifact = new AttestorSubmissionRequest.ArtifactInfo
{
Sha256 = new string('b', 64),
Kind = "sbom"
},
LogPreference = "primary",
Archive = false
}
};
}
private static RekorBackend CreateBackend()
{
return new RekorBackend
@@ -308,6 +377,63 @@ public sealed class HttpRekorClientTests
}
}
private sealed class HashPrecheckHitHandler : HttpMessageHandler
{
public int IndexLookupCalls { get; private set; }
public int SubmitCalls { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var path = request.RequestUri?.AbsolutePath ?? string.Empty;
if (request.Method == HttpMethod.Post && path.EndsWith("/api/v2/index/retrieve", StringComparison.Ordinal))
{
IndexLookupCalls++;
return Task.FromResult(BuildResponse("""["11111111-1111-1111-1111-111111111111"]"""));
}
if (request.Method == HttpMethod.Get && path.EndsWith("/api/v2/log/entries/11111111-1111-1111-1111-111111111111", StringComparison.Ordinal))
{
return Task.FromResult(BuildResponse("""{"uuid":"11111111-1111-1111-1111-111111111111","logIndex":777,"status":"included","integratedTime":1735689600}"""));
}
if (request.Method == HttpMethod.Post && path.EndsWith("/api/v2/log/entries", StringComparison.Ordinal))
{
SubmitCalls++;
return Task.FromResult(BuildResponse("""{"uuid":"should-not-submit","index":1,"status":"included"}"""));
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
private sealed class HashPrecheckUnsupportedHandler : HttpMessageHandler
{
public int IndexLookupCalls { get; private set; }
public int SubmitCalls { get; private set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var path = request.RequestUri?.AbsolutePath ?? string.Empty;
if (request.Method == HttpMethod.Post &&
(path.EndsWith("/api/v2/index/retrieve", StringComparison.Ordinal) ||
path.EndsWith("/api/v1/index/retrieve", StringComparison.Ordinal)))
{
IndexLookupCalls++;
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
if (request.Method == HttpMethod.Post && path.EndsWith("/api/v2/log/entries", StringComparison.Ordinal))
{
SubmitCalls++;
return Task.FromResult(BuildResponse("""{"uuid":"new-uuid","index":42,"status":"included","integratedTime":1735689600}"""));
}
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
private sealed class ProofOnlyHandler : HttpMessageHandler
{
private readonly string _proofJson;

View File

@@ -12,3 +12,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| VAL-SMOKE-001 | DONE | Removed xUnit v2 references and verified unit tests pass. |
| AUDIT-0208-T | DONE | Revalidated 2026-01-08 (raw string + xUnit1051 fixes). |
| AUDIT-0208-A | DONE | Applied fixes 2026-01-08 (raw string + xUnit1051 fixes). |
| SPRINT-20260208-060-IDEMP-001 | DONE | Added deterministic unit coverage for Rekor hash pre-check and submission fallback behavior; `HttpRekorClientTests` passing (2026-02-08). |