mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-13 01:12:59 +00:00
6b14cdbf06
* Added support for Native AOT compilation * Updated all IEnumerable response types to array response types * Added Pass support for ApiCredentials, removing the need for most implementations to add their own ApiCredentials type * Added KeepAliveTimeout setting setting ping frame timeouts for SocketApiClient * Added IBookTickerRestClient Shared interface for requesting book tickers * Added ISpotTriggerOrderRestClient Shared interface for managing spot trigger orders * Added ISpotOrderClientIdClient Shared interface for managing spot orders by client order id * Added IFuturesTriggerOrderRestClient Shared interface for managing futures trigger orders * Added IFuturesOrderClientIdClient Shared interface for managing futures orders by client order id * Added IFuturesTpSlRestClient Shared interface for setting TP/SL on open futures positions * Added GenerateClientOrderId to ISpotOrderRestClient and IFuturesOrderRestClient interface * Added OptionalExchangeParameters and Supported properties to EndpointOptions * Refactor Shared interfaces quantity parameters and properties to use SharedQuantity * Added SharedSymbol property to Shared interface models returning a symbol * Added TriggerPrice, IsTriggerOrder, TakeProfitPrice, StopLossPrice and IsCloseOrder to SharedFuturesOrder response model * Added MaxShortLeverage and MaxLongLeverage to SharedFuturesSymbol response model * Added StopLossPrice and TakeProfitPrice to SharedPosition response model * Added TriggerPrice and IsTriggerOrder to SharedSpotOrder response model * Added QuoteVolume property to SharedSpotTicker response model * Added AssetAlias configuration models * Added static ExchangeSymbolCache for tracking symbol information from exchanges * Added static CallResult.SuccessResult to be used instead of constructing success CallResult instance * Added static ApplyRules, RandomHexString and RandomLong helper methods to ExchangeHelpers class * Added AsErrorWithData To CallResult * Added OriginalData property to CallResult * Added support for adjusting the rate limit key per call, allowing for ratelimiting depending on request parameters * Added implementation for integration testing ISymbolOrderBook instances * Added implementation for integration testing socket subscriptions * Added implementation for testing socket queries * Updated request cancellation logging to Debug level * Updated logging SourceContext to include the client type * Updated some logging logic, errors no longer contain any data, exception are not logged as string but instead forwarded to structured logging * Fixed warning for Enum parsing throwing exception and output warnings for each object in a response to only once to prevent slowing down execution * Fixed memory leak in AsyncAutoRestEvent * Fixed logging for ping frame timeout * Fixed warning getting logged when user stops SymbolOrderBook instance * Fixed socket client `UnsubscribeAll` not unsubscribing dedicated connections * Fixed memory leak in Rest client cache * Fixed integers bigger than int16 not getting correctly parsed to enums * Fixed issue where the default options were overridden when using SetApiCredentials * Removed Newtonsoft.Json dependency * Removed legacy Rest client code * Removed legacy ISpotClient and IFuturesClient support
119 lines
4.1 KiB
C#
119 lines
4.1 KiB
C#
using CryptoExchange.Net.Interfaces;
|
|
using CryptoExchange.Net.Objects;
|
|
using CryptoExchange.Net.Objects.Sockets;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Linq.Expressions;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace CryptoExchange.Net.Testing
|
|
{
|
|
/// <summary>
|
|
/// Base class for executing websocket API integration tests
|
|
/// </summary>
|
|
/// <typeparam name="TClient">Client type</typeparam>
|
|
public abstract class SocketIntegrationTest<TClient>
|
|
{
|
|
/// <summary>
|
|
/// Get a client instance
|
|
/// </summary>
|
|
/// <param name="loggerFactory"></param>
|
|
/// <returns></returns>
|
|
public abstract TClient GetClient(ILoggerFactory loggerFactory);
|
|
|
|
/// <summary>
|
|
/// Whether the test should be run. By default integration tests aren't executed, can be set to true to force execution.
|
|
/// </summary>
|
|
public virtual bool Run { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether API credentials are provided and thus authenticated calls can be executed. Should be set in the GetClient implementation.
|
|
/// </summary>
|
|
public bool Authenticated { get; set; }
|
|
|
|
/// <summary>
|
|
/// Create a client
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected TClient CreateClient()
|
|
{
|
|
var fact = new LoggerFactory();
|
|
fact.AddProvider(new TraceLoggerProvider());
|
|
return GetClient(fact);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if integration tests should be executed
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
protected bool ShouldRun()
|
|
{
|
|
var integrationTests = Environment.GetEnvironmentVariable("INTEGRATION");
|
|
if (!Run && integrationTests != "1")
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Execute a REST endpoint call and check for any errors or warnings.
|
|
/// </summary>
|
|
/// <typeparam name="T">Type of the update</typeparam>
|
|
/// <param name="expression">The call expression</param>
|
|
/// <param name="expectUpdate">Whether an update is expected</param>
|
|
/// <param name="authRequest">Whether this is an authenticated request</param>
|
|
public async Task RunAndCheckUpdate<T>(Expression<Func<TClient, Action<DataEvent<T>>, Task<CallResult<UpdateSubscription>>>> expression, bool expectUpdate, bool authRequest)
|
|
{
|
|
if (!ShouldRun())
|
|
return;
|
|
|
|
var client = CreateClient();
|
|
|
|
var expressionBody = (MethodCallExpression)expression.Body;
|
|
if (authRequest && !Authenticated)
|
|
{
|
|
Debug.WriteLine($"Skipping {expressionBody.Method.Name}, not authenticated");
|
|
return;
|
|
}
|
|
|
|
var listener = new EnumValueTraceListener();
|
|
Trace.Listeners.Add(listener);
|
|
|
|
var evnt = new ManualResetEvent(!expectUpdate);
|
|
DataEvent<T>? receivedUpdate = null;
|
|
var updateHandler = (DataEvent<T> update) =>
|
|
{
|
|
receivedUpdate = update;
|
|
evnt.Set();
|
|
};
|
|
|
|
CallResult<UpdateSubscription> result;
|
|
try
|
|
{
|
|
result = await expression.Compile().Invoke(client, updateHandler).ConfigureAwait(false);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new Exception($"Method {expressionBody.Method.Name} threw an exception: " + ex.ToLogString());
|
|
}
|
|
finally
|
|
{
|
|
Trace.Listeners.Remove(listener);
|
|
}
|
|
|
|
if (!result.Success)
|
|
throw new Exception($"Method {expressionBody.Method.Name} returned error: " + result.Error);
|
|
|
|
evnt.WaitOne(TimeSpan.FromSeconds(10));
|
|
|
|
if (expectUpdate && receivedUpdate == null)
|
|
throw new Exception($"Method {expressionBody.Method.Name} has not received an update");
|
|
|
|
await result.Data.CloseAsync().ConfigureAwait(false);
|
|
Debug.WriteLine($"{expressionBody.Method.Name} {result}");
|
|
}
|
|
}
|
|
}
|