using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using StellaOps.Notify.Engine; using StellaOps.Notify.Models; using Xunit; namespace StellaOps.Notify.Connectors.Slack.Tests; public sealed class SlackChannelHealthProviderTests { private static readonly SlackChannelHealthProvider Provider = new(); [Fact] public async Task CheckAsync_ReturnsHealthy() { var channel = CreateChannel(enabled: true, target: "#sec-ops"); var context = new ChannelHealthContext( channel.TenantId, channel, channel.Config.Target!, new DateTimeOffset(2025, 10, 20, 14, 0, 0, TimeSpan.Zero), "trace-slack-001"); var result = await Provider.CheckAsync(context, CancellationToken.None); Assert.Equal(ChannelHealthStatus.Healthy, result.Status); Assert.Equal("true", result.Metadata["slack.channel.enabled"]); Assert.Equal("true", result.Metadata["slack.validation.targetPresent"]); Assert.Equal("#sec-ops", result.Metadata["slack.channel"]); Assert.Equal(ComputeSecretHash(channel.Config.SecretRef), result.Metadata["slack.secretRef.hash"]); } [Fact] public async Task CheckAsync_ReturnsDegradedWhenDisabled() { var channel = CreateChannel(enabled: false, target: "#sec-ops"); var context = new ChannelHealthContext( channel.TenantId, channel, channel.Config.Target!, DateTimeOffset.UtcNow, "trace-slack-002"); var result = await Provider.CheckAsync(context, CancellationToken.None); Assert.Equal(ChannelHealthStatus.Degraded, result.Status); Assert.Equal("false", result.Metadata["slack.channel.enabled"]); } [Fact] public async Task CheckAsync_ReturnsUnhealthyWhenTargetMissing() { var channel = CreateChannel(enabled: true, target: null); var context = new ChannelHealthContext( channel.TenantId, channel, channel.Name, DateTimeOffset.UtcNow, "trace-slack-003"); var result = await Provider.CheckAsync(context, CancellationToken.None); Assert.Equal(ChannelHealthStatus.Unhealthy, result.Status); Assert.Equal("false", result.Metadata["slack.validation.targetPresent"]); } private static NotifyChannel CreateChannel(bool enabled, string? target) { return NotifyChannel.Create( channelId: "channel-slack-sec-ops", tenantId: "tenant-sec", name: "slack:sec-ops", type: NotifyChannelType.Slack, config: NotifyChannelConfig.Create( secretRef: "ref://notify/channels/slack/sec-ops", target: target, properties: new Dictionary { ["workspace"] = "stellaops-sec", ["botToken"] = "xoxb-123456789012-abcdefghijklmnop" }), enabled: enabled); } private static string ComputeSecretHash(string secretRef) { var bytes = System.Text.Encoding.UTF8.GetBytes(secretRef.Trim()); var hash = System.Security.Cryptography.SHA256.HashData(bytes); return Convert.ToHexString(hash.AsSpan(0, 8)).ToLowerInvariant(); } }