156 lines
5.8 KiB
C#
156 lines
5.8 KiB
C#
using System.Collections.Immutable;
|
|
using FluentAssertions;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using StellaOps.Evidence.Models;
|
|
using StellaOps.Replay.Engine;
|
|
using StellaOps.Replay.Models;
|
|
using StellaOps.Testing.Manifests.Models;
|
|
using Xunit;
|
|
|
|
using StellaOps.TestKit;
|
|
namespace StellaOps.Replay.Tests;
|
|
|
|
public class ReplayEngineTests
|
|
{
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task Replay_SameManifest_ProducesIdenticalVerdict()
|
|
{
|
|
var manifest = CreateManifest();
|
|
var engine = CreateEngine();
|
|
|
|
var result1 = await engine.ReplayAsync(manifest, new ReplayOptions());
|
|
var result2 = await engine.ReplayAsync(manifest, new ReplayOptions());
|
|
|
|
result1.VerdictDigest.Should().Be(result2.VerdictDigest);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public async Task Replay_DifferentManifest_ProducesDifferentVerdict()
|
|
{
|
|
var manifest1 = CreateManifest();
|
|
var manifest2 = manifest1 with
|
|
{
|
|
PolicySnapshot = manifest1.PolicySnapshot with { LatticeRulesDigest = new string('f', 64) }
|
|
};
|
|
|
|
var engine = CreateEngine();
|
|
var result1 = await engine.ReplayAsync(manifest1, new ReplayOptions());
|
|
var result2 = await engine.ReplayAsync(manifest2, new ReplayOptions());
|
|
|
|
result1.VerdictDigest.Should().NotBe(result2.VerdictDigest);
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void CheckDeterminism_IdenticalResults_ReturnsTrue()
|
|
{
|
|
var engine = CreateEngine();
|
|
var result1 = new ReplayResult { RunId = "1", VerdictDigest = "abc123", Success = true, ExecutedAt = DateTimeOffset.UtcNow };
|
|
var result2 = new ReplayResult { RunId = "1", VerdictDigest = "abc123", Success = true, ExecutedAt = DateTimeOffset.UtcNow };
|
|
|
|
var check = engine.CheckDeterminism(result1, result2);
|
|
|
|
check.IsDeterministic.Should().BeTrue();
|
|
}
|
|
|
|
[Trait("Category", TestCategories.Unit)]
|
|
[Fact]
|
|
public void CheckDeterminism_DifferentResults_ReturnsDifferences()
|
|
{
|
|
var engine = CreateEngine();
|
|
var result1 = new ReplayResult
|
|
{
|
|
RunId = "1",
|
|
VerdictJson = "{\"score\":100}",
|
|
VerdictDigest = "abc123",
|
|
Success = true,
|
|
ExecutedAt = DateTimeOffset.UtcNow
|
|
};
|
|
var result2 = new ReplayResult
|
|
{
|
|
RunId = "1",
|
|
VerdictJson = "{\"score\":99}",
|
|
VerdictDigest = "def456",
|
|
Success = true,
|
|
ExecutedAt = DateTimeOffset.UtcNow
|
|
};
|
|
|
|
var check = engine.CheckDeterminism(result1, result2);
|
|
|
|
check.IsDeterministic.Should().BeFalse();
|
|
check.Differences.Should().NotBeEmpty();
|
|
}
|
|
|
|
private static ReplayEngine CreateEngine()
|
|
{
|
|
return new ReplayEngine(
|
|
new FakeFeedLoader(),
|
|
new FakePolicyLoader(),
|
|
new FakeScannerFactory(),
|
|
NullLogger<ReplayEngine>.Instance);
|
|
}
|
|
|
|
private static RunManifest CreateManifest()
|
|
{
|
|
return new RunManifest
|
|
{
|
|
RunId = Guid.NewGuid().ToString(),
|
|
SchemaVersion = "1.0.0",
|
|
ArtifactDigests = ImmutableArray.Create(new ArtifactDigest("sha256", new string('a', 64), null, null)),
|
|
FeedSnapshot = new FeedSnapshot("nvd", "v1", new string('b', 64), DateTimeOffset.UtcNow.AddHours(-1)),
|
|
PolicySnapshot = new PolicySnapshot("1.0.0", new string('c', 64), ImmutableArray<string>.Empty),
|
|
ToolVersions = new ToolVersions("1.0.0", "1.0.0", "1.0.0", "1.0.0", ImmutableDictionary<string, string>.Empty),
|
|
CryptoProfile = new CryptoProfile("default", ImmutableArray<string>.Empty, ImmutableArray<string>.Empty),
|
|
EnvironmentProfile = new EnvironmentProfile("postgres-only", false, null, null),
|
|
CanonicalizationVersion = "1.0.0",
|
|
InitiatedAt = DateTimeOffset.UtcNow
|
|
};
|
|
}
|
|
|
|
private sealed class FakeFeedLoader : IFeedLoader
|
|
{
|
|
public Task<FeedSnapshot> LoadByDigestAsync(string digest, CancellationToken ct = default)
|
|
=> Task.FromResult(new FeedSnapshot("nvd", "v1", digest, DateTimeOffset.UtcNow.AddHours(-1)));
|
|
}
|
|
|
|
private sealed class FakePolicyLoader : IPolicyLoader
|
|
{
|
|
public Task<PolicySnapshot> LoadByDigestAsync(string digest, CancellationToken ct = default)
|
|
=> Task.FromResult(new PolicySnapshot("1.0.0", digest, ImmutableArray<string>.Empty));
|
|
}
|
|
|
|
private sealed class FakeScannerFactory : IScannerFactory
|
|
{
|
|
public IScanner Create(ScannerOptions options) => new FakeScanner(options);
|
|
}
|
|
|
|
private sealed class FakeScanner : IScanner
|
|
{
|
|
private readonly ScannerOptions _options;
|
|
public FakeScanner(ScannerOptions options) => _options = options;
|
|
|
|
public Task<ScanResult> ScanAsync(ImmutableArray<ArtifactDigest> artifacts, CancellationToken ct = default)
|
|
{
|
|
var verdict = new
|
|
{
|
|
feedVersion = _options.FeedSnapshot.Version,
|
|
policyDigest = _options.PolicySnapshot.LatticeRulesDigest
|
|
};
|
|
var evidence = new EvidenceIndex
|
|
{
|
|
IndexId = Guid.NewGuid().ToString(),
|
|
SchemaVersion = "1.0.0",
|
|
Verdict = new VerdictReference("v1", new string('d', 64), VerdictOutcome.Pass, null),
|
|
Sboms = ImmutableArray<SbomEvidence>.Empty,
|
|
Attestations = ImmutableArray<AttestationEvidence>.Empty,
|
|
ToolChain = new ToolChainEvidence("1", "1", "1", "1", "1", ImmutableDictionary<string, string>.Empty),
|
|
RunManifestDigest = new string('e', 64),
|
|
CreatedAt = DateTimeOffset.UtcNow
|
|
};
|
|
return Task.FromResult(new ScanResult(verdict, evidence, 10));
|
|
}
|
|
}
|
|
}
|