Skip to content

Commit

Permalink
Add tests for subscription confirmation (#191)
Browse files Browse the repository at this point in the history
* Add tests for subscription confirmation
  • Loading branch information
Jonnern authored Mar 8, 2024
1 parent 61aa589 commit 462c857
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 6 deletions.
54 changes: 50 additions & 4 deletions CryptoExchange.Net.UnitTests/SocketClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@
using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using NUnit.Framework;
using NUnit.Framework.Constraints;

namespace CryptoExchange.Net.UnitTests
{
Expand Down Expand Up @@ -106,13 +105,14 @@ public async Task SocketMessages_Should_ContainOriginalDataIfEnabled(bool enable
original = messageEvent.OriginalData;
rstEvent.Set();
}));
var msgToSend = JsonConvert.SerializeObject(new { topic = "topic", property = 123 });

// act
await socket.InvokeMessage("{\"property\": 123}");
await socket.InvokeMessage(msgToSend);
rstEvent.WaitOne(1000);

// assert
Assert.IsTrue(original == (enabled ? "{\"property\": 123}" : null));
Assert.IsTrue(original == (enabled ? msgToSend : null));
}

[TestCase()]
Expand Down Expand Up @@ -183,5 +183,51 @@ public void FailingToConnectSocket_Should_ReturnError()
// assert
Assert.IsFalse(connectResult.Success);
}

[TestCase()]
public async Task ErrorResponse_ShouldNot_ConfirmSubscription()
{
// arrange
var channel = "trade_btcusd";
var client = new TestSocketClient(opt =>
{
opt.OutputOriginalData = true;
opt.SocketSubscriptionsCombineTarget = 1;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), client.SubClient, socket, "https://test.test"));

// act
var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
await socket.InvokeMessage(JsonConvert.SerializeObject(new { channel, status = "error" }));
await sub;

// assert
Assert.IsFalse(client.SubClient.TestSubscription.Confirmed);
}

[TestCase()]
public async Task SuccessResponse_Should_ConfirmSubscription()
{
// arrange
var channel = "trade_btcusd";
var client = new TestSocketClient(opt =>
{
opt.OutputOriginalData = true;
opt.SocketSubscriptionsCombineTarget = 1;
});
var socket = client.CreateSocket();
socket.CanConnect = true;
client.SubClient.ConnectSocketSub(new SocketConnection(new TraceLogger(), client.SubClient, socket, "https://test.test"));

// act
var sub = client.SubClient.SubscribeToSomethingAsync(channel, onUpdate => {}, ct: default);
await socket.InvokeMessage(JsonConvert.SerializeObject(new { channel, status = "confirmed" }));
await sub;

// assert
Assert.IsTrue(client.SubClient.TestSubscription.Confirmed);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class SubResponse
{
[JsonProperty("channel")]
public string Channel { get; set; } = null!;

[JsonProperty("status")]
public string Status { get; set; } = null!;
}

internal class UnsubResponse
{
[JsonProperty("status")]
public string Status { get; set; } = null!;
}

internal class TestChannelQuery : Query<SubResponse>
{
public override HashSet<string> ListenerIdentifiers { get; set; }

public TestChannelQuery(string channel, string request, bool authenticated, int weight = 1) : base(request, authenticated, weight)
{
ListenerIdentifiers = new HashSet<string> { channel };
}

public override Task<CallResult<SubResponse>> HandleMessageAsync(SocketConnection connection, DataEvent<SubResponse> message)
{
if (!message.Data.Status.Equals("confirmed", StringComparison.OrdinalIgnoreCase))
{
return Task.FromResult(new CallResult<SubResponse>(new ServerError(message.Data.Status)));
}

return base.HandleMessageAsync(connection, message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using CryptoExchange.Net.Sockets;
using CryptoExchange.Net.Sockets.MessageParsing.Interfaces;
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace CryptoExchange.Net.UnitTests.TestImplementations.Sockets
{
internal class TestSubscriptionWithResponseCheck<T> : Subscription<SubResponse, UnsubResponse>
{
private readonly Action<DataEvent<T>> _handler;
private readonly string _channel;

public override HashSet<string> ListenerIdentifiers { get; set; }

public TestSubscriptionWithResponseCheck(string channel, Action<DataEvent<T>> handler) : base(Mock.Of<ILogger>(), false)
{
ListenerIdentifiers = new HashSet<string>() { channel };
_handler = handler;
_channel = channel;
}

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 TestChannelQuery(_channel, "subscribe", false, 1);
public override Query GetUnsubQuery() => new TestChannelQuery(_channel, "unsubscribe", false, 1);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
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;
using CryptoExchange.Net.Sockets.MessageParsing.Interfaces;
using CryptoExchange.Net.UnitTests.TestImplementations.Sockets;
using Microsoft.Extensions.Logging;
using Moq;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -71,8 +74,12 @@ public class TestSocketOptions: SocketExchangeOptions<TestEnvironment>

public class TestSubSocketClient : SocketApiClient
{
private MessagePath _channelPath = MessagePath.Get().Property("channel");
private MessagePath _topicPath = MessagePath.Get().Property("topic");

public TestSubSocketClient(TestSocketOptions options, SocketApiOptions apiOptions): base(new TraceLogger(), options.Environment.TestAddress, options, apiOptions)
public Subscription TestSubscription { get; private set; } = null;

public TestSubSocketClient(TestSocketOptions options, SocketApiOptions apiOptions) : base(new TraceLogger(), options.Environment.TestAddress, options, apiOptions)
{

}
Expand All @@ -90,6 +97,23 @@ public CallResult<bool> ConnectSocketSub(SocketConnection sub)
return ConnectSocketAsync(sub).Result;
}

public override string GetListenerIdentifier(IMessageAccessor messageAccessor) => "topic";
public override string GetListenerIdentifier(IMessageAccessor message)
{
if (!message.IsJson)
{
return "topic";
}

var id = message.GetValue<string>(_channelPath);
id ??= message.GetValue<string>(_topicPath);

return id;
}

public Task<CallResult<UpdateSubscription>> SubscribeToSomethingAsync(string channel, Action<DataEvent<string>> onUpdate, CancellationToken ct)
{
TestSubscription = new TestSubscriptionWithResponseCheck<string>(channel, onUpdate);
return SubscribeAsync(TestSubscription, ct);
}
}
}

0 comments on commit 462c857

Please sign in to comment.