mirror of
				https://github.com/JKorf/CryptoExchange.Net
				synced 2025-11-04 04:17:32 +00:00 
			
		
		
		
	* 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
		
			
				
	
	
		
			236 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using CryptoExchange.Net.Interfaces;
 | 
						|
using CryptoExchange.Net.Objects;
 | 
						|
using Moq;
 | 
						|
using System;
 | 
						|
using System.IO;
 | 
						|
using System.Net;
 | 
						|
using System.Net.Http;
 | 
						|
using System.Reflection;
 | 
						|
using System.Text;
 | 
						|
using System.Threading;
 | 
						|
using System.Threading.Tasks;
 | 
						|
using CryptoExchange.Net.Authentication;
 | 
						|
using System.Collections.Generic;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
using CryptoExchange.Net.Clients;
 | 
						|
using CryptoExchange.Net.SharedApis;
 | 
						|
using Microsoft.Extensions.Options;
 | 
						|
using System.Linq;
 | 
						|
using CryptoExchange.Net.Converters.SystemTextJson;
 | 
						|
using System.Text.Json.Serialization;
 | 
						|
 | 
						|
namespace CryptoExchange.Net.UnitTests.TestImplementations
 | 
						|
{
 | 
						|
    public class TestRestClient: BaseRestClient
 | 
						|
    {
 | 
						|
        public TestRestApi1Client Api1 { get; }
 | 
						|
        public TestRestApi2Client Api2 { get; }
 | 
						|
 | 
						|
        public TestRestClient(Action<TestClientOptions> optionsDelegate = null)
 | 
						|
            : this(null, null, Options.Create(ApplyOptionsDelegate(optionsDelegate)))
 | 
						|
        {
 | 
						|
        }
 | 
						|
 | 
						|
        public TestRestClient(HttpClient httpClient, ILoggerFactory loggerFactory, IOptions<TestClientOptions> options) : base(loggerFactory, "Test")
 | 
						|
        {
 | 
						|
            Initialize(options.Value);
 | 
						|
 | 
						|
            Api1 = new TestRestApi1Client(options.Value);
 | 
						|
            Api2 = new TestRestApi2Client(options.Value);
 | 
						|
        }
 | 
						|
 | 
						|
        public void SetResponse(string responseData, out IRequest requestObj)
 | 
						|
        {
 | 
						|
            var expectedBytes = Encoding.UTF8.GetBytes(responseData);
 | 
						|
            var responseStream = new MemoryStream();
 | 
						|
            responseStream.Write(expectedBytes, 0, expectedBytes.Length);
 | 
						|
            responseStream.Seek(0, SeekOrigin.Begin);
 | 
						|
 | 
						|
            var response = new Mock<IResponse>();
 | 
						|
            response.Setup(c => c.IsSuccessStatusCode).Returns(true);
 | 
						|
            response.Setup(c => c.GetResponseStreamAsync()).Returns(Task.FromResult((Stream)responseStream));
 | 
						|
            
 | 
						|
            var headers = new Dictionary<string, string[]>();
 | 
						|
            var request = new Mock<IRequest>();
 | 
						|
            request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
 | 
						|
            request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(response.Object));
 | 
						|
            request.Setup(c => c.SetContent(It.IsAny<string>(), It.IsAny<string>())).Callback(new Action<string, string>((content, type) => { request.Setup(r => r.Content).Returns(content); }));
 | 
						|
            request.Setup(c => c.AddHeader(It.IsAny<string>(), It.IsAny<string>())).Callback<string, string>((key, val) => headers.Add(key, new string[] { val }));
 | 
						|
            request.Setup(c => c.GetHeaders()).Returns(() => headers.ToArray());
 | 
						|
 | 
						|
            var factory = Mock.Get(Api1.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Callback<HttpMethod, Uri, int>((method, uri, id) => 
 | 
						|
                { 
 | 
						|
                    request.Setup(a => a.Uri).Returns(uri);
 | 
						|
                    request.Setup(a => a.Method).Returns(method); 
 | 
						|
                })
 | 
						|
                .Returns(request.Object);
 | 
						|
 | 
						|
            factory = Mock.Get(Api2.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Callback<HttpMethod, Uri, int>((method, uri, id) =>
 | 
						|
                {
 | 
						|
                    request.Setup(a => a.Uri).Returns(uri);
 | 
						|
                    request.Setup(a => a.Method).Returns(method);
 | 
						|
                })
 | 
						|
                .Returns(request.Object);
 | 
						|
            requestObj = request.Object;
 | 
						|
        }
 | 
						|
 | 
						|
        public void SetErrorWithoutResponse(HttpStatusCode code, string message)
 | 
						|
        {
 | 
						|
            var we = new HttpRequestException();
 | 
						|
            typeof(HttpRequestException).GetField("_message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(we, message);
 | 
						|
           
 | 
						|
            var request = new Mock<IRequest>();
 | 
						|
            request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
 | 
						|
            request.Setup(c => c.GetHeaders()).Returns(new KeyValuePair<string, string[]>[0]);
 | 
						|
            request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we);
 | 
						|
 | 
						|
            var factory = Mock.Get(Api1.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Returns(request.Object);
 | 
						|
 | 
						|
 | 
						|
            factory = Mock.Get(Api2.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Returns(request.Object);
 | 
						|
        }
 | 
						|
 | 
						|
        public void SetErrorWithResponse(string responseData, HttpStatusCode code)
 | 
						|
        {
 | 
						|
            var expectedBytes = Encoding.UTF8.GetBytes(responseData);
 | 
						|
            var responseStream = new MemoryStream();
 | 
						|
            responseStream.Write(expectedBytes, 0, expectedBytes.Length);
 | 
						|
            responseStream.Seek(0, SeekOrigin.Begin);
 | 
						|
 | 
						|
            var response = new Mock<IResponse>();
 | 
						|
            response.Setup(c => c.IsSuccessStatusCode).Returns(false);
 | 
						|
            response.Setup(c => c.GetResponseStreamAsync()).Returns(Task.FromResult((Stream)responseStream));
 | 
						|
 | 
						|
            var headers = new List<KeyValuePair<string, string[]>>();
 | 
						|
            var request = new Mock<IRequest>();
 | 
						|
            request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
 | 
						|
            request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Returns(Task.FromResult(response.Object));
 | 
						|
            request.Setup(c => c.AddHeader(It.IsAny<string>(), It.IsAny<string>())).Callback<string, string>((key, val) => headers.Add(new KeyValuePair<string, string[]>(key, new string[] { val })));
 | 
						|
            request.Setup(c => c.GetHeaders()).Returns(headers.ToArray());
 | 
						|
 | 
						|
            var factory = Mock.Get(Api1.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
 | 
						|
                .Returns(request.Object);
 | 
						|
 | 
						|
            factory = Mock.Get(Api2.RequestFactory);
 | 
						|
            factory.Setup(c => c.Create(It.IsAny<HttpMethod>(), It.IsAny<Uri>(), It.IsAny<int>()))
 | 
						|
                .Callback<HttpMethod, Uri, int>((method, uri, id) => request.Setup(a => a.Uri).Returns(uri))
 | 
						|
                .Returns(request.Object);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public class TestRestApi1Client : RestApiClient
 | 
						|
    {
 | 
						|
        public TestRestApi1Client(TestClientOptions options) : base(new TraceLogger(), null, "https://localhost:123", options, options.Api1Options)
 | 
						|
        {
 | 
						|
            RequestFactory = new Mock<IRequestFactory>().Object;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <inheritdoc />
 | 
						|
        public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode futuresType, DateTime? deliverDate = null) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
 | 
						|
 | 
						|
        protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(new System.Text.Json.JsonSerializerOptions() { TypeInfoResolver = new TestSerializerContext() });
 | 
						|
        protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(new System.Text.Json.JsonSerializerOptions());
 | 
						|
 | 
						|
        public async Task<CallResult<T>> Request<T>(CancellationToken ct = default) where T : class
 | 
						|
        {
 | 
						|
            return await SendAsync<T>("http://www.test.com", new RequestDefinition("/", HttpMethod.Get) { Weight = 0 }, null, ct);
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<CallResult<T>> RequestWithParams<T>(HttpMethod method, ParameterCollection parameters, Dictionary<string, string> headers) where T : class
 | 
						|
        {
 | 
						|
            return await SendAsync<T>("http://www.test.com", new RequestDefinition("/", method) { Weight = 0 }, parameters, default, additionalHeaders: headers);
 | 
						|
        }
 | 
						|
 | 
						|
        public void SetParameterPosition(HttpMethod method, HttpMethodParameterPosition position)
 | 
						|
        {
 | 
						|
            ParameterPositions[method] = position;
 | 
						|
        }
 | 
						|
 | 
						|
        public override TimeSpan? GetTimeOffset()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
 | 
						|
        protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
 | 
						|
            => new TestAuthProvider(credentials);
 | 
						|
 | 
						|
        protected override Task<WebCallResult<DateTime>> GetServerTimestampAsync()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
 | 
						|
        public override TimeSyncInfo GetTimeSyncInfo()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public class TestRestApi2Client : RestApiClient
 | 
						|
    {
 | 
						|
        public TestRestApi2Client(TestClientOptions options) : base(new TraceLogger(), null, "https://localhost:123", options, options.Api2Options)
 | 
						|
        {
 | 
						|
            RequestFactory = new Mock<IRequestFactory>().Object;
 | 
						|
        }
 | 
						|
 | 
						|
        protected override IStreamMessageAccessor CreateAccessor() => new SystemTextJsonStreamMessageAccessor(new System.Text.Json.JsonSerializerOptions());
 | 
						|
        protected override IMessageSerializer CreateSerializer() => new SystemTextJsonMessageSerializer(new System.Text.Json.JsonSerializerOptions());
 | 
						|
 | 
						|
        /// <inheritdoc />
 | 
						|
        public override string FormatSymbol(string baseAsset, string quoteAsset, TradingMode futuresType, DateTime? deliverDate = null) => $"{baseAsset.ToUpperInvariant()}{quoteAsset.ToUpperInvariant()}";
 | 
						|
 | 
						|
        public async Task<CallResult<T>> Request<T>(CancellationToken ct = default) where T : class
 | 
						|
        {
 | 
						|
            return await SendAsync<T>("http://www.test.com", new RequestDefinition("/", HttpMethod.Get) { Weight = 0 }, null, ct);
 | 
						|
        }
 | 
						|
 | 
						|
        protected override Error ParseErrorResponse(int httpStatusCode, KeyValuePair<string, string[]>[] responseHeaders, IMessageAccessor accessor, Exception exception)
 | 
						|
        {
 | 
						|
            var errorData = accessor.Deserialize<TestError>();
 | 
						|
 | 
						|
            return new ServerError(errorData.Data.ErrorCode, errorData.Data.ErrorMessage);
 | 
						|
        }
 | 
						|
 | 
						|
        public override TimeSpan? GetTimeOffset()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
 | 
						|
        protected override AuthenticationProvider CreateAuthenticationProvider(ApiCredentials credentials)
 | 
						|
            => new TestAuthProvider(credentials);
 | 
						|
 | 
						|
        protected override Task<WebCallResult<DateTime>> GetServerTimestampAsync()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
 | 
						|
        public override TimeSyncInfo GetTimeSyncInfo()
 | 
						|
        {
 | 
						|
            throw new NotImplementedException();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public class TestError
 | 
						|
    {
 | 
						|
        [JsonPropertyName("errorCode")]
 | 
						|
        public int ErrorCode { get; set; }
 | 
						|
        [JsonPropertyName("errorMessage")]
 | 
						|
        public string ErrorMessage { get; set; }
 | 
						|
    }
 | 
						|
 | 
						|
    public class ParseErrorTestRestClient: TestRestClient
 | 
						|
    {
 | 
						|
        public ParseErrorTestRestClient() { }
 | 
						|
 | 
						|
    }
 | 
						|
}
 |