< Summary - Kestrun — Combined Coverage

Information
Class: Kestrun.Sse.InMemorySseBroadcaster
Assembly: Kestrun
File(s): /home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Sse/InMemorySseBroadcaster.cs
Tag: Kestrun/Kestrun@ca54e35c77799b76774b3805b6f075cdbc0c5fbe
Line coverage
91%
Covered lines: 32
Uncovered lines: 3
Coverable lines: 35
Total lines: 89
Line coverage: 91.4%
Branch coverage
88%
Covered branches: 16
Total branches: 18
Branch coverage: 88.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 01/12/2026 - 18:03:06 Line coverage: 0% (0/35) Branch coverage: 0% (0/18) Total lines: 89 Tag: Kestrun/Kestrun@956332ccc921363590dccd99d5707fb20b50966b01/16/2026 - 03:52:31 Line coverage: 91.4% (32/35) Branch coverage: 88.8% (16/18) Total lines: 89 Tag: Kestrun/Kestrun@0077556dd757d1b7434cf700cd5c7be05cea3514 01/12/2026 - 18:03:06 Line coverage: 0% (0/35) Branch coverage: 0% (0/18) Total lines: 89 Tag: Kestrun/Kestrun@956332ccc921363590dccd99d5707fb20b50966b01/16/2026 - 03:52:31 Line coverage: 91.4% (32/35) Branch coverage: 88.8% (16/18) Total lines: 89 Tag: Kestrun/Kestrun@0077556dd757d1b7434cf700cd5c7be05cea3514

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_ConnectedCount()100%11100%
Subscribe(...)66.66%6680%
BroadcastAsync(...)100%1010100%
RemoveClient(...)100%22100%

File(s)

/home/runner/work/Kestrun/Kestrun/src/CSharp/Kestrun/Sse/InMemorySseBroadcaster.cs

#LineLine coverage
 1using System.Collections.Concurrent;
 2using System.Threading.Channels;
 3
 4namespace Kestrun.Sse;
 5
 6/// <summary>
 7/// In-memory implementation of <see cref="ISseBroadcaster"/>.
 8/// Tracks connected clients and broadcasts formatted SSE payloads via per-client channels.
 9/// </summary>
 610public sealed class InMemorySseBroadcaster(Serilog.ILogger logger) : ISseBroadcaster
 11{
 612    private readonly ConcurrentDictionary<string, Channel<string>> _clients = new();
 613    private readonly Serilog.ILogger _logger = logger;
 14
 15    /// <inheritdoc />
 1516    public int ConnectedCount => _clients.Count;
 17
 18    /// <inheritdoc />
 19    public SseClientSubscription Subscribe(CancellationToken cancellationToken)
 20    {
 721        var clientId = Guid.NewGuid().ToString("n");
 22
 723        var channel = Channel.CreateBounded<string>(new BoundedChannelOptions(capacity: 256)
 724        {
 725            SingleReader = true,
 726            SingleWriter = false,
 727            FullMode = BoundedChannelFullMode.DropOldest
 728        });
 29
 730        if (!_clients.TryAdd(clientId, channel))
 31        {
 32            // Extremely unlikely; retry once with a different id.
 033            clientId = Guid.NewGuid().ToString("n");
 034            if (!_clients.TryAdd(clientId, channel))
 35            {
 036                throw new InvalidOperationException("Failed to register SSE client.");
 37            }
 38        }
 39
 740        if (cancellationToken.CanBeCanceled)
 41        {
 242            _ = cancellationToken.Register(() => RemoveClient(clientId));
 43        }
 44
 745        _logger.Debug("SSE client subscribed: {ClientId} (total={Count})", clientId, ConnectedCount);
 746        return new SseClientSubscription(clientId, channel.Reader);
 47    }
 48
 49    /// <inheritdoc />
 50    public ValueTask BroadcastAsync(string? eventName, string data, string? id = null, int? retryMs = null, Cancellation
 51    {
 452        if (_clients.IsEmpty)
 53        {
 154            return ValueTask.CompletedTask;
 55        }
 56
 357        var payload = SseEventFormatter.Format(eventName, data, id, retryMs);
 358        var failedClientIds = new List<string>();
 59
 1360        foreach (var kvp in _clients)
 61        {
 462            if (cancellationToken.IsCancellationRequested)
 63            {
 164                break;
 65            }
 66
 367            if (!kvp.Value.Writer.TryWrite(payload))
 68            {
 169                failedClientIds.Add(kvp.Key);
 70            }
 71        }
 72
 873        foreach (var clientId in failedClientIds)
 74        {
 175            RemoveClient(clientId);
 76        }
 77
 378        return ValueTask.CompletedTask;
 79    }
 80
 81    private void RemoveClient(string clientId)
 82    {
 283        if (_clients.TryRemove(clientId, out var channel))
 84        {
 285            _ = channel.Writer.TryComplete();
 286            _logger.Debug("SSE client removed: {ClientId} (total={Count})", clientId, ConnectedCount);
 87        }
 288    }
 89}