using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; using StellaOps.Notifier.Worker.Options; using StellaOps.Notifier.Worker.Processing; using Xunit; namespace StellaOps.Notifier.Tests; public class HttpEgressSloSinkTests { [Fact] public async Task PublishAsync_NoWebhook_DoesNothing() { var handler = new StubHandler(); var sink = CreateSink(handler, new EgressSloOptions { Webhook = null }); await sink.PublishAsync(BuildContext(), CancellationToken.None); Assert.Equal(0, handler.SendCount); } [Fact] public async Task PublishAsync_SendsWebhookWithPayload() { var handler = new StubHandler(); var sink = CreateSink(handler, new EgressSloOptions { Webhook = "https://example.test/slo", TimeoutSeconds = 5 }); await sink.PublishAsync(BuildContext(), CancellationToken.None); Assert.Equal(1, handler.SendCount); var request = handler.LastRequest!; Assert.Equal(HttpMethod.Post, request.Method); Assert.Equal("https://example.test/slo", request.RequestUri!.ToString()); } private static HttpEgressSloSink CreateSink(HttpMessageHandler handler, EgressSloOptions options) { var factory = new StubHttpClientFactory(handler); return new HttpEgressSloSink(factory, Options.Create(options), NullLogger.Instance); } private static EgressSloContext BuildContext() { var evt = Notify.Models.NotifyEvent.Create( Guid.NewGuid(), kind: "policy.violation", tenant: "tenant-a", ts: DateTimeOffset.UtcNow, payload: new System.Text.Json.Nodes.JsonObject(), actor: "tester", version: "1"); var ctx = EgressSloContext.FromNotifyEvent(evt); ctx.AddDelivery("Slack", "tmpl", evt.Kind); return ctx; } private sealed class StubHandler : HttpMessageHandler { public int SendCount { get; private set; } public HttpRequestMessage? LastRequest { get; private set; } protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { SendCount++; LastRequest = request; return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); } } private sealed class StubHttpClientFactory : IHttpClientFactory { private readonly HttpMessageHandler _handler; public StubHttpClientFactory(HttpMessageHandler handler) { _handler = handler; } public HttpClient CreateClient(string name) { return new HttpClient(_handler, disposeHandler: false); } } }