diff --git a/test/FritzCallMonitor.Tests/CallMonitorClientTest.cs b/test/FritzCallMonitor.Tests/CallMonitorClientTest.cs new file mode 100644 index 0000000..6c70e14 --- /dev/null +++ b/test/FritzCallMonitor.Tests/CallMonitorClientTest.cs @@ -0,0 +1,142 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using AMWD.Net.Api.Fritz.CallMonitor; +using AMWD.Net.Api.Fritz.CallMonitor.Utils; +using AMWD.Net.Api.Fritz.CallMonitor.Wrappers; +using Microsoft.Extensions.Logging; +using Moq; + +namespace FritzCallMonitor.Tests +{ + [TestClass] + public class CallMonitorClientTest + { + private const string HOST = "localhost"; + private const int PORT = 1012; + + private Mock _tcpClientMock; + private Mock _networkStreamMock; + + private Queue<(int DelaySeconds, byte[] BufferResponse)> _readAsyncResponses; + + [TestInitialize] + public void Initialize() + { + _readAsyncResponses = new Queue<(int, byte[])>(); + + _readAsyncResponses.Enqueue((0, Encoding.UTF8.GetBytes("25.08.25 20:15:30;RING;2;012345678901;9876543;SIP0;\r\n"))); + _readAsyncResponses.Enqueue((Timeout.Infinite, Array.Empty())); + } + + [TestMethod] + public void ShouldCreateInstance() + { + // Arrange & Act + using var client = new CallMonitorClient(HOST, PORT); + + // Assert + Assert.IsNotNull(client); + } + + [TestMethod] + [DataRow(null)] + [DataRow("")] + [DataRow(" ")] + public void ShouldThrowArgumentNullExceptionOnMissingHost(string host) + { + // Arrange, Act & Assert + Assert.ThrowsExactly(() => new CallMonitorClient(host, PORT)); + } + + [TestMethod] + [DataRow(0)] + [DataRow(65536)] + public void ShouldThrowArgumentOutOfRangeExceptionOnInvalidPort(int port) + { + // Arrange, Act & Assert + Assert.ThrowsExactly(() => new CallMonitorClient(HOST, port)); + } + + [TestMethod] + public void ShouldSetAndGetLogger() + { + // Arrange + var loggerMock = new Mock(); + var client = GetClient(); + + // Act + client.Logger = loggerMock.Object; + + // Assert + Assert.AreEqual(loggerMock.Object, client.Logger); + + _tcpClientMock.VerifySet(m => m.Logger = loggerMock.Object, Times.Once); + _tcpClientMock.VerifyGet(m => m.IsConnected, Times.Once); + _tcpClientMock.Verify(m => m.GetStream(), Times.Once); + + VerifyNoOtherCalls(); + } + + [TestMethod] + public void ShouldDisposeOnlyOnce() + { + // Arrange + var client = GetClient(); + + // Act + client.Dispose(); + client.Dispose(); + + // Assert + _tcpClientMock.Verify(c => c.Dispose(), Times.Once); + _tcpClientMock.Verify(m => m.GetStream(), Times.Once); + + VerifyNoOtherCalls(); + } + + private void VerifyNoOtherCalls() + { + _tcpClientMock.Verify(m => m.OnConnected, Times.Once); + _tcpClientMock.VerifyNoOtherCalls(); + } + + private CallMonitorClient GetClient() + { + _tcpClientMock = new Mock(HOST, PORT); + _networkStreamMock = new Mock(null); + + _tcpClientMock + .Setup(m => m.GetStream()) + .Returns(_networkStreamMock.Object); + + _networkStreamMock + .Setup(m => m.ReadAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) + .Returns((buffer, offset, count, token) => + { + var (delaySeconds, bufferResponse) = _readAsyncResponses.Dequeue(); + + return Task.Delay(TimeSpan.FromSeconds(delaySeconds), token).ContinueWith(t => + { + Array.Copy(bufferResponse, 0, buffer, offset, bufferResponse.Length); + return bufferResponse.Length; + }); + }); + + var client = new CallMonitorClient(HOST, PORT); + var tcpClientField = client.GetType().GetField("_tcpClient", BindingFlags.NonPublic | BindingFlags.Instance); + + ((IDisposable)tcpClientField.GetValue(client)).Dispose(); + tcpClientField.SetValue(client, _tcpClientMock.Object); + + var onConnectedMethodInfo = client.GetType().GetMethod("OnConnected", BindingFlags.NonPublic | BindingFlags.Instance); + _tcpClientMock.SetupGet(c => c.OnConnected).Returns((Func)onConnectedMethodInfo.CreateDelegate(typeof(Func), client)); + + _tcpClientMock.Object.OnConnected(_tcpClientMock.Object).Wait(); + return client; + } + } +}