This commit is contained in:
master
2026-02-04 19:59:20 +02:00
parent 557feefdc3
commit 5548cf83bf
1479 changed files with 53557 additions and 40339 deletions

View File

@@ -0,0 +1,48 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Replay.Loaders;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Replay.Tests;
public sealed class FeedSnapshotLoaderTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_InvalidDigest_ThrowsFormatException()
{
var loader = new FeedSnapshotLoader(new FeedStorageStub(null), NullLogger<FeedSnapshotLoader>.Instance);
var digest = SnapshotTestData.CreateInvalidDigest('a');
var action = () => loader.LoadByDigestAsync(digest);
await action.Should().ThrowAsync<FormatException>();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_ShortDigest_ThrowsFormatException()
{
var loader = new FeedSnapshotLoader(new FeedStorageStub(null), NullLogger<FeedSnapshotLoader>.Instance);
var digest = new string('a', 63);
var action = () => loader.LoadByDigestAsync(digest);
await action.Should().ThrowAsync<FormatException>();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_DigestMismatch_ThrowsException()
{
var snapshot = SnapshotTestData.CreateFeedSnapshot(SnapshotTestData.CreateValidDigest('b'));
var actualDigest = SnapshotTestData.ComputeDigest(snapshot);
var expectedDigest = SnapshotTestData.CreateDifferentDigest(actualDigest);
var loader = new FeedSnapshotLoader(new FeedStorageStub(snapshot), NullLogger<FeedSnapshotLoader>.Instance);
var action = () => loader.LoadByDigestAsync(expectedDigest);
await action.Should().ThrowAsync<DigestMismatchException>();
}
}

View File

@@ -0,0 +1,10 @@
namespace StellaOps.Replay.Tests;
internal sealed class FixedTimeProvider : TimeProvider
{
private readonly DateTimeOffset _now;
public FixedTimeProvider(DateTimeOffset now) => _now = now;
public override DateTimeOffset GetUtcNow() => _now;
}

View File

@@ -0,0 +1,48 @@
using FluentAssertions;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Replay.Loaders;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Replay.Tests;
public sealed class PolicySnapshotLoaderTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_InvalidDigest_ThrowsFormatException()
{
var loader = new PolicySnapshotLoader(new PolicyStorageStub(null), NullLogger<PolicySnapshotLoader>.Instance);
var digest = SnapshotTestData.CreateInvalidDigest('c');
var action = () => loader.LoadByDigestAsync(digest);
await action.Should().ThrowAsync<FormatException>();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_ShortDigest_ThrowsFormatException()
{
var loader = new PolicySnapshotLoader(new PolicyStorageStub(null), NullLogger<PolicySnapshotLoader>.Instance);
var digest = new string('c', 10);
var action = () => loader.LoadByDigestAsync(digest);
await action.Should().ThrowAsync<FormatException>();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task LoadByDigestAsync_DigestMismatch_ThrowsException()
{
var snapshot = SnapshotTestData.CreatePolicySnapshot(SnapshotTestData.CreateValidDigest('d'));
var actualDigest = SnapshotTestData.ComputeDigest(snapshot);
var expectedDigest = SnapshotTestData.CreateDifferentDigest(actualDigest);
var loader = new PolicySnapshotLoader(new PolicyStorageStub(snapshot), NullLogger<PolicySnapshotLoader>.Instance);
var action = () => loader.LoadByDigestAsync(expectedDigest);
await action.Should().ThrowAsync<DigestMismatchException>();
}
}

View File

@@ -16,13 +16,17 @@ internal static class ReplayEngineTestFixtures
new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
internal static readonly DateTimeOffset FixedFeedSnapshotAt = FixedTimestamp.AddHours(-1);
internal static ReplayEngine CreateEngine()
internal static ReplayEngine CreateEngine() =>
CreateEngine(new FixedTimeProvider(FixedTimestamp));
internal static ReplayEngine CreateEngine(TimeProvider timeProvider)
{
return new ReplayEngine(
new FakeFeedLoader(),
new FakePolicyLoader(),
new FakeScannerFactory(),
NullLogger<ReplayEngine>.Instance);
NullLogger<ReplayEngine>.Instance,
timeProvider);
}
internal static RunManifest CreateManifest()

View File

@@ -0,0 +1,63 @@
using FluentAssertions;
using StellaOps.Replay.Engine;
using StellaOps.Replay.Models;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Replay.Tests;
public partial class ReplayEngineTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CheckDeterminism_IdenticalResults_ReturnsTrue()
{
var engine = ReplayEngineTestFixtures.CreateEngine();
var result1 = new ReplayResult
{
RunId = "1",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var result2 = new ReplayResult
{
RunId = "1",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var check = engine.CheckDeterminism(result1, result2);
check.IsDeterministic.Should().BeTrue();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CheckDeterminism_DifferentResults_ReturnsDifferences()
{
var engine = ReplayEngineTestFixtures.CreateEngine();
var result1 = new ReplayResult
{
RunId = "1",
VerdictJson = "{\"score\":100}",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var result2 = new ReplayResult
{
RunId = "1",
VerdictJson = "{\"score\":99}",
VerdictDigest = "def456",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var check = engine.CheckDeterminism(result1, result2);
check.IsDeterministic.Should().BeFalse();
check.Differences.Should().NotBeEmpty();
}
}

View File

@@ -0,0 +1,23 @@
using FluentAssertions;
using StellaOps.Replay.Models;
using StellaOps.TestKit;
using Xunit;
namespace StellaOps.Replay.Tests;
public partial class ReplayEngineTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
public async Task Replay_InvalidManifest_UsesTimeProviderAsync()
{
var fixedTime = new DateTimeOffset(2026, 1, 2, 0, 0, 0, TimeSpan.Zero);
var engine = ReplayEngineTestFixtures.CreateEngine(new FixedTimeProvider(fixedTime));
var manifest = ReplayEngineTestFixtures.CreateManifest() with { RunId = "" };
var result = await engine.ReplayAsync(manifest, new ReplayOptions());
result.Success.Should().BeFalse();
result.ExecutedAt.Should().Be(fixedTime);
}
}

View File

@@ -6,7 +6,7 @@ using Xunit;
namespace StellaOps.Replay.Tests;
public class ReplayEngineTests
public partial class ReplayEngineTests
{
[Trait("Category", TestCategories.Unit)]
[Fact]
@@ -38,57 +38,4 @@ public class ReplayEngineTests
result1.VerdictDigest.Should().NotBe(result2.VerdictDigest);
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CheckDeterminism_IdenticalResults_ReturnsTrue()
{
var engine = ReplayEngineTestFixtures.CreateEngine();
var result1 = new ReplayResult
{
RunId = "1",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var result2 = new ReplayResult
{
RunId = "1",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var check = engine.CheckDeterminism(result1, result2);
check.IsDeterministic.Should().BeTrue();
}
[Trait("Category", TestCategories.Unit)]
[Fact]
public void CheckDeterminism_DifferentResults_ReturnsDifferences()
{
var engine = ReplayEngineTestFixtures.CreateEngine();
var result1 = new ReplayResult
{
RunId = "1",
VerdictJson = "{\"score\":100}",
VerdictDigest = "abc123",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var result2 = new ReplayResult
{
RunId = "1",
VerdictJson = "{\"score\":99}",
VerdictDigest = "def456",
Success = true,
ExecutedAt = ReplayEngineTestFixtures.FixedTimestamp
};
var check = engine.CheckDeterminism(result1, result2);
check.IsDeterministic.Should().BeFalse();
check.Differences.Should().NotBeEmpty();
}
}

View File

@@ -0,0 +1,24 @@
using StellaOps.Replay.Loaders;
using StellaOps.Testing.Manifests.Models;
namespace StellaOps.Replay.Tests;
internal sealed class FeedStorageStub : IFeedStorage
{
private readonly FeedSnapshot? _snapshot;
public FeedStorageStub(FeedSnapshot? snapshot) => _snapshot = snapshot;
public Task<FeedSnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default)
=> Task.FromResult(_snapshot);
}
internal sealed class PolicyStorageStub : IPolicyStorage
{
private readonly PolicySnapshot? _snapshot;
public PolicyStorageStub(PolicySnapshot? snapshot) => _snapshot = snapshot;
public Task<PolicySnapshot?> GetByDigestAsync(string digest, CancellationToken ct = default)
=> Task.FromResult(_snapshot);
}

View File

@@ -0,0 +1,36 @@
using StellaOps.Canonicalization.Json;
using StellaOps.Testing.Manifests.Models;
using System.Collections.Immutable;
using System.Security.Cryptography;
using System.Text;
namespace StellaOps.Replay.Tests;
internal static class SnapshotTestData
{
internal static readonly DateTimeOffset FixedSnapshotAt =
new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
internal static FeedSnapshot CreateFeedSnapshot(string digest) =>
new FeedSnapshot("nvd", "v1", digest, FixedSnapshotAt);
internal static PolicySnapshot CreatePolicySnapshot(string digest) =>
new PolicySnapshot("1.0.0", digest, ImmutableArray<string>.Empty);
internal static string ComputeDigest<T>(T value)
{
var json = CanonicalJsonSerializer.Serialize(value);
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(json))).ToLowerInvariant();
}
internal static string CreateValidDigest(char fill) => new string(fill, 64);
internal static string CreateInvalidDigest(char fill) => new string(fill, 63) + "g";
internal static string CreateDifferentDigest(string digest)
{
var last = digest[^1];
var replacement = last == 'a' ? 'b' : 'a';
return digest[..^1] + replacement;
}
}

View File

@@ -12,3 +12,4 @@ Source of truth: `docs-archived/implplan/2025-12-29-csproj-audit/SPRINT_20251229
| REMED-03 | DONE | Tier 0 remediation (usings sorted, deterministic test data, warnings as errors); dotnet test passed 2026-02-02. |
| REMED-04 | DONE | Async naming updates; ConfigureAwait(false) skipped in tests per xUnit1030; dotnet test passed 2026-02-02. |
| REMED-05 | DONE | File split to keep tests <= 100 lines; dotnet test passed 2026-02-02. |
| REMED-07 | DONE | 2026-02-05; replay tests split into partials, loader validation/digest mismatch coverage + failure timestamp test added; dotnet test src/__Libraries/__Tests/StellaOps.Replay.Tests/StellaOps.Replay.Tests.csproj passed (11 tests). |