Refactor code structure and optimize performance across multiple modules

This commit is contained in:
StellaOps Bot
2025-12-26 20:03:22 +02:00
parent c786faae84
commit b4fc66feb6
3353 changed files with 88254 additions and 1590657 deletions

View File

@@ -39,7 +39,8 @@ namespace StellaOps.Attestor.Tests;
public sealed class AttestationBundleEndpointsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportEndpoint_RequiresAuthentication()
{
using var factory = new AttestorWebApplicationFactory();
@@ -50,7 +51,8 @@ public sealed class AttestationBundleEndpointsTests
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExportAndImportEndpoints_RoundTripBundles()
{
using var factory = new AttestorWebApplicationFactory();
@@ -64,6 +66,7 @@ public sealed class AttestationBundleEndpointsTests
using (var scope = factory.Services.CreateScope())
{
var repository = scope.ServiceProvider.GetRequiredService<IAttestorEntryRepository>();
using StellaOps.TestKit;
var archiveStore = scope.ServiceProvider.GetRequiredService<IAttestorArchiveStore>();
var entry = new AttestorEntry

View File

@@ -8,11 +8,13 @@ using StellaOps.Attestor.Core.Storage;
using StellaOps.Attestor.WebService.Contracts;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestationQueryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_FiltersAndPagination_Work()
{
var repository = new InMemoryAttestorEntryRepository();
@@ -83,7 +85,8 @@ public sealed class AttestationQueryTests
Assert.All(secondPage.Items, item => Assert.DoesNotContain(item.RekorUuid, firstPageIds));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryBuildQuery_ValidatesInputs()
{
var httpContext = new DefaultHttpContext();

View File

@@ -5,11 +5,13 @@ using System.Threading.Tasks;
using StellaOps.Attestor.Core.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestorEntryRepositoryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task QueryAsync_FiltersAndPagination_Work()
{
var repository = new InMemoryAttestorEntryRepository();
@@ -53,7 +55,8 @@ public sealed class AttestorEntryRepositoryTests
Assert.All(secondPage.Items, item => Assert.DoesNotContain(item.RekorUuid, seen));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAsync_EnforcesUniqueBundleSha()
{
var repository = new InMemoryAttestorEntryRepository();

View File

@@ -21,6 +21,8 @@ using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Security;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
[Collection("SmSoftGate")]
@@ -28,7 +30,8 @@ public sealed class AttestorSigningServiceTests : IDisposable
{
private readonly List<string> _temporaryPaths = new();
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SignAsync_Ed25519Key_ReturnsValidSignature()
{
var privateKey = new byte[32];
@@ -110,7 +113,8 @@ public sealed class AttestorSigningServiceTests : IDisposable
Assert.Equal("signed", auditSink.Records[0].Result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SignAsync_KmsKey_ProducesVerifiableSignature()
{
var kmsRoot = CreateTempDirectory();
@@ -215,7 +219,8 @@ public sealed class AttestorSigningServiceTests : IDisposable
Assert.Equal("signed", auditSink.Records[0].Result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SignAsync_Sm2Key_ReturnsValidSignature_WhenGateEnabled()
{
var originalGate = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");
@@ -312,7 +317,8 @@ public sealed class AttestorSigningServiceTests : IDisposable
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Sm2Registry_Fails_WhenGateDisabled()
{
var originalGate = Environment.GetEnvironmentVariable("SM_SOFT_ALLOWED");

View File

@@ -5,11 +5,13 @@ using StellaOps.Attestor.Core.Storage;
using StellaOps.Attestor.Infrastructure.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestorStorageTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAsync_PersistsAndFetchesEntry()
{
var repository = new InMemoryAttestorEntryRepository();
@@ -27,7 +29,8 @@ public sealed class AttestorStorageTests
Assert.Single(byArtifact);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAsync_UpsertsExistingDocument()
{
var repository = new InMemoryAttestorEntryRepository();
@@ -47,7 +50,8 @@ public sealed class AttestorStorageTests
Assert.Equal("pending", stored!.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemoryDedupeStore_RoundTripsAndExpires()
{
var store = new InMemoryAttestorDedupeStore();

View File

@@ -17,11 +17,14 @@ using StellaOps.Attestor.Infrastructure.Submission;
using StellaOps.Attestor.Tests.Support;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestorSubmissionServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_ReturnsDeterministicUuid_OnDuplicateBundle()
{
var options = Options.Create(new AttestorOptions
@@ -92,7 +95,8 @@ public sealed class AttestorSubmissionServiceTests
Assert.Equal(request.Meta.Artifact.Sha256, verificationCache.InvalidatedSubjects[0]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Validator_ThrowsWhenModeNotAllowed()
{
var canonicalizer = new DefaultDsseCanonicalizer();
@@ -104,7 +108,8 @@ public sealed class AttestorSubmissionServiceTests
await Assert.ThrowsAsync<AttestorValidationException>(() => validator.ValidateAsync(request));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_Throws_WhenMirrorDisabledButRequested()
{
var options = Options.Create(new AttestorOptions
@@ -163,7 +168,8 @@ public sealed class AttestorSubmissionServiceTests
Assert.Equal("mirror_disabled", ex.Code);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_ReturnsMirrorMetadata_WhenPreferenceBoth()
{
var options = Options.Create(new AttestorOptions
@@ -233,7 +239,8 @@ public sealed class AttestorSubmissionServiceTests
Assert.Equal("included", result.Mirror.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_UsesMirrorAsCanonical_WhenPreferenceMirror()
{
var options = Options.Create(new AttestorOptions

View File

@@ -7,13 +7,15 @@ using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Infrastructure.Submission;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestorSubmissionValidatorHardeningTests
{
private static readonly DefaultDsseCanonicalizer Canonicalizer = new();
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ValidateAsync_ThrowsWhenPayloadExceedsLimit()
{
var constraints = new AttestorSubmissionConstraints(
@@ -28,7 +30,8 @@ public sealed class AttestorSubmissionValidatorHardeningTests
Assert.Equal("payload_too_large", exception.Code);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ValidateAsync_ThrowsWhenCertificateChainTooLong()
{
var constraints = new AttestorSubmissionConstraints(
@@ -43,7 +46,8 @@ public sealed class AttestorSubmissionValidatorHardeningTests
Assert.Equal("certificate_chain_too_long", exception.Code);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ValidateAsync_FuzzedInputs_DoNotCrash()
{
var constraints = new AttestorSubmissionConstraints();

View File

@@ -22,6 +22,8 @@ using StellaOps.Attestor.Verify;
using StellaOps.Attestor.Tests.Support;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class AttestorVerificationServiceTests
@@ -29,7 +31,8 @@ public sealed class AttestorVerificationServiceTests
private static readonly byte[] HmacSecret = Encoding.UTF8.GetBytes("attestor-hmac-secret");
private static readonly string HmacSecretBase64 = Convert.ToBase64String(HmacSecret);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ReturnsOk_ForExistingUuid()
{
var options = Options.Create(new AttestorOptions
@@ -122,7 +125,8 @@ public sealed class AttestorVerificationServiceTests
Assert.Equal("missing", verifyResult.Report.Transparency.WitnessStatus);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_KmsBundle_Passes_WhenTwoSignaturesRequired()
{
var options = Options.Create(new AttestorOptions
@@ -213,7 +217,8 @@ public sealed class AttestorVerificationServiceTests
Assert.Equal(2, verifyResult.Report.Signatures.RequiredSignatures);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_FlagsTamperedBundle()
{
var options = Options.Create(new AttestorOptions
@@ -426,7 +431,8 @@ public sealed class AttestorVerificationServiceTests
return buffer;
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_OfflineSkipsProofRefreshWhenMissing()
{
var options = Options.Create(new AttestorOptions
@@ -490,7 +496,8 @@ public sealed class AttestorVerificationServiceTests
Assert.Equal(0, rekorClient.ProofRequests);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_OfflineUsesImportedProof()
{
var options = Options.Create(new AttestorOptions
@@ -577,7 +584,8 @@ public sealed class AttestorVerificationServiceTests
Assert.Equal(0, rekorClient.ProofRequests);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_FailsWhenWitnessRootMismatch()
{
var options = Options.Create(new AttestorOptions

View File

@@ -5,11 +5,13 @@ using StellaOps.Attestor.Core.Options;
using StellaOps.Attestor.WebService.Contracts;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class BulkVerificationContractsTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryBuildJob_ReturnsError_WhenItemsMissing()
{
var options = new AttestorOptions();
@@ -22,7 +24,8 @@ public sealed class BulkVerificationContractsTests
Assert.NotNull(error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TryBuildJob_AppliesDefaults()
{
var options = new AttestorOptions

View File

@@ -11,11 +11,14 @@ using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Infrastructure.Bulk;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class BulkVerificationWorkerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ProcessJobAsync_CompletesAllItems()
{
var jobStore = new InMemoryBulkVerificationJobStore();

View File

@@ -10,11 +10,14 @@ using StellaOps.Attestor.Core.Verification;
using StellaOps.Attestor.Infrastructure.Verification;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class CachedAttestorVerificationServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ReturnsCachedResult_OnRepeatedCalls()
{
var options = Options.Create(new AttestorOptions());
@@ -44,7 +47,8 @@ public sealed class CachedAttestorVerificationServiceTests
Assert.Equal(1, inner.VerifyCallCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_BypassesCache_WhenRefreshProofRequested()
{
var options = Options.Create(new AttestorOptions());
@@ -75,7 +79,8 @@ public sealed class CachedAttestorVerificationServiceTests
Assert.Equal(2, inner.VerifyCallCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_BypassesCache_WhenDescriptorIncomplete()
{
var options = Options.Create(new AttestorOptions());

View File

@@ -1,6 +1,7 @@
using StellaOps.Attestor.Core.Verification;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
/// <summary>
@@ -19,7 +20,8 @@ public sealed class CheckpointSignatureVerifierTests
private const string InvalidFormatCheckpoint = "not a valid checkpoint";
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_ValidFormat_ExtractsFields()
{
// Act
@@ -32,7 +34,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.NotNull(result.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_InvalidFormat_ReturnsFailure()
{
// Act
@@ -43,7 +46,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.Contains("Invalid", result.FailureReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_EmptyString_ReturnsFailure()
{
// Act
@@ -54,7 +58,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.NotNull(result.FailureReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_MinimalValidFormat_ExtractsFields()
{
// Arrange - minimal checkpoint without timestamp
@@ -74,7 +79,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.Equal(32, result.RootHash!.Length); // SHA-256 hash
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_InvalidBase64Root_ReturnsFailure()
{
// Arrange - invalid base64 in root hash
@@ -92,7 +98,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.Contains("Invalid root hash", result.FailureReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ParseCheckpoint_InvalidTreeSize_ReturnsFailure()
{
// Arrange - non-numeric tree size
@@ -110,7 +117,8 @@ public sealed class CheckpointSignatureVerifierTests
Assert.Contains("Invalid tree size", result.FailureReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyCheckpoint_NullCheckpoint_ThrowsArgumentNull()
{
// Act & Assert
@@ -118,7 +126,8 @@ public sealed class CheckpointSignatureVerifierTests
CheckpointSignatureVerifier.VerifyCheckpoint(null!, [], []));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyCheckpoint_NullSignature_ThrowsArgumentNull()
{
// Act & Assert
@@ -126,7 +135,8 @@ public sealed class CheckpointSignatureVerifierTests
CheckpointSignatureVerifier.VerifyCheckpoint("checkpoint", null!, []));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyCheckpoint_NullPublicKey_ThrowsArgumentNull()
{
// Act & Assert
@@ -134,7 +144,8 @@ public sealed class CheckpointSignatureVerifierTests
CheckpointSignatureVerifier.VerifyCheckpoint("checkpoint", [], null!));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyCheckpoint_InvalidFormat_ReturnsFailure()
{
// Arrange

View File

@@ -11,11 +11,13 @@ using StellaOps.Attestor.Core.Submission;
using StellaOps.Attestor.Infrastructure.Rekor;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class HttpRekorClientTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_ParsesResponse()
{
var payload = new
@@ -65,7 +67,8 @@ public sealed class HttpRekorClientTests
Assert.Equal("leaf", response.Proof!.Inclusion!.LeafHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_ThrowsOnConflict()
{
var client = CreateClient(HttpStatusCode.Conflict, new { error = "duplicate" });
@@ -96,7 +99,8 @@ public sealed class HttpRekorClientTests
await Assert.ThrowsAsync<InvalidOperationException>(() => rekorClient.SubmitAsync(request, backend));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetProofAsync_ReturnsNullOnNotFound()
{
var client = CreateClient(HttpStatusCode.NotFound, new { });

View File

@@ -13,11 +13,14 @@ using StellaOps.Attestor.Core.Transparency;
using StellaOps.Attestor.Infrastructure.Transparency;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class HttpTransparencyWitnessClientTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetObservationAsync_CachesSuccessfulResponses()
{
var handler = new StubHttpMessageHandler(_ =>
@@ -78,7 +81,8 @@ public sealed class HttpTransparencyWitnessClientTests
Assert.Equal(1, handler.CallCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetObservationAsync_ReturnsErrorObservation_OnNonSuccess()
{
var handler = new StubHttpMessageHandler(_ => new HttpResponseMessage(HttpStatusCode.BadGateway));
@@ -121,7 +125,8 @@ public sealed class HttpTransparencyWitnessClientTests
Assert.Equal(1, handler.CallCount);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetObservationAsync_ReturnsCachedErrorObservation_OnException()
{
var handler = new StubHttpMessageHandler(_ => throw new HttpRequestException("boom"));

View File

@@ -8,13 +8,15 @@ using StellaOps.Attestor.Core.Options;
using StellaOps.Attestor.Infrastructure.Storage;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class LiveDedupeStoreTests
{
private const string Category = "LiveTTL";
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
[Trait("Category", Category)]
public async Task Redis_dedupe_entry_sets_time_to_live()
{

View File

@@ -1,11 +1,13 @@
using StellaOps.Attestor.Core.Verification;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class MerkleProofVerifierTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HashLeaf_ProducesDeterministicHash()
{
var data = "test data"u8.ToArray();
@@ -17,7 +19,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(32, hash1.Length); // SHA-256 produces 32 bytes
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HashLeaf_IncludesLeafPrefix()
{
var data = Array.Empty<byte>();
@@ -29,7 +32,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(32, hash.Length);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HashInterior_ProducesDeterministicHash()
{
var left = new byte[] { 1, 2, 3 };
@@ -41,7 +45,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(hash1, hash2);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HashInterior_OrderMatters()
{
var a = new byte[] { 1, 2, 3 };
@@ -53,7 +58,8 @@ public sealed class MerkleProofVerifierTests
Assert.NotEqual(hashAB, hashBA);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_SingleLeafTree_Succeeds()
{
var leafData = "single leaf"u8.ToArray();
@@ -70,7 +76,8 @@ public sealed class MerkleProofVerifierTests
Assert.True(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_TwoLeafTree_LeftLeaf_Succeeds()
{
var leaf0Data = "leaf 0"u8.ToArray();
@@ -91,7 +98,8 @@ public sealed class MerkleProofVerifierTests
Assert.True(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_TwoLeafTree_RightLeaf_Succeeds()
{
var leaf0Data = "leaf 0"u8.ToArray();
@@ -112,7 +120,8 @@ public sealed class MerkleProofVerifierTests
Assert.True(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_InvalidLeafHash_Fails()
{
var leaf0Data = "leaf 0"u8.ToArray();
@@ -135,7 +144,8 @@ public sealed class MerkleProofVerifierTests
Assert.False(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_WrongRootHash_Fails()
{
var leaf0Hash = MerkleProofVerifier.HashLeaf("leaf 0"u8.ToArray());
@@ -152,7 +162,8 @@ public sealed class MerkleProofVerifierTests
Assert.False(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_InvalidIndex_Fails()
{
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
@@ -168,7 +179,8 @@ public sealed class MerkleProofVerifierTests
Assert.False(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_NegativeIndex_Fails()
{
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
@@ -183,7 +195,8 @@ public sealed class MerkleProofVerifierTests
Assert.False(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_ZeroTreeSize_Fails()
{
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
@@ -198,7 +211,8 @@ public sealed class MerkleProofVerifierTests
Assert.False(verified);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HexToBytes_ConvertsCorrectly()
{
var hex = "0102030405";
@@ -209,7 +223,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(expected, result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void HexToBytes_Handles0xPrefix()
{
var hex = "0x0102030405";
@@ -220,7 +235,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(expected, result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BytesToHex_ConvertsCorrectly()
{
var bytes = new byte[] { 0xAB, 0xCD, 0xEF };
@@ -230,7 +246,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal("abcdef", result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeRootFromPath_WithEmptyPath_ReturnsSingleLeaf()
{
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
@@ -245,7 +262,8 @@ public sealed class MerkleProofVerifierTests
Assert.Equal(leafHash, root);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeRootFromPath_WithEmptyPath_NonSingleTree_ReturnsNull()
{
var leafHash = MerkleProofVerifier.HashLeaf("test"u8.ToArray());
@@ -259,7 +277,8 @@ public sealed class MerkleProofVerifierTests
Assert.Null(root);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_FourLeafTree_AllPositions()
{
// Build a 4-leaf tree manually

View File

@@ -3,6 +3,8 @@ using System.Text.Json;
using StellaOps.Attestor.Core.Verification;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
/// <summary>
@@ -35,7 +37,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
""",
};
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_SingleLeafTree_Succeeds()
{
// Arrange - single leaf tree (tree size = 1)
@@ -54,7 +57,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.True(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_TwoLeafTree_LeftLeaf_Succeeds()
{
// Arrange - two-leaf tree, verify left leaf
@@ -78,7 +82,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.True(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_TwoLeafTree_RightLeaf_Succeeds()
{
// Arrange - two-leaf tree, verify right leaf
@@ -102,7 +107,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.True(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_FourLeafTree_AllPositions_Succeed()
{
// Arrange - four-leaf balanced tree
@@ -147,7 +153,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_WrongLeafHash_Fails()
{
// Arrange
@@ -172,7 +179,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.False(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_WrongRootHash_Fails()
{
// Arrange
@@ -195,7 +203,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.False(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_InvalidLeafIndex_Fails()
{
// Arrange
@@ -214,7 +223,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.False(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_NegativeLeafIndex_Fails()
{
// Arrange
@@ -233,7 +243,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.False(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void VerifyInclusion_ZeroTreeSize_Fails()
{
// Arrange
@@ -252,7 +263,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.False(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeRootFromPath_EmptyProof_SingleLeaf_ReturnsLeafHash()
{
// Arrange
@@ -271,7 +283,8 @@ public sealed class RekorInclusionVerificationIntegrationTests
Assert.Equal(leaf, result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ComputeRootFromPath_EmptyProof_MultiLeaf_ReturnsNull()
{
// Arrange - empty proof for multi-leaf tree is invalid

View File

@@ -27,5 +27,6 @@
<ProjectReference Include="../../../__Libraries/StellaOps.DependencyInjection/StellaOps.DependencyInjection.csproj" />
<ProjectReference Include="..\..\StellaOps.Attestor.Verify\StellaOps.Attestor.Verify.csproj" />
<ProjectReference Include="..\..\..\__Libraries\StellaOps.Cryptography.Plugin.SmSoft\StellaOps.Cryptography.Plugin.SmSoft.csproj" />
<ProjectReference Include="../../../__Libraries/StellaOps.TestKit/StellaOps.TestKit.csproj" />
</ItemGroup>
</Project>

View File

@@ -22,13 +22,15 @@ using StellaOps.Attestor.Infrastructure.Verification;
using StellaOps.Attestor.Verify;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public sealed class TimeSkewValidationIntegrationTests
{
private static readonly DateTimeOffset FixedNow = new(2025, 12, 18, 12, 0, 0, TimeSpan.Zero);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_WhenSkewRejected_Throws_WhenFailOnRejectEnabled()
{
var options = CreateOptions(new TimeSkewOptions
@@ -52,7 +54,8 @@ public sealed class TimeSkewValidationIntegrationTests
await Assert.ThrowsAsync<TimeSkewValidationException>(() => submissionService.SubmitAsync(request, context));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SubmitAsync_WhenSkewRejected_Succeeds_WhenFailOnRejectDisabled()
{
var options = CreateOptions(new TimeSkewOptions
@@ -77,7 +80,8 @@ public sealed class TimeSkewValidationIntegrationTests
Assert.False(string.IsNullOrWhiteSpace(result.Uuid));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_WhenSkewRejected_ReturnsFailed_WhenFailOnRejectEnabled()
{
var options = CreateOptions(new TimeSkewOptions
@@ -139,7 +143,8 @@ public sealed class TimeSkewValidationIntegrationTests
Assert.Contains(result.Issues, issue => issue.StartsWith("time_skew_rejected:", StringComparison.Ordinal));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_WhenSkewRejected_DoesNotFail_WhenFailOnRejectDisabled()
{
var options = CreateOptions(new TimeSkewOptions

View File

@@ -1,6 +1,7 @@
using StellaOps.Attestor.Core.Verification;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.Attestor.Tests;
public class TimeSkewValidatorTests
@@ -14,7 +15,8 @@ public class TimeSkewValidatorTests
FailOnReject = true
};
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenDisabled_ReturnsSkipped()
{
// Arrange
@@ -31,7 +33,8 @@ public class TimeSkewValidatorTests
Assert.Contains("disabled", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_WhenNoIntegratedTime_ReturnsSkipped()
{
// Arrange
@@ -46,7 +49,8 @@ public class TimeSkewValidatorTests
Assert.Contains("No integrated time", result.Message);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(0)] // No skew
[InlineData(5)] // 5 seconds ago
[InlineData(30)] // 30 seconds ago
@@ -67,7 +71,8 @@ public class TimeSkewValidatorTests
Assert.InRange(result.SkewSeconds, secondsAgo - 1, secondsAgo + 1);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(60)] // At warn threshold
[InlineData(120)] // 2 minutes
[InlineData(299)] // Just under reject threshold
@@ -87,7 +92,8 @@ public class TimeSkewValidatorTests
Assert.Contains("warning threshold", result.Message);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(300)] // At reject threshold
[InlineData(600)] // 10 minutes
[InlineData(3600)] // 1 hour
@@ -107,7 +113,8 @@ public class TimeSkewValidatorTests
Assert.Contains("rejection threshold", result.Message);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(5)] // 5 seconds in future (OK)
[InlineData(30)] // 30 seconds in future (OK)
[InlineData(60)] // At max future threshold (OK)
@@ -127,7 +134,8 @@ public class TimeSkewValidatorTests
Assert.True(result.SkewSeconds < 0); // Negative means future
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData(61)] // Just over max future
[InlineData(120)] // 2 minutes in future
[InlineData(3600)] // 1 hour in future
@@ -147,7 +155,8 @@ public class TimeSkewValidatorTests
Assert.Contains("Future timestamp", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_UsesCurrentTimeWhenLocalTimeNotProvided()
{
// Arrange
@@ -162,7 +171,8 @@ public class TimeSkewValidatorTests
Assert.InRange(result.SkewSeconds, 9, 12); // Allow for test execution time
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_CustomThresholds_AreRespected()
{
// Arrange
@@ -184,7 +194,8 @@ public class TimeSkewValidatorTests
Assert.Equal(TimeSkewStatus.Warning, result.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Validate_ReturnsCorrectTimestamps()
{
// Arrange
@@ -201,7 +212,8 @@ public class TimeSkewValidatorTests
Assert.Equal(30, result.SkewSeconds, precision: 0);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Constructor_ThrowsOnNullOptions()
{
// Act & Assert