1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2026-04-12 16:13:12 +00:00

Compare commits

...

2 Commits

Author SHA1 Message Date
JKorf
61c66300af wip 2026-04-07 21:30:31 +02:00
JKorf
25a392ee38 Fixed Shared GetOrderBookOptions and GetRecentTradesOptions request validation not calling base validation 2026-04-07 19:42:42 +02:00
4 changed files with 101 additions and 3 deletions

View File

@ -0,0 +1,94 @@
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Sockets.Default;
using CryptoExchange.Net.Sockets.Default.Routing;
using CryptoExchange.Net.Sockets.Interfaces;
using NUnit.Framework;
using System;
namespace CryptoExchange.Net.UnitTests
{
internal class RoutingTableTests
{
[Test]
public void Constructor_CreatesEntriesAndDeduplicatesHandlersPerTypeIdentifier()
{
var processor = new TestMessageProcessor(1, MessageRouter.Create(
MessageRoute<string>.CreateWithoutTopicFilter("ticker", EmptyHandler<string>()),
MessageRoute<string>.CreateWithTopicFilter("ticker", "btcusdt", EmptyHandler<string>()),
MessageRoute<int>.CreateWithoutTopicFilter("trade", EmptyHandler<int>())));
var routingTable = new RoutingTable(new[] { processor });
var tickerEntry = routingTable.GetRouteTableEntry("ticker");
var tradeEntry = routingTable.GetRouteTableEntry("trade");
Assert.Multiple(() =>
{
Assert.That(tickerEntry, Is.Not.Null);
Assert.That(tickerEntry!.DeserializationType, Is.EqualTo(typeof(string)));
Assert.That(tickerEntry.IsStringOutput, Is.True);
Assert.That(tickerEntry.Handlers, Has.Count.EqualTo(1));
Assert.That(tickerEntry.Handlers[0], Is.SameAs(processor));
Assert.That(tradeEntry, Is.Not.Null);
Assert.That(tradeEntry!.DeserializationType, Is.EqualTo(typeof(int)));
Assert.That(tradeEntry.IsStringOutput, Is.False);
Assert.That(tradeEntry.Handlers, Has.Count.EqualTo(1));
Assert.That(tradeEntry.Handlers[0], Is.SameAs(processor));
});
}
[Test]
public void Constructor_AddsAllProcessorsForSameTypeIdentifier()
{
var processor1 = new TestMessageProcessor(1, MessageRouter.Create(
MessageRoute<string>.CreateWithoutTopicFilter("ticker", EmptyHandler<string>())));
var processor2 = new TestMessageProcessor(2, MessageRouter.Create(
MessageRoute<string>.CreateWithTopicFilter("ticker", "ethusdt", EmptyHandler<string>())));
var routingTable = new RoutingTable(new IMessageProcessor[] { processor1, processor2 });
var entry = routingTable.GetRouteTableEntry("ticker");
Assert.Multiple(() =>
{
Assert.That(entry, Is.Not.Null);
Assert.That(entry!.Handlers, Has.Count.EqualTo(2));
Assert.That(entry.Handlers, Does.Contain(processor1));
Assert.That(entry.Handlers, Does.Contain(processor2));
});
}
[Test]
public void GetRouteTableEntry_ReturnsNullForUnknownTypeIdentifier()
{
var processor = new TestMessageProcessor(1, MessageRouter.Create(
MessageRoute<string>.CreateWithoutTopicFilter("ticker", EmptyHandler<string>())));
var routingTable = new RoutingTable(new[] { processor });
Assert.That(routingTable.GetRouteTableEntry("unknown"), Is.Null);
}
private static Func<SocketConnection, DateTime, string?, T, CallResult?> EmptyHandler<T>()
{
return (_, _, _, _) => null;
}
private class TestMessageProcessor : IMessageProcessor
{
public int Id { get; }
public MessageRouter MessageRouter { get; }
public event Action? OnMessageRouterUpdated;
public TestMessageProcessor(int id, MessageRouter messageRouter)
{
Id = id;
MessageRouter = messageRouter;
}
public bool Handle(string typeIdentifier, string? topicFilter, SocketConnection socketConnection, DateTime receiveTime, string? originalData, object result)
{
return false;
}
}
}
}

View File

@ -45,7 +45,7 @@ namespace CryptoExchange.Net.SharedApis
public override Error? ValidateRequest(string exchange, GetOrderBookRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
if (request.Limit == null)
return null;
return base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
if (MaxLimit.HasValue && request.Limit.Value > MaxLimit)
return ArgumentError.Invalid(nameof(GetOrderBookRequest.Limit), $"Max limit is {MaxLimit}");

View File

@ -22,8 +22,12 @@ namespace CryptoExchange.Net.SharedApis
}
/// <inheritdoc />
public Error? Validate(GetRecentTradesRequest request)
public override Error? ValidateRequest(string exchange, GetRecentTradesRequest request, TradingMode? tradingMode, TradingMode[] supportedApiTypes)
{
var baseError = base.ValidateRequest(exchange, request, tradingMode, supportedApiTypes);
if (baseError != null)
return baseError;
if (request.Limit > MaxLimit)
return ArgumentError.Invalid(nameof(GetRecentTradesRequest.Limit), $"Only the most recent {MaxLimit} trades are available");

View File

@ -57,7 +57,6 @@ namespace CryptoExchange.Net.Sockets.Default.Routing
private List<MessageRoute> _routesWithoutTopicFilter;
private Dictionary<string, List<MessageRoute>> _routesWithTopicFilter;
#if NET8_0_OR_GREATER
// Used for mapping a type identifier to the routes matching it
private FrozenDictionary<string, List<MessageRoute>>? _routesWithTopicFilterFrozen;
#endif
@ -129,6 +128,7 @@ namespace CryptoExchange.Net.Sockets.Default.Routing
{
result ??= thisResult;
#warning MultipleReaders is only for queries, subscriptions should always have multiple readers = true. Maybe create different RoutingSubTable implementations for Queries and Subscriptions?
if (!MultipleReaders)
break;
}