1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-08-11 16:32:57 +00:00

Websocket refactoring (#190)

Websocket refactoring
This commit is contained in:
Jan Korf
2024-02-24 19:21:47 +01:00
committed by GitHub
parent d91755dff5
commit d533557324
585 changed files with 114831 additions and 2198 deletions
@@ -7,7 +7,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0-preview-20211130-02"></PackageReference>
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="3.13.2"></PackageReference>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0"></PackageReference>
</ItemGroup>
@@ -1,11 +1,17 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.UnitTests.TestImplementations;
using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using NUnit.Framework.Constraints;
namespace CryptoExchange.Net.UnitTests
{
@@ -46,7 +52,7 @@ namespace CryptoExchange.Net.UnitTests
}
[TestCase]
public void SocketMessages_Should_BeProcessedInDataHandlers()
public async Task SocketMessages_Should_BeProcessedInDataHandlers()
{
// arrange
var client = new TestSocketClient(options => {
@@ -58,28 +64,31 @@ namespace CryptoExchange.Net.UnitTests
socket.DisconnectTime = DateTime.UtcNow;
var sub = new SocketConnection(new TraceLogger(), client.SubClient, socket, null);
var rstEvent = new ManualResetEvent(false);
JToken result = null;
sub.AddSubscription(SocketSubscription.CreateForIdentifier(10, "TestHandler", true, false, (messageEvent) =>
{
result = messageEvent.JsonData;
rstEvent.Set();
}));
Dictionary<string, string> result = null;
client.SubClient.ConnectSocketSub(sub);
sub.AddSubscription(new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
{
result = messageEvent.Data;
rstEvent.Set();
}));
// act
socket.InvokeMessage("{\"property\": 123}");
await socket.InvokeMessage("{\"property\": \"123\", \"topic\": \"topic\"}");
rstEvent.WaitOne(1000);
// assert
Assert.IsTrue((int)result["property"] == 123);
Assert.IsTrue(result["property"] == "123");
}
[TestCase(false)]
[TestCase(true)]
public void SocketMessages_Should_ContainOriginalDataIfEnabled(bool enabled)
public async Task SocketMessages_Should_ContainOriginalDataIfEnabled(bool enabled)
{
// arrange
var client = new TestSocketClient(options => {
var client = new TestSocketClient(options =>
{
options.ReconnectInterval = TimeSpan.Zero;
options.SubOptions.OutputOriginalData = enabled;
});
@@ -90,15 +99,16 @@ namespace CryptoExchange.Net.UnitTests
var sub = new SocketConnection(new TraceLogger(), client.SubClient, socket, null);
var rstEvent = new ManualResetEvent(false);
string original = null;
sub.AddSubscription(SocketSubscription.CreateForIdentifier(10, "TestHandler", true, false, (messageEvent) =>
client.SubClient.ConnectSocketSub(sub);
sub.AddSubscription(new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) =>
{
original = messageEvent.OriginalData;
rstEvent.Set();
}));
client.SubClient.ConnectSocketSub(sub);
// act
socket.InvokeMessage("{\"property\": 123}");
await socket.InvokeMessage("{\"property\": 123}");
rstEvent.WaitOne(1000);
// assert
@@ -109,16 +119,18 @@ namespace CryptoExchange.Net.UnitTests
public void UnsubscribingStream_Should_CloseTheSocket()
{
// arrange
var client = new TestSocketClient(options => {
var client = new TestSocketClient(options =>
{
options.ReconnectInterval = TimeSpan.Zero;
});
});
var socket = client.CreateSocket();
socket.CanConnect = true;
var sub = new SocketConnection(new TraceLogger(), client.SubClient, socket, null);
client.SubClient.ConnectSocketSub(sub);
var us = SocketSubscription.CreateForIdentifier(10, "Test", true, false, (e) => { });
var ups = new UpdateSubscription(sub, us);
sub.AddSubscription(us);
var subscription = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
var ups = new UpdateSubscription(sub, subscription);
sub.AddSubscription(subscription);
// act
client.UnsubscribeAsync(ups).Wait();
@@ -140,12 +152,13 @@ namespace CryptoExchange.Net.UnitTests
var sub2 = new SocketConnection(new TraceLogger(), client.SubClient, socket2, null);
client.SubClient.ConnectSocketSub(sub1);
client.SubClient.ConnectSocketSub(sub2);
var us1 = SocketSubscription.CreateForIdentifier(10, "Test1", true, false, (e) => { });
var us2 = SocketSubscription.CreateForIdentifier(11, "Test2", true, false, (e) => { });
sub1.AddSubscription(us1);
sub2.AddSubscription(us2);
var ups1 = new UpdateSubscription(sub1, us1);
var ups2 = new UpdateSubscription(sub2, us2);
var subscription1 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
var subscription2 = new TestSubscription<Dictionary<string, string>>(Mock.Of<ILogger>(), (messageEvent) => { });
sub1.AddSubscription(subscription1);
sub2.AddSubscription(subscription2);
var ups1 = new UpdateSubscription(sub1, subscription1);
var ups2 = new UpdateSubscription(sub2, subscription2);
// act
client.UnsubscribeAllAsync().Wait();
@@ -5,8 +5,8 @@ using System.Threading.Tasks;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.OrderBook;
using CryptoExchange.Net.Sockets;
using NUnit.Framework;
namespace CryptoExchange.Net.UnitTests
@@ -14,13 +14,13 @@ namespace CryptoExchange.Net.UnitTests
[TestFixture]
public class SymbolOrderBookTests
{
private static OrderBookOptions defaultOrderBookOptions = new OrderBookOptions();
private static readonly OrderBookOptions _defaultOrderBookOptions = new OrderBookOptions();
private class TestableSymbolOrderBook : SymbolOrderBook
{
public TestableSymbolOrderBook() : base(null, "Test", "BTC/USD")
{
Initialize(defaultOrderBookOptions);
Initialize(_defaultOrderBookOptions);
}
@@ -0,0 +1,19 @@
using CryptoExchange.Net.Sockets;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestQuery : Query<object>
{
public override HashSet<string> ListenerIdentifiers { get; set; }
public TestQuery(string identifier, object request, bool authenticated, int weight = 1) : base(request, authenticated, weight)
{
ListenerIdentifiers = new HashSet<string> { identifier };
}
}
}
@@ -0,0 +1,36 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.MessageParsing.Interfaces;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestSubscription<T> : Subscription<object, object>
{
private readonly Action<DataEvent<T>> _handler;
public override HashSet<string> ListenerIdentifiers { get; set; } = new HashSet<string> { "topic" };
public TestSubscription(ILogger logger, Action<DataEvent<T>> handler) : base(logger, false)
{
_handler = handler;
}
public override Task<CallResult> DoHandleMessageAsync(SocketConnection connection, DataEvent<object> message)
{
var data = (T)message.Data;
_handler.Invoke(message.As(data));
return Task.FromResult(new CallResult(null));
}
public override Type GetMessageType(IMessageAccessor message) => typeof(T);
public override Query GetSubQuery(SocketConnection connection) => new TestQuery("sub", new object(), false, 1);
public override Query GetUnsubQuery() => new TestQuery("unsub", new object(), false, 1);
}
}
@@ -1,4 +1,6 @@
using System;
using System.IO;
using System.Net.WebSockets;
using System.Security.Authentication;
using System.Text;
using System.Threading.Tasks;
@@ -12,16 +14,15 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
public bool CanConnect { get; set; }
public bool Connected { get; set; }
public event Action OnClose;
public event Func<Task> OnClose;
#pragma warning disable 0067
public event Action OnReconnected;
public event Action OnReconnecting;
public event Func<Task> OnReconnected;
public event Func<Task> OnReconnecting;
#pragma warning restore 0067
public event Action<int> OnRequestSent;
public event Action<string> OnMessage;
public event Action<Exception> OnError;
public event Action OnOpen;
public event Func<int, Task> OnRequestSent;
public event Func<WebSocketMessageType, Stream, Task> OnStreamMessage;
public event Func<Exception, Task> OnError;
public event Func<Task> OnOpen;
public Func<Task<Uri>> GetReconnectionUrl { get; set; }
public int Id { get; }
@@ -110,9 +111,10 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
OnOpen?.Invoke();
}
public void InvokeMessage(string data)
public async Task InvokeMessage(string data)
{
OnMessage?.Invoke(data);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(data));
await OnStreamMessage?.Invoke(WebSocketMessageType.Text, stream);
}
public void SetProxy(ApiProxy proxy)
@@ -5,7 +5,9 @@ using CryptoExchange.Net.Authentication;
using CryptoExchange.Net.Interfaces;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Options;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.MessageParsing.Interfaces;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json.Linq;
@@ -88,35 +90,6 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
return ConnectSocketAsync(sub).Result;
}
protected internal override bool HandleQueryResponse<T>(SocketConnection s, object request, JToken data, out CallResult<T> callResult)
{
throw new NotImplementedException();
}
protected internal override bool HandleSubscriptionResponse(SocketConnection s, SocketSubscription subscription, object request, JToken message,
out CallResult<object> callResult)
{
throw new NotImplementedException();
}
protected internal override bool MessageMatchesHandler(SocketConnection s, JToken message, object request)
{
throw new NotImplementedException();
}
protected internal override bool MessageMatchesHandler(SocketConnection s, JToken message, string identifier)
{
return true;
}
protected internal override Task<CallResult<bool>> AuthenticateSocketAsync(SocketConnection s)
{
throw new NotImplementedException();
}
protected internal override Task<bool> UnsubscribeAsync(SocketConnection connection, SocketSubscription s)
{
throw new NotImplementedException();
}
public override string GetListenerIdentifier(IMessageAccessor messageAccessor) => "topic";
}
}