using StellaOps.AirGap.Time.Models; using StellaOps.AirGap.Time.Services; using StellaOps.AirGap.Time.Stores; namespace StellaOps.AirGap.Time.Tests; public class SealedStartupValidatorTests { [Fact] public async Task FailsWhenAnchorMissing() { var validator = Build(out var statusService); var result = await validator.ValidateAsync("t1", StalenessBudget.Default, default); Assert.False(result.IsValid); Assert.Equal("time-anchor-missing", result.Reason); } [Fact] public async Task FailsWhenBreach() { var validator = Build(out var statusService); var anchor = new TimeAnchor(DateTimeOffset.UnixEpoch, "src", "fmt", "fp", "digest"); await statusService.SetAnchorAsync("t1", anchor, new StalenessBudget(10, 20)); var now = DateTimeOffset.UnixEpoch.AddSeconds(25); var status = await statusService.GetStatusAsync("t1", now); var result = status.Staleness.IsBreach; Assert.True(result); var validation = await validator.ValidateAsync("t1", new StalenessBudget(10, 20), default); Assert.False(validation.IsValid); Assert.Equal("time-anchor-stale", validation.Reason); } [Fact] public async Task SucceedsWhenFresh() { var validator = Build(out var statusService); var anchor = new TimeAnchor(DateTimeOffset.UnixEpoch, "src", "fmt", "fp", "digest"); await statusService.SetAnchorAsync("t1", anchor, new StalenessBudget(10, 20)); var validation = await validator.ValidateAsync("t1", new StalenessBudget(10, 20), default); Assert.True(validation.IsValid); } [Fact] public async Task FailsOnBudgetMismatch() { var validator = Build(out var statusService); var anchor = new TimeAnchor(DateTimeOffset.UnixEpoch, "src", "fmt", "fp", "digest"); await statusService.SetAnchorAsync("t1", anchor, new StalenessBudget(10, 20)); var validation = await validator.ValidateAsync("t1", new StalenessBudget(5, 15), default); Assert.False(validation.IsValid); Assert.Equal("time-anchor-budget-mismatch", validation.Reason); } private static SealedStartupValidator Build(out TimeStatusService statusService) { var store = new InMemoryTimeAnchorStore(); statusService = new TimeStatusService(store, new StalenessCalculator()); return new SealedStartupValidator(statusService); } }