< Summary - Combined Code Coverage

Information
Class: NLightning.Infrastructure.Protocol.Services.MessageService
Assembly: NLightning.Infrastructure
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Protocol/Services/MessageService.cs
Tag: 38_17925369700
Line coverage
57%
Covered lines: 43
Uncovered lines: 32
Coverable lines: 75
Total lines: 178
Line coverage: 57.3%
Branch coverage
44%
Covered branches: 15
Total branches: 34
Branch coverage: 44.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%11100%
get_IsConnected()50%22100%
SendMessageAsync()41.67%37.291244%
ReceiveMessage(...)33.33%51.951234.78%
RaiseException(...)0%620%
Dispose()100%11100%
Dispose(...)83.33%6.04690%
Finalize()100%11100%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Protocol/Services/MessageService.cs

#LineLine coverage
 1using Microsoft.Extensions.Logging;
 2
 3namespace NLightning.Infrastructure.Protocol.Services;
 4
 5using Domain.Exceptions;
 6using Domain.Protocol.Interfaces;
 7using Domain.Protocol.Messages;
 8using Domain.Protocol.Payloads;
 9using Domain.Serialization.Interfaces;
 10using Domain.Transport;
 11using Exceptions;
 12
 13/// <summary>
 14/// Service for sending and receiving messages.
 15/// </summary>
 16/// <remarks>
 17/// This class is used to send and receive messages over a transport service.
 18/// </remarks>
 19/// <seealso cref="IMessageService" />
 20internal sealed class MessageService : IMessageService
 21{
 22    private readonly ILogger<IMessageService> _logger;
 23    private readonly IMessageSerializer _messageSerializer;
 24    private readonly ITransportService? _transportService;
 25
 26    private volatile bool _disposed;
 2027    private readonly object _disposeLock = new();
 28
 29    /// <inheritdoc />
 30    public event EventHandler<IMessage?>? OnMessageReceived;
 31
 32    public event EventHandler<Exception>? OnExceptionRaised;
 33
 34    /// <inheritdoc />
 435    public bool IsConnected => _transportService?.IsConnected ?? false;
 36
 37    /// <summary>
 38    /// Initializes a new <see cref="MessageService"/> class.
 39    /// </summary>
 40    /// <param name="logger">The logger.</param>
 41    /// <param name="messageSerializer">The message serializer.</param>
 42    /// <param name="transportService">The transport service.</param>
 2043    public MessageService(ILogger<IMessageService> logger, IMessageSerializer messageSerializer,
 2044                          ITransportService transportService)
 45    {
 2046        _logger = logger;
 2047        _messageSerializer = messageSerializer;
 2048        _transportService = transportService;
 49
 2050        _transportService.MessageReceived += ReceiveMessage;
 2051        _transportService.ExceptionRaised += RaiseException;
 2052    }
 53
 54    /// <inheritdoc />
 55    /// <exception cref="ObjectDisposedException">Thrown when the object is disposed.</exception>
 56    public async Task SendMessageAsync(IMessage message, bool throwOnException = false,
 57                                       CancellationToken cancellationToken = default)
 58    {
 859        lock (_disposeLock)
 860            if (_disposed)
 61            {
 462                var connectionException = new ConnectionException($"{nameof(MessageService)} was disposed.",
 463                                                                  new ObjectDisposedException(nameof(MessageService)));
 464                if (throwOnException)
 465                    throw connectionException;
 66
 067                RaiseException(this, connectionException);
 068                return;
 69            }
 70
 71        try
 72        {
 473            if (cancellationToken.IsCancellationRequested)
 074                return;
 75
 476            if (_transportService == null)
 77            {
 078                var connectionException = new ConnectionException($"{nameof(MessageService)} is not initialized");
 079                if (throwOnException)
 080                    throw connectionException;
 81
 082                RaiseException(this, connectionException);
 083                return;
 84            }
 85
 486            await _transportService.WriteMessageAsync(message, cancellationToken);
 487        }
 088        catch (Exception e)
 89        {
 090            var connectionException = new ConnectionException("Failed to send message", e);
 091            if (throwOnException)
 092                throw connectionException;
 93
 094            RaiseException(this, connectionException);
 095        }
 496    }
 97
 98    private void ReceiveMessage(object? _, MemoryStream stream)
 99    {
 100        try
 101        {
 4102            lock (_disposeLock)
 103            {
 4104                if (_disposed)
 0105                    return;
 106
 4107                var message = _messageSerializer.DeserializeMessageAsync(stream).GetAwaiter().GetResult();
 4108                if (message is not null)
 109                {
 4110                    OnMessageReceived?.Invoke(this, message);
 111                }
 4112            }
 4113        }
 0114        catch (MessageSerializationException mse)
 115        {
 0116            var message = mse.Message;
 0117            if (mse.InnerException is PayloadSerializationException pse)
 118            {
 0119                message = pse.InnerException is not null ? pse.InnerException.Message : pse.Message;
 120            }
 0121            else if (mse.InnerException is not null)
 122            {
 0123                message = mse.InnerException.Message;
 124            }
 125
 0126            _logger.LogError(mse, "Failed to deserialize message: {Message}", message);
 0127            SendMessageAsync(new ErrorMessage(new ErrorPayload(message))).GetAwaiter().GetResult();
 0128            RaiseException(this, mse);
 0129        }
 0130        catch (Exception e)
 131        {
 0132            _logger.LogError(e, "Failed to receive message");
 0133            RaiseException(this, e);
 0134        }
 4135    }
 136
 137    private void RaiseException(object? sender, Exception e)
 138    {
 0139        OnExceptionRaised?.Invoke(sender, new ConnectionException("Error received from transportService", e));
 0140    }
 141
 142    #region Dispose Pattern
 143
 144    /// <inheritdoc />
 145    /// <remarks>
 146    /// Disposes the TransportService.
 147    /// </remarks>
 148    public void Dispose()
 149    {
 8150        Dispose(true);
 8151        GC.SuppressFinalize(this);
 8152    }
 153
 154    private void Dispose(bool disposing)
 155    {
 14156        lock (_disposeLock)
 157        {
 14158            if (_disposed)
 0159                return;
 160
 14161            if (disposing && _transportService is not null)
 162            {
 8163                _transportService.MessageReceived -= ReceiveMessage;
 8164                _transportService.ExceptionRaised -= RaiseException;
 8165                _transportService.Dispose();
 166            }
 167
 14168            _disposed = true;
 14169        }
 14170    }
 171
 172    ~MessageService()
 173    {
 6174        Dispose(false);
 12175    }
 176
 177    #endregion
 178}