using Xunit; using System; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging.Abstractions; using StellaOps.AirGap.Policy; using StellaOps.Cli.Configuration; namespace StellaOps.Cli.Tests.Configuration; public sealed class EgressPolicyHttpMessageHandlerTests { [Fact] public async Task SendAsync_AllowsRequestWhenPolicyPermits() { var options = new EgressPolicyOptions { Mode = EgressPolicyMode.Sealed }; options.AddAllowRule("example.com"); var policy = new EgressPolicy(options); var handler = new EgressPolicyHttpMessageHandler( policy, NullLogger.Instance, component: "cli-tests", intent: "allow-test") { InnerHandler = new StubHandler() }; using var client = new HttpClient(handler, disposeHandler: true); var response = await client.GetAsync("https://example.com/resource", CancellationToken.None); Assert.Equal(HttpStatusCode.OK, response.StatusCode); } [Fact] public async Task SendAsync_ThrowsWhenPolicyBlocksRequest() { var options = new EgressPolicyOptions { Mode = EgressPolicyMode.Sealed }; var policy = new EgressPolicy(options); var handler = new EgressPolicyHttpMessageHandler( policy, NullLogger.Instance, component: "cli-tests", intent: "deny-test") { InnerHandler = new StubHandler() }; using var client = new HttpClient(handler, disposeHandler: true); var exception = await Assert.ThrowsAsync( () => client.GetAsync("https://blocked.example", CancellationToken.None)); Assert.Contains(AirGapEgressBlockedException.ErrorCode, exception.Message, StringComparison.OrdinalIgnoreCase); } private sealed class StubHandler : HttpMessageHandler { protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) => Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); } }