using System; using System.IO; using FluentAssertions; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Time.Testing; using StellaOps.Signals.Hosting; using StellaOps.Signals.Options; using Xunit; namespace StellaOps.Signals.Reachability.Tests; public sealed class SignalsSealedModeMonitorTests : IDisposable { private readonly string tempDir = Path.Combine(Path.GetTempPath(), $"signals-sealed-tests-{Guid.NewGuid():N}"); [Fact] public void IsCompliant_WhenEnforcementDisabled_ReturnsTrue() { var options = new SignalsOptions(); options.AirGap.SealedMode.EnforcementEnabled = false; var monitor = CreateMonitor(options); monitor.IsCompliant(out _).Should().BeTrue(); } [Fact] public void IsCompliant_WhenEvidenceMissing_ReturnsFalse() { var options = CreateEnforcedOptions(); options.AirGap.SealedMode.EvidencePath = Path.Combine(tempDir, "missing.json"); var monitor = CreateMonitor(options); monitor.IsCompliant(out var reason).Should().BeFalse(); reason.Should().Contain("not found"); } [Fact] public void IsCompliant_WhenEvidenceFresh_ReturnsTrue() { var evidencePath = CreateEvidenceFile(TimeSpan.Zero); var options = CreateEnforcedOptions(); options.AirGap.SealedMode.EvidencePath = evidencePath; var monitor = CreateMonitor(options); monitor.IsCompliant(out _).Should().BeTrue(); } [Fact] public void IsCompliant_WhenEvidenceStale_ReturnsFalse() { var evidencePath = CreateEvidenceFile(TimeSpan.FromHours(7)); var options = CreateEnforcedOptions(); options.AirGap.SealedMode.EvidencePath = evidencePath; var monitor = CreateMonitor(options); monitor.IsCompliant(out _).Should().BeFalse(); } private SignalsOptions CreateEnforcedOptions() { var options = new SignalsOptions(); options.AirGap.SealedMode.EnforcementEnabled = true; options.AirGap.SealedMode.MaxEvidenceAge = TimeSpan.FromHours(6); options.AirGap.SealedMode.CacheLifetime = TimeSpan.FromSeconds(1); return options; } private string CreateEvidenceFile(TimeSpan age) { Directory.CreateDirectory(tempDir); var path = Path.Combine(tempDir, $"{Guid.NewGuid():N}.json"); File.WriteAllText(path, "{}"); if (age > TimeSpan.Zero) { File.SetLastWriteTimeUtc(path, DateTime.UtcNow - age); } return path; } private SignalsSealedModeMonitor CreateMonitor(SignalsOptions options) { return new SignalsSealedModeMonitor( options, new FakeTimeProvider(DateTimeOffset.UtcNow), NullLogger.Instance); } public void Dispose() { if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, recursive: true); } } }