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 f10d83c444
1385 changed files with 69732 additions and 10280 deletions

View File

@@ -8,6 +8,7 @@ using StellaOps.TaskRunner.Storage.Postgres.Repositories;
using StellaOps.Infrastructure.Postgres.Options;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Storage.Postgres.Tests;
[Collection(TaskRunnerPostgresCollection.Name)]
@@ -41,7 +42,8 @@ public sealed class PostgresPackRunStateStoreTests : IAsyncLifetime
await _dataSource.DisposeAsync();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetAsync_ReturnsNullForUnknownRunId()
{
// Act
@@ -51,7 +53,8 @@ public sealed class PostgresPackRunStateStoreTests : IAsyncLifetime
result.Should().BeNull();
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAndGet_RoundTripsState()
{
// Arrange
@@ -70,7 +73,8 @@ public sealed class PostgresPackRunStateStoreTests : IAsyncLifetime
fetched.Steps.Should().HaveCount(1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAsync_UpdatesExistingState()
{
// Arrange
@@ -88,7 +92,8 @@ public sealed class PostgresPackRunStateStoreTests : IAsyncLifetime
fetched!.PlanHash.Should().Be("sha256:hash2");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListAsync_ReturnsAllStates()
{
// Arrange

View File

@@ -4,11 +4,13 @@ using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using StellaOps.TaskRunner.WebService.Deprecation;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class ApiDeprecationTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DeprecatedEndpoint_PathPattern_MatchesExpected()
{
var endpoint = new DeprecatedEndpoint
@@ -25,7 +27,8 @@ public sealed class ApiDeprecationTests
Assert.NotNull(endpoint.SunsetAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ApiDeprecationOptions_DefaultValues_AreCorrect()
{
var options = new ApiDeprecationOptions();
@@ -36,7 +39,8 @@ public sealed class ApiDeprecationTests
Assert.Empty(options.DeprecatedEndpoints);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoggingDeprecationNotificationService_GetUpcoming_FiltersCorrectly()
{
var now = DateTimeOffset.UtcNow;
@@ -73,7 +77,8 @@ public sealed class ApiDeprecationTests
Assert.Equal("/v1/soon/*", upcoming[0].EndpointPath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoggingDeprecationNotificationService_GetUpcoming_OrdersBySunsetDate()
{
var now = DateTimeOffset.UtcNow;
@@ -100,7 +105,8 @@ public sealed class ApiDeprecationTests
Assert.Equal("/v1/third/*", upcoming[2].EndpointPath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DeprecationInfo_DaysUntilSunset_CalculatesCorrectly()
{
var now = DateTimeOffset.UtcNow;
@@ -118,7 +124,8 @@ public sealed class ApiDeprecationTests
Assert.Equal("/v2/test/*", info.ReplacementPath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void DeprecationNotification_RecordProperties_AreAccessible()
{
var notification = new DeprecationNotification(
@@ -135,7 +142,8 @@ public sealed class ApiDeprecationTests
Assert.Equal(2, notification.AffectedConsumerIds?.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PathPattern_WildcardToRegex_MatchesSingleSegment()
{
var pattern = "^" + Regex.Escape("/v1/packs/*")
@@ -148,7 +156,8 @@ public sealed class ApiDeprecationTests
Assert.DoesNotMatch(pattern, "/v2/packs/foo");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PathPattern_DoubleWildcard_MatchesMultipleSegments()
{
var pattern = "^" + Regex.Escape("/v1/legacy/**")

View File

@@ -2,11 +2,13 @@ using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.TaskRunner.Core.Evidence;
using StellaOps.TaskRunner.Core.Events;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class BundleImportEvidenceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BundleImportHashChain_Compute_CreatesDeterministicHash()
{
var input = new BundleImportInputManifest(
@@ -41,7 +43,8 @@ public sealed class BundleImportEvidenceTests
Assert.StartsWith("sha256:", chain1.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BundleImportHashChain_Compute_DifferentInputsProduceDifferentHashes()
{
var input1 = new BundleImportInputManifest(
@@ -78,7 +81,8 @@ public sealed class BundleImportEvidenceTests
Assert.NotEqual(chain1.InputsHash, chain2.InputsHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_CaptureAsync_StoresEvidence()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -96,7 +100,8 @@ public sealed class BundleImportEvidenceTests
Assert.Equal(1, store.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_CaptureAsync_CreatesCorrectMaterials()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -120,7 +125,8 @@ public sealed class BundleImportEvidenceTests
Assert.Contains(snapshot.Materials, m => m.Section == "hashchain");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_CaptureAsync_SetsCorrectMetadata()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -141,7 +147,8 @@ public sealed class BundleImportEvidenceTests
Assert.Equal("2", snapshot.Metadata["outputCount"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_CaptureAsync_EmitsTimelineEvent()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -165,7 +172,8 @@ public sealed class BundleImportEvidenceTests
Assert.Equal("bundle.import.evidence_captured", evt.EventType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_GetAsync_ReturnsEvidence()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -183,7 +191,8 @@ public sealed class BundleImportEvidenceTests
Assert.Equal(evidence.TenantId, retrieved.TenantId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_GetAsync_ReturnsNullForMissingJob()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -196,7 +205,8 @@ public sealed class BundleImportEvidenceTests
Assert.Null(retrieved);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_ExportToPortableBundleAsync_CreatesFile()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -230,7 +240,8 @@ public sealed class BundleImportEvidenceTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task BundleImportEvidenceService_ExportToPortableBundleAsync_FailsForMissingJob()
{
var store = new InMemoryPackRunEvidenceStore();
@@ -249,7 +260,8 @@ public sealed class BundleImportEvidenceTests
Assert.Contains("No evidence found", result.Error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BundleImportEvidence_RecordProperties_AreAccessible()
{
var evidence = CreateTestEvidence();
@@ -264,7 +276,8 @@ public sealed class BundleImportEvidenceTests
Assert.NotNull(evidence.ValidationResult);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BundleImportValidationResult_RecordProperties_AreAccessible()
{
var result = new BundleImportValidationResult(

View File

@@ -10,7 +10,8 @@ namespace StellaOps.TaskRunner.Tests;
public sealed class BundleIngestionStepExecutorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExecuteAsync_ValidBundle_CopiesAndSucceeds()
{
using var temp = new TempDirectory();
@@ -41,7 +42,8 @@ public sealed class BundleIngestionStepExecutorTests
Assert.Contains(checksum, metadata, StringComparison.OrdinalIgnoreCase);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExecuteAsync_ChecksumMismatch_Fails()
{
using var temp = new TempDirectory();
@@ -64,10 +66,12 @@ public sealed class BundleIngestionStepExecutorTests
Assert.Contains("Checksum mismatch", result.Error, StringComparison.OrdinalIgnoreCase);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExecuteAsync_MissingChecksum_Fails()
{
using var temp = new TempDirectory();
using StellaOps.TestKit;
var source = Path.Combine(temp.Path, "bundle.tgz");
var ct = TestContext.Current.CancellationToken;
await File.WriteAllTextAsync(source, "bundle-data", ct);
@@ -86,7 +90,8 @@ public sealed class BundleIngestionStepExecutorTests
Assert.Contains("Checksum is required", result.Error, StringComparison.OrdinalIgnoreCase);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExecuteAsync_UnknownUses_NoOpSuccess()
{
var ct = TestContext.Current.CancellationToken;

View File

@@ -1,6 +1,7 @@
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Infrastructure.Execution;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilePackRunLogStoreTests : IDisposable
@@ -12,7 +13,8 @@ public sealed class FilePackRunLogStoreTests : IDisposable
rootPath = Path.Combine(Path.GetTempPath(), "StellaOps_TaskRunnerTests", Guid.NewGuid().ToString("n"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task AppendAndReadAsync_RoundTripsEntriesInOrder()
{
var store = new FilePackRunLogStore(rootPath);
@@ -61,7 +63,8 @@ public sealed class FilePackRunLogStoreTests : IDisposable
});
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ExistsAsync_ReturnsFalseWhenNoLogPresent()
{
var store = new FilePackRunLogStore(rootPath);

View File

@@ -3,11 +3,13 @@ using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Infrastructure.Execution;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilePackRunStateStoreTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task SaveAndGetAsync_RoundTripsState()
{
var directory = CreateTempDirectory();
@@ -34,7 +36,8 @@ public sealed class FilePackRunStateStoreTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListAsync_ReturnsStatesInDeterministicOrder()
{
var directory = CreateTempDirectory();

View File

@@ -5,7 +5,8 @@ namespace StellaOps.TaskRunner.Tests;
public sealed class FilesystemPackRunArtifactReaderTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListAsync_ReturnsEmpty_WhenManifestMissing()
{
using var temp = new TempDir();
@@ -17,10 +18,12 @@ public sealed class FilesystemPackRunArtifactReaderTests
Assert.Empty(results);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListAsync_ParsesManifestAndSortsByName()
{
using var temp = new TempDir();
using StellaOps.TestKit;
var runId = "run-1";
var manifestPath = Path.Combine(temp.Path, "run-1", "artifact-manifest.json");
Directory.CreateDirectory(Path.GetDirectoryName(manifestPath)!);

View File

@@ -6,6 +6,7 @@ using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Infrastructure.Execution;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilesystemPackRunArtifactUploaderTests : IDisposable
@@ -17,7 +18,8 @@ public sealed class FilesystemPackRunArtifactUploaderTests : IDisposable
artifactsRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("n"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CopiesFileOutputs()
{
var sourceFile = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():n}.txt");
@@ -43,7 +45,8 @@ public sealed class FilesystemPackRunArtifactUploaderTests : IDisposable
Assert.Equal("files/bundle.txt", manifest.Outputs[0].StoredPath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task RecordsMissingFilesWithoutThrowing()
{
var uploader = CreateUploader();
@@ -57,7 +60,8 @@ public sealed class FilesystemPackRunArtifactUploaderTests : IDisposable
Assert.Equal("missing", manifest.Outputs[0].Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task WritesExpressionOutputsAsJson()
{
var uploader = CreateUploader();

View File

@@ -2,11 +2,13 @@ using System.Text.Json;
using StellaOps.AirGap.Policy;
using StellaOps.TaskRunner.Infrastructure.Execution;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class FilesystemPackRunDispatcherTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TryDequeueAsync_BlocksJob_WhenEgressPolicyDeniesDestination()
{
var root = Path.Combine(Path.GetTempPath(), "StellaOps_TaskRunnerTests", Guid.NewGuid().ToString("n"));

View File

@@ -1,10 +1,12 @@
using StellaOps.TaskRunner.WebService;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class OpenApiMetadataFactoryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_ProducesExpectedDefaults()
{
var metadata = OpenApiMetadataFactory.Create();
@@ -20,7 +22,8 @@ public sealed class OpenApiMetadataFactoryTests
Assert.True(hashPart.All(c => char.IsDigit(c) || (c >= 'a' && c <= 'f')));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_AllowsOverrideUrl()
{
var metadata = OpenApiMetadataFactory.Create("/docs/openapi.json");
@@ -28,7 +31,8 @@ public sealed class OpenApiMetadataFactoryTests
Assert.Equal("/docs/openapi.json", metadata.SpecUrl);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_SignatureIncludesAllComponents()
{
var metadata1 = OpenApiMetadataFactory.Create("/path1");
@@ -38,7 +42,8 @@ public sealed class OpenApiMetadataFactoryTests
Assert.NotEqual(metadata1.Signature, metadata2.Signature);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_ETagIsDeterministic()
{
var metadata1 = OpenApiMetadataFactory.Create();

View File

@@ -2,11 +2,13 @@ using System.Text.Json.Nodes;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunApprovalCoordinatorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_FromPlan_PopulatesApprovals()
{
var plan = BuildPlan();
@@ -18,7 +20,8 @@ public sealed class PackRunApprovalCoordinatorTests
Assert.Equal(PackRunApprovalStatus.Pending, approvals[0].Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Approve_AllowsResumeWhenLastApprovalCompletes()
{
var plan = BuildPlan();
@@ -31,7 +34,8 @@ public sealed class PackRunApprovalCoordinatorTests
Assert.Equal("approver-1", result.State.ActorId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Reject_DoesNotResumeAndMarksState()
{
var plan = BuildPlan();
@@ -44,7 +48,8 @@ public sealed class PackRunApprovalCoordinatorTests
Assert.Equal("Not safe", result.State.Summary);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BuildNotifications_UsesRequirements()
{
var plan = BuildPlan();
@@ -57,7 +62,8 @@ public sealed class PackRunApprovalCoordinatorTests
Assert.Contains("Packs.Approve", notification.RequiredGrants);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void BuildPolicyNotifications_ProducesGateMetadata()
{
var plan = BuildPolicyPlan();

View File

@@ -4,11 +4,13 @@ using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Infrastructure.Execution;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunApprovalDecisionServiceTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ApplyAsync_ApprovingLastGateSchedulesResume()
{
var plan = TestPlanFactory.CreatePlan();
@@ -48,7 +50,8 @@ public sealed class PackRunApprovalDecisionServiceTests
Assert.Equal(PackRunApprovalStatus.Approved, approvalStore.LastUpdated?.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ApplyAsync_ReturnsNotFoundWhenStateMissing()
{
var approvalStore = new InMemoryApprovalStore(new Dictionary<string, IReadOnlyList<PackRunApprovalState>>());
@@ -69,7 +72,8 @@ public sealed class PackRunApprovalDecisionServiceTests
Assert.False(scheduler.ScheduledContexts.Any());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ApplyAsync_ReturnsPlanHashMismatchWhenIncorrect()
{
var plan = TestPlanFactory.CreatePlan();
@@ -107,7 +111,8 @@ public sealed class PackRunApprovalDecisionServiceTests
Assert.False(scheduler.ScheduledContexts.Any());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ApplyAsync_ReturnsPlanHashMismatchWhenFormatInvalid()
{
var plan = TestPlanFactory.CreatePlan();

View File

@@ -3,11 +3,13 @@ using StellaOps.TaskRunner.Core.Attestation;
using StellaOps.TaskRunner.Core.Events;
using StellaOps.TaskRunner.Core.Evidence;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunAttestationTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GenerateAsync_CreatesAttestationWithSubjects()
{
var store = new InMemoryPackRunAttestationStore();
@@ -45,7 +47,8 @@ public sealed class PackRunAttestationTests
Assert.NotNull(result.Attestation.Envelope);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GenerateAsync_WithoutSigner_CreatesPendingAttestation()
{
var store = new InMemoryPackRunAttestationStore();
@@ -79,7 +82,8 @@ public sealed class PackRunAttestationTests
Assert.Null(result.Attestation.Envelope);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GenerateAsync_EmitsTimelineEvent()
{
var store = new InMemoryPackRunAttestationStore();
@@ -115,7 +119,8 @@ public sealed class PackRunAttestationTests
Assert.Equal(PackRunAttestationEventTypes.AttestationCreated, evt.EventType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ValidatesSubjectsMatch()
{
var store = new InMemoryPackRunAttestationStore();
@@ -161,7 +166,8 @@ public sealed class PackRunAttestationTests
Assert.Equal(PackRunRevocationStatus.NotRevoked, verifyResult.RevocationStatus);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_DetectsMismatchedSubjects()
{
var store = new InMemoryPackRunAttestationStore();
@@ -213,7 +219,8 @@ public sealed class PackRunAttestationTests
Assert.Contains(verifyResult.Errors, e => e.Contains("Missing subjects"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_DetectsRevokedAttestation()
{
var store = new InMemoryPackRunAttestationStore();
@@ -264,7 +271,8 @@ public sealed class PackRunAttestationTests
Assert.Equal(PackRunRevocationStatus.Revoked, verifyResult.RevocationStatus);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task VerifyAsync_ReturnsErrorForNonExistentAttestation()
{
var store = new InMemoryPackRunAttestationStore();
@@ -286,7 +294,8 @@ public sealed class PackRunAttestationTests
Assert.Contains(verifyResult.Errors, e => e.Contains("not found"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListByRunAsync_ReturnsAttestationsForRun()
{
var store = new InMemoryPackRunAttestationStore();
@@ -321,7 +330,8 @@ public sealed class PackRunAttestationTests
Assert.All(attestations, a => Assert.Equal("run-007", a.RunId));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetEnvelopeAsync_ReturnsEnvelopeForSignedAttestation()
{
var store = new InMemoryPackRunAttestationStore();
@@ -354,7 +364,8 @@ public sealed class PackRunAttestationTests
Assert.Single(envelope.Signatures);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunAttestationSubject_FromArtifact_ParsesSha256Prefix()
{
var artifact = new PackRunArtifactReference(
@@ -369,7 +380,8 @@ public sealed class PackRunAttestationTests
Assert.Equal("abcdef123456", subject.Digest["sha256"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunAttestation_ComputeStatementDigest_IsDeterministic()
{
var subjects = new List<PackRunAttestationSubject>
@@ -399,7 +411,8 @@ public sealed class PackRunAttestationTests
Assert.StartsWith("sha256:", digest1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunDsseEnvelope_ComputeDigest_IsDeterministic()
{
var envelope = new PackRunDsseEnvelope(
@@ -414,7 +427,8 @@ public sealed class PackRunAttestationTests
Assert.StartsWith("sha256:", digest1);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GenerateAsync_WithExternalParameters_IncludesInPredicate()
{
var store = new InMemoryPackRunAttestationStore();
@@ -450,7 +464,8 @@ public sealed class PackRunAttestationTests
Assert.Contains("manifestUrl", result.Attestation.PredicateJson);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GenerateAsync_WithResolvedDependencies_IncludesInPredicate()
{
var store = new InMemoryPackRunAttestationStore();

View File

@@ -6,6 +6,7 @@ using StellaOps.TaskRunner.Core.Execution.Simulation;
using StellaOps.TaskRunner.Core.Planning;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
/// <summary>
@@ -21,7 +22,8 @@ public sealed class PackRunEvidenceSnapshotTests
#region PackRunEvidenceSnapshot Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithMaterials_ComputesMerkleRoot()
{
// Arrange
@@ -49,7 +51,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.StartsWith("sha256:", snapshot.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithEmptyMaterials_ReturnsZeroHash()
{
// Act
@@ -64,7 +67,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal("sha256:" + new string('0', 64), snapshot.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithMetadata_StoresMetadata()
{
// Arrange
@@ -89,7 +93,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal("value2", snapshot.Metadata["key2"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_SameMaterials_ProducesDeterministicHash()
{
// Arrange
@@ -111,7 +116,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal(snapshot1.RootHash, snapshot2.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_MaterialOrderDoesNotAffectHash()
{
// Arrange - materials in different order
@@ -140,7 +146,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal(snapshot1.RootHash, snapshot2.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToJson_AndFromJson_RoundTrips()
{
// Arrange
@@ -167,7 +174,8 @@ public sealed class PackRunEvidenceSnapshotTests
#region PackRunEvidenceMaterial Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void FromString_ComputesSha256Hash()
{
// Act
@@ -182,7 +190,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal(13, material.SizeBytes); // "Hello, World!" is 13 bytes
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void FromJson_ComputesSha256Hash()
{
// Arrange
@@ -198,7 +207,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal("application/json", material.MediaType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void FromContent_WithAttributes_StoresAttributes()
{
// Arrange
@@ -214,7 +224,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal("step-001", material.Attributes["stepId"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CanonicalPath_CombinesSectionAndPath()
{
// Act
@@ -228,7 +239,8 @@ public sealed class PackRunEvidenceSnapshotTests
#region InMemoryPackRunEvidenceStore Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Store_AndGet_ReturnsSnapshot()
{
// Arrange
@@ -248,7 +260,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal(snapshot.RootHash, retrieved.RootHash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Get_NonExistent_ReturnsNull()
{
// Arrange
@@ -261,7 +274,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Null(result);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListByRun_ReturnsMatchingSnapshots()
{
// Arrange
@@ -291,7 +305,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.All(results, s => Assert.Equal(TestRunId, s.RunId));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ListByKind_ReturnsMatchingSnapshots()
{
// Arrange
@@ -324,7 +339,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.All(results, s => Assert.Equal(PackRunEvidenceSnapshotKind.StepExecution, s.Kind));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Verify_ValidSnapshot_ReturnsValid()
{
// Arrange
@@ -349,7 +365,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Null(result.Error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Verify_NonExistent_ReturnsInvalid()
{
// Arrange
@@ -367,7 +384,8 @@ public sealed class PackRunEvidenceSnapshotTests
#region PackRunRedactionGuard Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactTranscript_RedactsSensitiveOutput()
{
// Arrange
@@ -393,7 +411,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains("[REDACTED", redacted.Output);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactTranscript_PreservesNonSensitiveOutput()
{
// Arrange
@@ -418,7 +437,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal("Build completed successfully", redacted.Output);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactIdentity_RedactsEmail()
{
// Arrange
@@ -433,7 +453,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains("[", redacted); // Contains redaction markers
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactIdentity_HashesNonEmailIdentity()
{
// Arrange
@@ -447,7 +468,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.EndsWith("]", redacted);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactApproval_RedactsApproverAndComments()
{
// Arrange
@@ -470,7 +492,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains("[REDACTED", redacted.Comments);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RedactValue_ReturnsHashedValue()
{
// Arrange
@@ -485,7 +508,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.DoesNotContain("super-secret-value", redacted);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void NoOpRedactionGuard_PreservesAllData()
{
// Arrange
@@ -515,7 +539,8 @@ public sealed class PackRunEvidenceSnapshotTests
#region PackRunEvidenceSnapshotService Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CaptureRunCompletion_StoresSnapshot()
{
// Arrange
@@ -544,7 +569,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Equal(1, store.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CaptureRunCompletion_WithTranscripts_IncludesRedactedTranscripts()
{
// Arrange
@@ -574,7 +600,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.NotNull(transcriptMaterial);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CaptureStepExecution_CapturesTranscript()
{
// Arrange
@@ -599,7 +626,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains(result.Snapshot.Materials, m => m.Section == "transcript");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CaptureApprovalDecision_CapturesApproval()
{
// Arrange
@@ -629,7 +657,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains(result.Snapshot.Materials, m => m.Section == "approval");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CapturePolicyEvaluation_CapturesEvaluation()
{
// Arrange
@@ -659,7 +688,8 @@ public sealed class PackRunEvidenceSnapshotTests
Assert.Contains(result.Snapshot.Materials, m => m.Section == "policy");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task CaptureRunCompletion_EmitsTimelineEvent()
{
// Arrange

View File

@@ -2,11 +2,13 @@ using System.Text.Json.Nodes;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunExecutionGraphBuilderTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Build_GeneratesParallelMetadata()
{
var manifest = TestManifests.Load(TestManifests.Parallel);
@@ -31,7 +33,8 @@ public sealed class PackRunExecutionGraphBuilderTests
Assert.All(parallel.Children, child => Assert.Equal(PackRunStepKind.Run, child.Kind));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Build_PreservesMapIterationsAndDisabledSteps()
{
var planner = new TaskPackPlanner();

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunGateStateUpdaterTests
@@ -10,7 +11,8 @@ public sealed class PackRunGateStateUpdaterTests
private static readonly DateTimeOffset RequestedAt = DateTimeOffset.UnixEpoch;
private static readonly DateTimeOffset UpdateTimestamp = DateTimeOffset.UnixEpoch.AddMinutes(5);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Apply_ApprovedGate_ClearsReasonAndSucceeds()
{
var plan = BuildApprovalPlan();
@@ -30,7 +32,8 @@ public sealed class PackRunGateStateUpdaterTests
Assert.Equal(UpdateTimestamp, gate.LastTransitionAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Apply_RejectedGate_FlagsFailure()
{
var plan = BuildApprovalPlan();
@@ -50,7 +53,8 @@ public sealed class PackRunGateStateUpdaterTests
Assert.Equal(UpdateTimestamp, gate.LastTransitionAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Apply_PolicyGate_ClearsPendingReason()
{
var plan = BuildPolicyPlan();

View File

@@ -3,11 +3,13 @@ using Microsoft.Extensions.Time.Testing;
using StellaOps.TaskRunner.Core.Events;
using StellaOps.TaskRunner.Core.IncidentMode;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunIncidentModeTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ActivateAsync_ActivatesIncidentModeSuccessfully()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -34,7 +36,8 @@ public sealed class PackRunIncidentModeTests
Assert.NotNull(result.Status.ExpiresAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ActivateAsync_WithoutDuration_CreatesIndefiniteIncidentMode()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -57,7 +60,8 @@ public sealed class PackRunIncidentModeTests
Assert.Null(result.Status.ExpiresAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ActivateAsync_EmitsTimelineEvent()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -88,7 +92,8 @@ public sealed class PackRunIncidentModeTests
Assert.Equal(PackRunIncidentEventTypes.IncidentModeActivated, evt.EventType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task DeactivateAsync_DeactivatesIncidentMode()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -118,7 +123,8 @@ public sealed class PackRunIncidentModeTests
Assert.False(status.Active);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetStatusAsync_ReturnsInactiveForUnknownRun()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -132,7 +138,8 @@ public sealed class PackRunIncidentModeTests
Assert.Equal(IncidentEscalationLevel.None, status.Level);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task GetStatusAsync_AutoDeactivatesExpiredIncidentMode()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -161,7 +168,8 @@ public sealed class PackRunIncidentModeTests
Assert.False(status.Active);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleSloBreachAsync_ActivatesIncidentModeFromBreach()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -190,7 +198,8 @@ public sealed class PackRunIncidentModeTests
Assert.Contains("error_rate_5m", result.Status.ActivationReason!);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleSloBreachAsync_MapsSeverityToLevel()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -228,7 +237,8 @@ public sealed class PackRunIncidentModeTests
}
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task HandleSloBreachAsync_ReturnsErrorForMissingResourceId()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -254,7 +264,8 @@ public sealed class PackRunIncidentModeTests
Assert.Contains("No resource ID", result.Error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EscalateAsync_IncreasesEscalationLevel()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -286,7 +297,8 @@ public sealed class PackRunIncidentModeTests
Assert.Contains("Escalated", result.Status.ActivationReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EscalateAsync_FailsWhenNotInIncidentMode()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -304,7 +316,8 @@ public sealed class PackRunIncidentModeTests
Assert.Contains("not active", result.Error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EscalateAsync_FailsWhenNewLevelIsLowerOrEqual()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -333,7 +346,8 @@ public sealed class PackRunIncidentModeTests
Assert.Contains("Cannot escalate", result.Error);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GetSettingsForLevel_ReturnsCorrectSettings()
{
var store = new InMemoryPackRunIncidentModeStore();
@@ -356,7 +370,8 @@ public sealed class PackRunIncidentModeTests
Assert.Equal(365, criticalSettings.RetentionPolicy.LogRetentionDays);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunIncidentModeStatus_Inactive_ReturnsDefaultValues()
{
var inactive = PackRunIncidentModeStatus.Inactive();
@@ -371,7 +386,8 @@ public sealed class PackRunIncidentModeTests
Assert.False(inactive.DebugCaptureSettings.CaptureActive);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IncidentRetentionPolicy_Extended_HasLongerRetention()
{
var defaultPolicy = IncidentRetentionPolicy.Default();
@@ -382,7 +398,8 @@ public sealed class PackRunIncidentModeTests
Assert.True(extendedPolicy.ArtifactRetentionDays > defaultPolicy.ArtifactRetentionDays);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void IncidentTelemetrySettings_Enhanced_HasHigherSampling()
{
var defaultSettings = IncidentTelemetrySettings.Default();

View File

@@ -3,11 +3,13 @@ using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using System.Text.Json.Nodes;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunProcessorTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ProcessNewRunAsync_PersistsApprovalsAndPublishesNotifications()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -28,7 +30,8 @@ public sealed class PackRunProcessorTests
Assert.Empty(publisher.Policies);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ProcessNewRunAsync_NoApprovals_ResumesImmediately()
{
var manifest = TestManifests.Load(TestManifests.Output);

View File

@@ -11,7 +11,8 @@ namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunProvenanceWriterTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Filesystem_writer_emits_manifest()
{
var (context, state) = CreateRunState();
@@ -27,6 +28,7 @@ public sealed class PackRunProvenanceWriterTests
Assert.True(File.Exists(path));
using var document = JsonDocument.Parse(await File.ReadAllTextAsync(path, ct));
using StellaOps.TestKit;
var root = document.RootElement;
Assert.Equal("run-test", root.GetProperty("runId").GetString());
Assert.Equal("tenant-alpha", root.GetProperty("tenantId").GetString());

View File

@@ -3,11 +3,13 @@ using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Execution.Simulation;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunSimulationEngineTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_IdentifiesGateStatuses()
{
var manifest = TestManifests.Load(TestManifests.PolicyGate);
@@ -24,7 +26,8 @@ public sealed class PackRunSimulationEngineTests
Assert.Equal(PackRunSimulationStatus.Pending, run.Status);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_MarksDisabledStepsAndOutputs()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -47,7 +50,8 @@ public sealed class PackRunSimulationEngineTests
Assert.Equal(PackRunExecutionGraph.DefaultFailurePolicy.BackoffSeconds, result.FailurePolicy.BackoffSeconds);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_ProjectsOutputsAndRuntimeFlags()
{
var manifest = TestManifests.Load(TestManifests.Output);
@@ -73,7 +77,8 @@ public sealed class PackRunSimulationEngineTests
});
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_LoopStep_SetsWillIterateStatus()
{
var manifest = TestManifests.Load(TestManifests.Loop);
@@ -99,7 +104,8 @@ public sealed class PackRunSimulationEngineTests
Assert.Equal("collect", loopStep.LoopInfo.AggregationMode);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_ConditionalStep_SetsWillBranchStatus()
{
var manifest = TestManifests.Load(TestManifests.Conditional);
@@ -123,7 +129,8 @@ public sealed class PackRunSimulationEngineTests
Assert.True(conditionalStep.ConditionalInfo.OutputUnion);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Simulate_PolicyGateStep_HasPolicyInfo()
{
var manifest = TestManifests.Load(TestManifests.PolicyGate);

View File

@@ -3,11 +3,13 @@ using StellaOps.TaskRunner.Core.Execution.Simulation;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TaskRunner.Core.TaskPacks;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunStateFactoryTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CreateInitialState_AssignsGateReasons()
{
var manifest = TestManifests.Load(TestManifests.Sample);

View File

@@ -1,13 +1,15 @@
using StellaOps.TaskRunner.Core.Execution;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class PackRunStepStateMachineTests
{
private static readonly TaskPackPlanFailurePolicy RetryTwicePolicy = new(MaxAttempts: 3, BackoffSeconds: 5, ContinueOnError: false);
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Start_FromPending_SetsRunning()
{
var state = PackRunStepStateMachine.Create();
@@ -17,7 +19,8 @@ public sealed class PackRunStepStateMachineTests
Assert.Equal(0, started.Attempts);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CompleteSuccess_IncrementsAttempts()
{
var state = PackRunStepStateMachine.Create();
@@ -29,7 +32,8 @@ public sealed class PackRunStepStateMachineTests
Assert.Null(completed.NextAttemptAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void RegisterFailure_SchedulesRetryUntilMaxAttempts()
{
var state = PackRunStepStateMachine.Create();
@@ -54,7 +58,8 @@ public sealed class PackRunStepStateMachineTests
Assert.Null(terminalFailure.State.NextAttemptAt);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Skip_FromPending_SetsSkipped()
{
var state = PackRunStepStateMachine.Create();

View File

@@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.TaskRunner.Core.Events;
using Xunit;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
/// <summary>
@@ -18,7 +19,8 @@ public sealed class PackRunTimelineEventTests
#region Domain Model Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithRequiredFields_GeneratesValidEvent()
{
// Arrange
@@ -45,7 +47,8 @@ public sealed class PackRunTimelineEventTests
Assert.Null(evt.EventSeq);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithPayload_ComputesHashAndNormalizes()
{
// Arrange
@@ -69,7 +72,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(64 + 7, evt.PayloadHash.Length); // sha256: prefix + 64 hex chars
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithStepId_SetsStepId()
{
// Act
@@ -86,7 +90,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(TestStepId, evt.StepId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Create_WithEvidencePointer_SetsPointer()
{
// Arrange
@@ -108,7 +113,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("sha256:def456", evt.EvidencePointer.BundleDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void WithReceivedAt_CreatesCopyWithTimestamp()
{
// Arrange
@@ -131,7 +137,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(evt.EventId, updated.EventId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void WithSequence_CreatesCopyWithSequence()
{
// Arrange
@@ -151,7 +158,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(42, updated.EventSeq);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void ToJson_SerializesEvent()
{
// Arrange
@@ -174,7 +182,8 @@ public sealed class PackRunTimelineEventTests
Assert.Contains(TestStepId, json);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void FromJson_DeserializesEvent()
{
// Arrange
@@ -200,7 +209,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(original.StepId, deserialized.StepId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void GenerateIdempotencyKey_ReturnsConsistentKey()
{
// Arrange
@@ -226,7 +236,8 @@ public sealed class PackRunTimelineEventTests
#region Event Types Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunEventTypes_HasExpectedValues()
{
Assert.Equal("pack.started", PackRunEventTypes.PackStarted);
@@ -237,7 +248,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("pack.step.failed", PackRunEventTypes.StepFailed);
}
[Theory]
[Trait("Category", TestCategories.Unit)]
[Theory]
[InlineData("pack.started", true)]
[InlineData("pack.step.completed", true)]
[InlineData("scan.completed", false)]
@@ -251,7 +263,8 @@ public sealed class PackRunTimelineEventTests
#region Evidence Pointer Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EvidencePointer_Bundle_CreatesCorrectType()
{
var bundleId = Guid.NewGuid();
@@ -262,7 +275,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("sha256:abc", pointer.BundleDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EvidencePointer_Attestation_CreatesCorrectType()
{
var pointer = PackRunEvidencePointer.Attestation("subject:uri", "sha256:abc");
@@ -272,7 +286,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("sha256:abc", pointer.AttestationDigest);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EvidencePointer_Manifest_CreatesCorrectType()
{
var pointer = PackRunEvidencePointer.Manifest("https://example.com/manifest", "/locker/path");
@@ -286,7 +301,8 @@ public sealed class PackRunTimelineEventTests
#region In-Memory Sink Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_WriteAsync_StoresEvent()
{
// Arrange
@@ -309,7 +325,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(1, sink.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_WriteAsync_Deduplicates()
{
// Arrange
@@ -333,7 +350,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(1, sink.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_AssignsMonotonicSequence()
{
// Arrange
@@ -365,7 +383,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(2, result2.Sequence);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_WriteBatchAsync_StoresMultiple()
{
// Arrange
@@ -389,7 +408,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(3, sink.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_GetEventsForRun_FiltersCorrectly()
{
// Arrange
@@ -423,7 +443,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("run-2", run2Events[0].RunId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task InMemorySink_Clear_RemovesAll()
{
// Arrange
@@ -447,7 +468,8 @@ public sealed class PackRunTimelineEventTests
#region Emitter Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitPackStartedAsync_CreatesEvent()
{
// Arrange
@@ -474,7 +496,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(1, sink.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitPackCompletedAsync_CreatesEvent()
{
// Arrange
@@ -497,7 +520,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(PackRunEventTypes.PackCompleted, result.Event.EventType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitPackFailedAsync_CreatesEventWithError()
{
// Arrange
@@ -523,7 +547,8 @@ public sealed class PackRunTimelineEventTests
Assert.Contains("failureReason", result.Event.Attributes!.Keys);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitStepStartedAsync_IncludesAttempt()
{
// Arrange
@@ -550,7 +575,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("2", result.Event.Attributes!["attempt"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitStepCompletedAsync_IncludesDuration()
{
// Arrange
@@ -577,7 +603,8 @@ public sealed class PackRunTimelineEventTests
Assert.Contains("durationMs", result.Event.Attributes!.Keys);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitStepFailedAsync_IncludesError()
{
// Arrange
@@ -605,7 +632,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal("Connection timeout", result.Event.Attributes!["error"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitBatchAsync_OrdersEventsDeterministically()
{
// Arrange
@@ -637,7 +665,8 @@ public sealed class PackRunTimelineEventTests
Assert.Equal(PackRunEventTypes.StepStarted, stored[2].EventType);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Emitter_EmitBatchAsync_HandlesDuplicates()
{
// Arrange
@@ -673,7 +702,8 @@ public sealed class PackRunTimelineEventTests
#region Null Sink Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task NullSink_WriteAsync_ReturnsSuccess()
{
// Arrange

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Options;
using StellaOps.TaskRunner.Core.AirGap;
using StellaOps.TaskRunner.Core.TaskPacks;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class SealedInstallEnforcerTests
@@ -26,7 +27,8 @@ public sealed class SealedInstallEnforcerTests
};
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenPackDoesNotRequireSealedInstall_ReturnsAllowed()
{
var statusProvider = new MockAirGapStatusProvider(SealedModeStatus.Unsealed());
@@ -44,7 +46,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("Pack does not require sealed install", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenEnforcementDisabled_ReturnsAllowed()
{
var statusProvider = new MockAirGapStatusProvider(SealedModeStatus.Unsealed());
@@ -62,7 +65,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("Enforcement disabled", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenSealedRequiredButEnvironmentNotSealed_ReturnsDenied()
{
var statusProvider = new MockAirGapStatusProvider(SealedModeStatus.Unsealed());
@@ -83,7 +87,8 @@ public sealed class SealedInstallEnforcerTests
Assert.False(result.Violation.ActualSealed);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenSealedRequiredAndEnvironmentSealed_ReturnsAllowed()
{
var status = new SealedModeStatus(
@@ -118,7 +123,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("Sealed install requirements satisfied", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenBundleVersionBelowMinimum_ReturnsDenied()
{
var status = new SealedModeStatus(
@@ -159,7 +165,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("min_bundle_version", result.RequirementViolations[0].Requirement);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenAdvisoryTooStale_ReturnsDenied()
{
var status = new SealedModeStatus(
@@ -205,7 +212,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("max_advisory_staleness_hours", result.RequirementViolations[0].Requirement);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenTimeAnchorMissing_ReturnsDenied()
{
var status = new SealedModeStatus(
@@ -246,7 +254,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("require_time_anchor", result.RequirementViolations[0].Requirement);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenTimeAnchorInvalid_ReturnsDenied()
{
var status = new SealedModeStatus(
@@ -286,7 +295,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Contains(result.RequirementViolations, v => v.Requirement == "require_time_anchor");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EnforceAsync_WhenStatusProviderFails_ReturnsDenied()
{
var statusProvider = new FailingAirGapStatusProvider();
@@ -305,7 +315,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Contains("Failed to verify", result.Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SealedModeStatus_Unsealed_ReturnsCorrectDefaults()
{
var status = SealedModeStatus.Unsealed();
@@ -316,7 +327,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Null(status.BundleVersion);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SealedModeStatus_Unavailable_ReturnsCorrectDefaults()
{
var status = SealedModeStatus.Unavailable();
@@ -325,7 +337,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Equal("unavailable", status.Mode);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void SealedRequirements_Default_HasExpectedValues()
{
var defaults = SealedRequirements.Default;
@@ -337,7 +350,8 @@ public sealed class SealedInstallEnforcerTests
Assert.True(defaults.RequireSignatureVerification);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EnforcementResult_CreateAllowed_SetsProperties()
{
var result = SealedInstallEnforcementResult.CreateAllowed("Test message");
@@ -349,7 +363,8 @@ public sealed class SealedInstallEnforcerTests
Assert.Null(result.RequirementViolations);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void EnforcementResult_CreateDenied_SetsProperties()
{
var violation = new SealedInstallViolation("pack-1", "1.0.0", true, false, "Seal the environment");

View File

@@ -4,11 +4,13 @@ using System.Text.Json.Nodes;
using StellaOps.AirGap.Policy;
using StellaOps.TaskRunner.Core.Planning;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
public sealed class TaskPackPlannerTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithSequentialSteps_ComputesDeterministicHash()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -39,7 +41,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal(plan.Hash, resultB.Plan!.Hash);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PlanHash_IsPrefixedSha256Digest()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -54,7 +57,8 @@ public sealed class TaskPackPlannerTests
Assert.True(hex.All(c => Uri.IsHexDigit(c)), "Hash contains non-hex characters.");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WhenConditionEvaluatesFalse_DisablesStep()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -70,7 +74,8 @@ public sealed class TaskPackPlannerTests
Assert.False(result.Plan!.Steps[2].Enabled);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithStepReferences_MarksParametersAsRuntime()
{
var manifest = TestManifests.Load(TestManifests.StepReference);
@@ -85,7 +90,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal("steps.prepare.outputs.summary", referenceParameters["sourceSummary"].Expression);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithMapStep_ExpandsIterations()
{
var manifest = TestManifests.Load(TestManifests.Map);
@@ -106,7 +112,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal("alpha", mapStep.Children![0].Parameters!["item"].Value!.GetValue<string>());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CollectApprovalRequirements_GroupsGates()
{
var manifest = TestManifests.Load(TestManifests.Sample);
@@ -124,7 +131,8 @@ public sealed class TaskPackPlannerTests
Assert.Contains(notifications, hint => hint.Type == "approval-request" && hint.StepId == plan.Steps[1].Id);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithSecretReference_RecordsSecretMetadata()
{
var manifest = TestManifests.Load(TestManifests.Secret);
@@ -140,7 +148,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal("secrets.apiKey", param.Expression);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithOutputs_ProjectsResolvedValues()
{
var manifest = TestManifests.Load(TestManifests.Output);
@@ -162,7 +171,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal("steps.generate.outputs.evidence", evidence.Expression.Expression);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WithFailurePolicy_PopulatesPlanFailure()
{
var manifest = TestManifests.Load(TestManifests.FailurePolicy);
@@ -177,7 +187,8 @@ public sealed class TaskPackPlannerTests
Assert.False(plan.FailurePolicy.ContinueOnError);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PolicyGateHints_IncludeRuntimeMetadata()
{
var manifest = TestManifests.Load(TestManifests.PolicyGate);
@@ -196,7 +207,8 @@ public sealed class TaskPackPlannerTests
Assert.Equal("steps.prepare.outputs.evidence", evidence.Expression);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_SealedMode_BlocksUndeclaredEgress()
{
var manifest = TestManifests.Load(TestManifests.EgressBlocked);
@@ -212,7 +224,8 @@ public sealed class TaskPackPlannerTests
Assert.Contains(result.Errors, error => error.Message.Contains("example.com", StringComparison.OrdinalIgnoreCase));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_WhenRequiredInputMissing_ReturnsError()
{
var manifest = TestManifests.Load(TestManifests.RequiredInput);
@@ -223,7 +236,8 @@ public sealed class TaskPackPlannerTests
Assert.NotEmpty(result.Errors);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_SealedMode_AllowsDeclaredEgress()
{
var manifest = TestManifests.Load(TestManifests.EgressAllowed);
@@ -240,7 +254,8 @@ public sealed class TaskPackPlannerTests
Assert.True(result.Success);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void Plan_SealedMode_RuntimeUrlWithoutDeclaration_ReturnsError()
{
var manifest = TestManifests.Load(TestManifests.EgressRuntime);

View File

@@ -8,7 +8,8 @@ namespace StellaOps.TaskRunner.Tests;
public sealed class TaskRunnerClientTests
{
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StreamingLogReader_ParsesNdjsonLines()
{
var ct = TestContext.Current.CancellationToken;
@@ -27,7 +28,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal("Starting", entries[0].Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StreamingLogReader_SkipsEmptyLines()
{
var ct = TestContext.Current.CancellationToken;
@@ -43,7 +45,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal(2, entries.Count);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StreamingLogReader_SkipsMalformedLines()
{
var ct = TestContext.Current.CancellationToken;
@@ -54,6 +57,7 @@ public sealed class TaskRunnerClientTests
""";
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(ndjson));
using StellaOps.TestKit;
var entries = await StreamingLogReader.CollectAsync(stream, ct);
Assert.Equal(2, entries.Count);
@@ -61,7 +65,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal("AlsoValid", entries[1].Message);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StreamingLogReader_FilterByLevel_FiltersCorrectly()
{
var ct = TestContext.Current.CancellationToken;
@@ -84,7 +89,8 @@ public sealed class TaskRunnerClientTests
Assert.DoesNotContain(filtered, e => e.Level == "info");
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task StreamingLogReader_GroupByStep_GroupsCorrectly()
{
var ct = TestContext.Current.CancellationToken;
@@ -104,7 +110,8 @@ public sealed class TaskRunnerClientTests
Assert.Single(groups["(global)"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Paginator_IteratesAllPages()
{
var ct = TestContext.Current.CancellationToken;
@@ -129,7 +136,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal(allItems, collected);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Paginator_GetPage_ReturnsCorrectPage()
{
var ct = TestContext.Current.CancellationToken;
@@ -151,7 +159,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal(11, page2.Items[0]); // Items 11-20
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task PaginatorExtensions_TakeAsync_TakesCorrectNumber()
{
var ct = TestContext.Current.CancellationToken;
@@ -167,7 +176,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal(new[] { 1, 2, 3, 4, 5 }, taken);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task PaginatorExtensions_SkipAsync_SkipsCorrectNumber()
{
var ct = TestContext.Current.CancellationToken;
@@ -183,7 +193,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal(new[] { 6, 7, 8, 9, 10 }, skipped);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunLifecycleHelper_TerminalStatuses_IncludesExpectedStatuses()
{
Assert.Contains("completed", PackRunLifecycleHelper.TerminalStatuses);
@@ -194,7 +205,8 @@ public sealed class TaskRunnerClientTests
Assert.DoesNotContain("pending", PackRunLifecycleHelper.TerminalStatuses);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunModels_CreatePackRunRequest_SerializesCorrectly()
{
var request = new CreatePackRunRequest(
@@ -210,7 +222,8 @@ public sealed class TaskRunnerClientTests
Assert.Equal("value", request.Inputs["key"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void PackRunModels_SimulatedStep_HasCorrectProperties()
{
var loopInfo = new LoopInfo("{{ inputs.items }}", "item", 100);

View File

@@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.TaskRunner.Core.Tenancy;
using StellaOps.TaskRunner.Infrastructure.Tenancy;
using StellaOps.TestKit;
namespace StellaOps.TaskRunner.Tests;
/// <summary>
@@ -12,7 +13,8 @@ public sealed class TenantEnforcementTests
{
#region TenantContext Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_RequiresTenantId()
{
Assert.ThrowsAny<ArgumentException>(() =>
@@ -25,7 +27,8 @@ public sealed class TenantEnforcementTests
new TenantContext(" ", "project-1"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_RequiresProjectId()
{
Assert.ThrowsAny<ArgumentException>(() =>
@@ -38,7 +41,8 @@ public sealed class TenantEnforcementTests
new TenantContext("tenant-1", " "));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_TrimsIds()
{
var context = new TenantContext(" tenant-1 ", " project-1 ");
@@ -47,7 +51,8 @@ public sealed class TenantEnforcementTests
Assert.Equal("project-1", context.ProjectId);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_GeneratesStoragePrefix()
{
var context = new TenantContext("Tenant-1", "Project-1");
@@ -55,7 +60,8 @@ public sealed class TenantEnforcementTests
Assert.Equal("tenant-1/project-1", context.StoragePrefix);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_GeneratesFlatPrefix()
{
var context = new TenantContext("Tenant-1", "Project-1");
@@ -63,7 +69,8 @@ public sealed class TenantEnforcementTests
Assert.Equal("tenant-1_project-1", context.FlatPrefix);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_GeneratesLoggingScope()
{
var context = new TenantContext("tenant-1", "project-1");
@@ -73,7 +80,8 @@ public sealed class TenantEnforcementTests
Assert.Equal("project-1", scope["ProjectId"]);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void TenantContext_DefaultRestrictionsAreNone()
{
var context = new TenantContext("tenant-1", "project-1");
@@ -88,7 +96,8 @@ public sealed class TenantEnforcementTests
#region StoragePathResolver Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StoragePathResolver_HierarchicalPaths()
{
var options = new TenantStoragePathOptions
@@ -113,7 +122,8 @@ public sealed class TenantEnforcementTests
Assert.Contains("tenant-1", logsPath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StoragePathResolver_FlatPaths()
{
var options = new TenantStoragePathOptions
@@ -130,7 +140,8 @@ public sealed class TenantEnforcementTests
Assert.Contains("tenant-1_project-1_run-123", statePath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StoragePathResolver_HashedPaths()
{
var options = new TenantStoragePathOptions
@@ -148,7 +159,8 @@ public sealed class TenantEnforcementTests
Assert.Contains("project-1", basePath);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public void StoragePathResolver_ValidatesPathOwnership()
{
var options = new TenantStoragePathOptions
@@ -173,7 +185,8 @@ public sealed class TenantEnforcementTests
#region EgressPolicy Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_AllowsByDefault()
{
var options = new TenantEgressPolicyOptions { AllowByDefault = true };
@@ -185,7 +198,8 @@ public sealed class TenantEnforcementTests
Assert.True(result.IsAllowed);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_BlocksGlobalBlocklist()
{
var options = new TenantEgressPolicyOptions
@@ -202,7 +216,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(EgressBlockReason.GlobalPolicy, result.BlockReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_BlocksSuspendedTenants()
{
var options = new TenantEgressPolicyOptions { AllowByDefault = true };
@@ -218,7 +233,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(EgressBlockReason.TenantSuspended, result.BlockReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_BlocksRestrictedTenants()
{
var options = new TenantEgressPolicyOptions { AllowByDefault = true };
@@ -234,7 +250,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(EgressBlockReason.TenantRestriction, result.BlockReason);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_AllowsRestrictedTenantAllowlist()
{
var options = new TenantEgressPolicyOptions { AllowByDefault = true };
@@ -255,7 +272,8 @@ public sealed class TenantEnforcementTests
Assert.False(blockedResult.IsAllowed);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_SupportsWildcardDomains()
{
var options = new TenantEgressPolicyOptions
@@ -271,7 +289,8 @@ public sealed class TenantEnforcementTests
Assert.False(result.IsAllowed);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task EgressPolicy_RecordsAttempts()
{
var auditLog = new InMemoryEgressAuditLog();
@@ -298,7 +317,8 @@ public sealed class TenantEnforcementTests
#region TenantEnforcer Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_RequiresTenantId()
{
var enforcer = CreateTenantEnforcer();
@@ -310,7 +330,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(TenantEnforcementFailureKind.MissingTenantId, result.FailureKind);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_RequiresProjectId()
{
var options = new TenancyEnforcementOptions { RequireProjectId = true };
@@ -323,7 +344,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(TenantEnforcementFailureKind.MissingProjectId, result.FailureKind);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_BlocksSuspendedTenants()
{
var tenantProvider = new InMemoryTenantContextProvider();
@@ -343,7 +365,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(TenantEnforcementFailureKind.TenantSuspended, result.FailureKind);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_BlocksReadOnlyTenants()
{
var tenantProvider = new InMemoryTenantContextProvider();
@@ -362,7 +385,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(TenantEnforcementFailureKind.TenantReadOnly, result.FailureKind);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_EnforcesConcurrentRunLimit()
{
var tenantProvider = new InMemoryTenantContextProvider();
@@ -385,7 +409,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(TenantEnforcementFailureKind.MaxConcurrentRunsReached, result.FailureKind);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_AllowsWithinConcurrentLimit()
{
var tenantProvider = new InMemoryTenantContextProvider();
@@ -407,7 +432,8 @@ public sealed class TenantEnforcementTests
Assert.NotNull(result.Tenant);
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_TracksRunStartCompletion()
{
var runTracker = new InMemoryConcurrentRunTracker();
@@ -427,7 +453,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(0, await enforcer.GetConcurrentRunCountAsync(tenant));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_CreatesExecutionContext()
{
var tenantProvider = new InMemoryTenantContextProvider();
@@ -446,7 +473,8 @@ public sealed class TenantEnforcementTests
Assert.Contains("tenant-1", context.LoggingScope["TenantId"].ToString());
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task TenantEnforcer_ThrowsOnInvalidRequest()
{
var enforcer = CreateTenantEnforcer();
@@ -460,7 +488,8 @@ public sealed class TenantEnforcementTests
#region ConcurrentRunTracker Tests
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ConcurrentRunTracker_TracksMultipleTenants()
{
var tracker = new InMemoryConcurrentRunTracker();
@@ -474,7 +503,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(0, await tracker.GetCountAsync("tenant-3"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ConcurrentRunTracker_PreventsDoubleIncrement()
{
var tracker = new InMemoryConcurrentRunTracker();
@@ -485,7 +515,8 @@ public sealed class TenantEnforcementTests
Assert.Equal(1, await tracker.GetCountAsync("tenant-1"));
}
[Fact]
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task ConcurrentRunTracker_HandlesNonExistentDecrement()
{
var tracker = new InMemoryConcurrentRunTracker();