mirror of
https://github.com/JKorf/CryptoExchange.Net.git
synced 2026-08-14 09:52:53 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 271503c426 | |||
| f0ece589f7 | |||
| 3a9382ca0f | |||
| 1e1a02324a |
@@ -6,9 +6,9 @@
|
|||||||
<PackageId>CryptoExchange.Net</PackageId>
|
<PackageId>CryptoExchange.Net</PackageId>
|
||||||
<Authors>JKorf</Authors>
|
<Authors>JKorf</Authors>
|
||||||
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
<Description>CryptoExchange.Net is a base library which is used to implement different cryptocurrency (exchange) API's. It provides a standardized way of implementing different API's, which results in a very similar experience for users of the API implementations.</Description>
|
||||||
<PackageVersion>11.2.0</PackageVersion>
|
<PackageVersion>11.2.1</PackageVersion>
|
||||||
<AssemblyVersion>11.2.0</AssemblyVersion>
|
<AssemblyVersion>11.2.1</AssemblyVersion>
|
||||||
<FileVersion>11.2.0</FileVersion>
|
<FileVersion>11.2.1</FileVersion>
|
||||||
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
|
||||||
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
<PackageTags>OKX;OKX.Net;Mexc;Mexc.Net;Kucoin;Kucoin.Net;Kraken;Kraken.Net;Huobi;Huobi.Net;CoinEx;CoinEx.Net;Bybit;Bybit.Net;Bitget;Bitget.Net;Bitfinex;Bitfinex.Net;Binance;Binance.Net;CryptoCurrency;CryptoCurrency Exchange;CryptoExchange.Net</PackageTags>
|
||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
|
|||||||
@@ -395,9 +395,9 @@ namespace CryptoExchange.Net.Testing.Comparers
|
|||||||
}
|
}
|
||||||
else if (objectValue is bool bl)
|
else if (objectValue is bool bl)
|
||||||
{
|
{
|
||||||
if (bl && (stringValue != "1" && stringValue != "true" && stringValue != "True"))
|
if (bl && (stringValue != "1" && stringValue != "true" && stringValue != "True" && stringValue != "yes" && stringValue != "YES"))
|
||||||
throw new Exception($"{method}: {property} not equal: {stringValue} vs {bl}");
|
throw new Exception($"{method}: {property} not equal: {stringValue} vs {bl}");
|
||||||
if (!bl && (stringValue != "0" && stringValue != "-1" && stringValue != "false" && stringValue != "False"))
|
if (!bl && (stringValue != "0" && stringValue != "-1" && stringValue != "false" && stringValue != "False" && stringValue != "no" && stringValue != "NO"))
|
||||||
throw new Exception($"{method}: {property} not equal: {stringValue} vs {bl}");
|
throw new Exception($"{method}: {property} not equal: {stringValue} vs {bl}");
|
||||||
}
|
}
|
||||||
else if (propertyType.IsEnum || Nullable.GetUnderlyingType(propertyType)?.IsEnum == true)
|
else if (propertyType.IsEnum || Nullable.GetUnderlyingType(propertyType)?.IsEnum == true)
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// <param name="name">Method name for looking up json test values</param>
|
/// <param name="name">Method name for looking up json test values</param>
|
||||||
/// <param name="nestedJsonProperty">Use nested json property for compare</param>
|
/// <param name="nestedJsonProperty">Use nested json property for compare</param>
|
||||||
/// <param name="ignoreProperties">Ignore certain properties</param>
|
/// <param name="ignoreProperties">Ignore certain properties</param>
|
||||||
|
/// <param name="ignoreParamValidation">Ignore certain parameter validation</param>
|
||||||
/// <param name="useSingleArrayItem">Use the first item of an json array response</param>
|
/// <param name="useSingleArrayItem">Use the first item of an json array response</param>
|
||||||
/// <param name="skipResponseValidation">Whether to skip the response model validation</param>
|
/// <param name="skipResponseValidation">Whether to skip the response model validation</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@@ -66,9 +67,10 @@ namespace CryptoExchange.Net.Testing
|
|||||||
string name,
|
string name,
|
||||||
string? nestedJsonProperty = null,
|
string? nestedJsonProperty = null,
|
||||||
List<string>? ignoreProperties = null,
|
List<string>? ignoreProperties = null,
|
||||||
|
List<string>? ignoreParamValidation = null,
|
||||||
bool useSingleArrayItem = false,
|
bool useSingleArrayItem = false,
|
||||||
bool skipResponseValidation = false)
|
bool skipResponseValidation = false)
|
||||||
=> ValidateAsync<TResponse, TResponse>(methodInvoke, name, nestedJsonProperty, ignoreProperties, useSingleArrayItem, skipResponseValidation);
|
=> ValidateAsync<TResponse, TResponse>(methodInvoke, name, nestedJsonProperty, ignoreProperties, ignoreParamValidation, useSingleArrayItem, skipResponseValidation);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validate a request
|
/// Validate a request
|
||||||
@@ -79,6 +81,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// <param name="name">Method name for looking up json test values</param>
|
/// <param name="name">Method name for looking up json test values</param>
|
||||||
/// <param name="nestedJsonProperty">Use nested json property for compare</param>
|
/// <param name="nestedJsonProperty">Use nested json property for compare</param>
|
||||||
/// <param name="ignoreProperties">Ignore certain properties</param>
|
/// <param name="ignoreProperties">Ignore certain properties</param>
|
||||||
|
/// <param name="ignoreParamValidation">Ignore certain parameter validation</param>
|
||||||
/// <param name="useSingleArrayItem">Use the first item of an json array response</param>
|
/// <param name="useSingleArrayItem">Use the first item of an json array response</param>
|
||||||
/// <param name="skipResponseValidation">Whether to skip the response model validation</param>
|
/// <param name="skipResponseValidation">Whether to skip the response model validation</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
@@ -88,6 +91,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
string name,
|
string name,
|
||||||
string? nestedJsonProperty = null,
|
string? nestedJsonProperty = null,
|
||||||
List<string>? ignoreProperties = null,
|
List<string>? ignoreProperties = null,
|
||||||
|
List<string>? ignoreParamValidation = null,
|
||||||
bool useSingleArrayItem = false,
|
bool useSingleArrayItem = false,
|
||||||
bool skipResponseValidation = false) where TActualResponse : TResponse
|
bool skipResponseValidation = false) where TActualResponse : TResponse
|
||||||
{
|
{
|
||||||
@@ -115,7 +119,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
var expectedPath = reader.ReadLine();
|
var expectedPath = reader.ReadLine();
|
||||||
var expectedAuth = bool.Parse(reader.ReadLine()!);
|
var expectedAuth = bool.Parse(reader.ReadLine()!);
|
||||||
var paramsAndResponseBody = reader.ReadToEnd();
|
var paramsAndResponseBody = reader.ReadToEnd();
|
||||||
var paramsAndResponseBodySplit = paramsAndResponseBody.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
|
var paramsAndResponseBodySplit = paramsAndResponseBody.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
var uriParamsLine = paramsAndResponseBodySplit.FirstOrDefault(x => x.StartsWith("UriParams: "));
|
var uriParamsLine = paramsAndResponseBodySplit.FirstOrDefault(x => x.StartsWith("UriParams: "));
|
||||||
Dictionary<string, object>? expectedUriParams = null;
|
Dictionary<string, object>? expectedUriParams = null;
|
||||||
@@ -156,7 +160,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
? urlParametersString.Split('&').ToDictionary(x => x.Split('=')[0], x => (object)x.Split('=')[1])
|
? urlParametersString.Split('&').ToDictionary(x => x.Split('=')[0], x => (object)x.Split('=')[1])
|
||||||
: new());
|
: new());
|
||||||
|
|
||||||
CompareParameters(expectedUriParams, urlParameters);
|
CompareParameters(expectedUriParams, urlParameters, ignoreParamValidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expectedBodyParams != null)
|
if (expectedBodyParams != null)
|
||||||
@@ -173,7 +177,7 @@ namespace CryptoExchange.Net.Testing
|
|||||||
bodyParameters = splitKvp.Select(x => x.Split('=')).ToDictionary(x => x[0], x => (object)x[1]);
|
bodyParameters = splitKvp.Select(x => x.Split('=')).ToDictionary(x => x[0], x => (object)x[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompareParameters(expectedBodyParams, bodyParameters);
|
CompareParameters(expectedBodyParams, bodyParameters, ignoreParamValidation);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!skipResponseValidation)
|
if (!skipResponseValidation)
|
||||||
@@ -186,13 +190,16 @@ namespace CryptoExchange.Net.Testing
|
|||||||
Trace.Listeners.Remove(listener);
|
Trace.Listeners.Remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CompareParameters(Dictionary<string, object> expectedUrlParameters, Dictionary<string, object> parameters)
|
private void CompareParameters(Dictionary<string, object> expectedUrlParameters, Dictionary<string, object> parameters, List<string>? ignoreParamValidation)
|
||||||
{
|
{
|
||||||
if (expectedUrlParameters.Count > parameters.Count)
|
if (expectedUrlParameters.Count > parameters.Count)
|
||||||
throw new Exception($"Url parameters count not matched. Expected: {expectedUrlParameters.Count}, Actual: {parameters.Count}");
|
throw new Exception($"Url parameters count not matched. Expected: {expectedUrlParameters.Count}, Actual: {parameters.Count}");
|
||||||
|
|
||||||
foreach (var kvp in expectedUrlParameters)
|
foreach (var kvp in expectedUrlParameters)
|
||||||
{
|
{
|
||||||
|
if (ignoreParamValidation != null && ignoreParamValidation.Contains(kvp.Key))
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!parameters.TryGetValue(kvp.Key, out var value))
|
if (!parameters.TryGetValue(kvp.Key, out var value))
|
||||||
throw new Exception($"Url parameter {kvp.Key} not found in actual parameters");
|
throw new Exception($"Url parameter {kvp.Key} not found in actual parameters");
|
||||||
|
|
||||||
@@ -206,11 +213,14 @@ namespace CryptoExchange.Net.Testing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="methodInvoke">Method invocation</param>
|
/// <param name="methodInvoke">Method invocation</param>
|
||||||
/// <param name="name">Method name for looking up json test values</param>
|
/// <param name="name">Method name for looking up json test values</param>
|
||||||
|
/// <param name="ignoreParamValidation">Ignore certain parameter validation</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public async Task ValidateAsync(
|
public async Task ValidateAsync(
|
||||||
Func<TClient, Task<WebCallResult>> methodInvoke,
|
Func<TClient, Task<WebCallResult>> methodInvoke,
|
||||||
string name)
|
string name,
|
||||||
|
List<string>? ignoreParamValidation = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var listener = new EnumValueTraceListener();
|
var listener = new EnumValueTraceListener();
|
||||||
Trace.Listeners.Add(listener);
|
Trace.Listeners.Add(listener);
|
||||||
@@ -235,7 +245,26 @@ namespace CryptoExchange.Net.Testing
|
|||||||
var expectedMethod = reader.ReadLine();
|
var expectedMethod = reader.ReadLine();
|
||||||
var expectedPath = reader.ReadLine();
|
var expectedPath = reader.ReadLine();
|
||||||
var expectedAuth = bool.Parse(reader.ReadLine()!);
|
var expectedAuth = bool.Parse(reader.ReadLine()!);
|
||||||
var response = reader.ReadToEnd();
|
var paramsAndResponseBody = reader.ReadToEnd();
|
||||||
|
var paramsAndResponseBodySplit = paramsAndResponseBody.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
var uriParamsLine = paramsAndResponseBodySplit.FirstOrDefault(x => x.StartsWith("UriParams: "));
|
||||||
|
Dictionary<string, object>? expectedUriParams = null;
|
||||||
|
var bodyParamsLine = paramsAndResponseBodySplit.FirstOrDefault(x => x.StartsWith("BodyParams: "));
|
||||||
|
Dictionary<string, object>? expectedBodyParams = null;
|
||||||
|
var response = string.Join("\r\n", paramsAndResponseBodySplit.Where(x => !x.StartsWith("UriParams: ") && !x.StartsWith("BodyParams: ")));
|
||||||
|
|
||||||
|
if (uriParamsLine != null)
|
||||||
|
{
|
||||||
|
var expectedUriParamsJson = uriParamsLine.Substring(11);
|
||||||
|
expectedUriParams = JsonSerializer.Deserialize<Dictionary<string, object>>(expectedUriParamsJson)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bodyParamsLine != null)
|
||||||
|
{
|
||||||
|
var expectedBodyParamsJson = bodyParamsLine.Substring(12);
|
||||||
|
expectedBodyParams = JsonSerializer.Deserialize<Dictionary<string, object>>(expectedBodyParamsJson)!;
|
||||||
|
}
|
||||||
|
|
||||||
TestHelpers.ConfigureRestClient(_client, response, System.Net.HttpStatusCode.OK);
|
TestHelpers.ConfigureRestClient(_client, response, System.Net.HttpStatusCode.OK);
|
||||||
var result = await methodInvoke(_client).ConfigureAwait(false);
|
var result = await methodInvoke(_client).ConfigureAwait(false);
|
||||||
@@ -250,6 +279,35 @@ namespace CryptoExchange.Net.Testing
|
|||||||
if (expectedPath != result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0])
|
if (expectedPath != result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0])
|
||||||
throw new Exception(name + $" path not matched. Expected: {expectedPath}, Actual: {result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0]}");
|
throw new Exception(name + $" path not matched. Expected: {expectedPath}, Actual: {result.RequestUrl!.Replace(_baseAddress, "").Split(new char[] { '?' })[0]}");
|
||||||
|
|
||||||
|
if (expectedUriParams != null)
|
||||||
|
{
|
||||||
|
// Validate request parameters
|
||||||
|
var urlParamsSplit = result.RequestUrl!.Split(new char[] { '?' });
|
||||||
|
var urlParametersString = urlParamsSplit.Length > 1 ? urlParamsSplit[1] : null;
|
||||||
|
var urlParameters = (urlParametersString != null
|
||||||
|
? urlParametersString.Split('&').ToDictionary(x => x.Split('=')[0], x => (object)x.Split('=')[1])
|
||||||
|
: new());
|
||||||
|
|
||||||
|
CompareParameters(expectedUriParams, urlParameters, ignoreParamValidation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expectedBodyParams != null)
|
||||||
|
{
|
||||||
|
// Validate request body
|
||||||
|
Dictionary<string, object> bodyParameters;
|
||||||
|
if (result.RequestBody!.StartsWith("{") || result.RequestBody.StartsWith("["))
|
||||||
|
{
|
||||||
|
bodyParameters = JsonSerializer.Deserialize<Dictionary<string, object>>(result.RequestBody!)!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var splitKvp = result.RequestBody.Split('&');
|
||||||
|
bodyParameters = splitKvp.Select(x => x.Split('=')).ToDictionary(x => x[0], x => (object)x[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
CompareParameters(expectedBodyParams, bodyParameters, ignoreParamValidation);
|
||||||
|
}
|
||||||
|
|
||||||
Trace.Listeners.Remove(listener);
|
Trace.Listeners.Remove(listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,6 +86,9 @@ Make a one time donation in a crypto currency of your choice. If you prefer to d
|
|||||||
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
Alternatively, sponsor me on Github using [Github Sponsors](https://github.com/sponsors/JKorf).
|
||||||
|
|
||||||
## Release notes
|
## Release notes
|
||||||
|
* Version 11.2.1 - 02 Jun 2026
|
||||||
|
* Fixed some testing logic
|
||||||
|
|
||||||
* Version 11.2.0 - 26 May 2026
|
* Version 11.2.0 - 26 May 2026
|
||||||
* Added RateLimitGroup client option to allow for specifying different rate limiting groups
|
* Added RateLimitGroup client option to allow for specifying different rate limiting groups
|
||||||
* Added request parameter checking in RestRequestValidator
|
* Added request parameter checking in RestRequestValidator
|
||||||
|
|||||||
Reference in New Issue
Block a user