1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-09 00:46:19 +00:00

Added CallResult tests, fixed response time not set

This commit is contained in:
Jan Korf 2022-01-15 15:23:52 +01:00
parent 8f6e853e13
commit a37a2d6e31
5 changed files with 266 additions and 20 deletions

View File

@ -0,0 +1,176 @@
using CryptoExchange.Net.Objects;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace CryptoExchange.Net.UnitTests
{
[TestFixture()]
internal class CallResultTests
{
[Test]
public void TestBasicErrorCallResult()
{
var result = new CallResult(new ServerError("TestError"));
Assert.AreEqual(result.Error.Message, "TestError");
Assert.IsFalse(result);
Assert.IsFalse(result.Success);
}
[Test]
public void TestBasicSuccessCallResult()
{
var result = new CallResult(null);
Assert.IsNull(result.Error);
Assert.IsTrue(result);
Assert.IsTrue(result.Success);
}
[Test]
public void TestCallResultError()
{
var result = new CallResult<object>(new ServerError("TestError"));
Assert.AreEqual(result.Error.Message, "TestError");
Assert.IsNull(result.Data);
Assert.IsFalse(result);
Assert.IsFalse(result.Success);
}
[Test]
public void TestCallResultSuccess()
{
var result = new CallResult<object>(new object());
Assert.IsNull(result.Error);
Assert.IsNotNull(result.Data);
Assert.IsTrue(result);
Assert.IsTrue(result.Success);
}
[Test]
public void TestCallResultSuccessAs()
{
var result = new CallResult<TestObjectResult>(new TestObjectResult());
var asResult = result.As<TestObject2>(result.Data.InnerData);
Assert.IsNull(asResult.Error);
Assert.IsNotNull(asResult.Data);
Assert.IsTrue(asResult.Data is TestObject2);
Assert.IsTrue(asResult);
Assert.IsTrue(asResult.Success);
}
[Test]
public void TestCallResultErrorAs()
{
var result = new CallResult<TestObjectResult>(new ServerError("TestError"));
var asResult = result.As<TestObject2>(default);
Assert.IsNotNull(asResult.Error);
Assert.AreEqual(asResult.Error.Message, "TestError");
Assert.IsNull(asResult.Data);
Assert.IsFalse(asResult);
Assert.IsFalse(asResult.Success);
}
[Test]
public void TestCallResultErrorAsError()
{
var result = new CallResult<TestObjectResult>(new ServerError("TestError"));
var asResult = result.AsError<TestObject2>(new ServerError("TestError2"));
Assert.IsNotNull(asResult.Error);
Assert.AreEqual(asResult.Error.Message, "TestError2");
Assert.IsNull(asResult.Data);
Assert.IsFalse(asResult);
Assert.IsFalse(asResult.Success);
}
[Test]
public void TestWebCallResultErrorAsError()
{
var result = new WebCallResult<TestObjectResult>(new ServerError("TestError"));
var asResult = result.AsError<TestObject2>(new ServerError("TestError2"));
Assert.IsNotNull(asResult.Error);
Assert.AreEqual(asResult.Error.Message, "TestError2");
Assert.IsNull(asResult.Data);
Assert.IsFalse(asResult);
Assert.IsFalse(asResult.Success);
}
[Test]
public void TestWebCallResultSuccessAsError()
{
var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK,
new List<KeyValuePair<string, IEnumerable<string>>>(),
TimeSpan.FromSeconds(1),
"{}",
"https://test.com/api",
null,
HttpMethod.Get,
new List<KeyValuePair<string, IEnumerable<string>>>(),
new TestObjectResult(),
null);
var asResult = result.AsError<TestObject2>(new ServerError("TestError2"));
Assert.IsNotNull(asResult.Error);
Assert.AreEqual(asResult.Error.Message, "TestError2");
Assert.AreEqual(asResult.ResponseStatusCode, System.Net.HttpStatusCode.OK);
Assert.AreEqual(asResult.ResponseTime, TimeSpan.FromSeconds(1));
Assert.AreEqual(asResult.RequestUrl, "https://test.com/api");
Assert.AreEqual(asResult.RequestMethod, HttpMethod.Get);
Assert.IsNull(asResult.Data);
Assert.IsFalse(asResult);
Assert.IsFalse(asResult.Success);
}
[Test]
public void TestWebCallResultSuccessAsSuccess()
{
var result = new WebCallResult<TestObjectResult>(
System.Net.HttpStatusCode.OK,
new List<KeyValuePair<string, IEnumerable<string>>>(),
TimeSpan.FromSeconds(1),
"{}",
"https://test.com/api",
null,
HttpMethod.Get,
new List<KeyValuePair<string, IEnumerable<string>>>(),
new TestObjectResult(),
null);
var asResult = result.As<TestObject2>(result.Data.InnerData);
Assert.IsNull(asResult.Error);
Assert.AreEqual(asResult.ResponseStatusCode, System.Net.HttpStatusCode.OK);
Assert.AreEqual(asResult.ResponseTime, TimeSpan.FromSeconds(1));
Assert.AreEqual(asResult.RequestUrl, "https://test.com/api");
Assert.AreEqual(asResult.RequestMethod, HttpMethod.Get);
Assert.IsNotNull(asResult.Data);
Assert.IsTrue(asResult);
Assert.IsTrue(asResult.Success);
}
}
public class TestObjectResult
{
public TestObject2 InnerData;
public TestObjectResult()
{
InnerData = new TestObject2();
}
}
public class TestObject2
{
}
}

View File

@ -72,6 +72,7 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
typeof(HttpRequestException).GetField("_message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(we, message); typeof(HttpRequestException).GetField("_message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(we, message);
var request = new Mock<IRequest>(); var request = new Mock<IRequest>();
request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
request.Setup(c => c.GetHeaders()).Returns(new Dictionary<string, IEnumerable<string>>()); request.Setup(c => c.GetHeaders()).Returns(new Dictionary<string, IEnumerable<string>>());
request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we); request.Setup(c => c.GetResponseAsync(It.IsAny<CancellationToken>())).Throws(we);

View File

@ -196,16 +196,16 @@ namespace CryptoExchange.Net
// Validate if it is valid json. Sometimes other data will be returned, 502 error html pages for example // Validate if it is valid json. Sometimes other data will be returned, 502 error html pages for example
var parseResult = ValidateJson(data); var parseResult = ValidateJson(data);
if (!parseResult.Success) if (!parseResult.Success)
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, ClientOptions.OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, ClientOptions.OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, parseResult.Error!);
// Let the library implementation see if it is an error response, and if so parse the error // Let the library implementation see if it is an error response, and if so parse the error
var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false); var error = await TryParseErrorAsync(parseResult.Data).ConfigureAwait(false);
if (error != null) if (error != null)
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, ClientOptions.OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, ClientOptions.OutputOriginalData ? data : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error!);
// Not an error, so continue deserializing // Not an error, so continue deserializing
var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId); var deserializeResult = Deserialize<T>(parseResult.Data, deserializer, request.RequestId);
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, ClientOptions.OutputOriginalData ? data: null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, sw.Elapsed, ClientOptions.OutputOriginalData ? data: null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), deserializeResult.Data, deserializeResult.Error);
} }
else else
{ {
@ -214,7 +214,7 @@ namespace CryptoExchange.Net
responseStream.Close(); responseStream.Close();
response.Close(); response.Close();
return new WebCallResult<T>(statusCode, headers, ClientOptions.OutputOriginalData ? desResult.OriginalData : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error); return new WebCallResult<T>(statusCode, headers, sw.Elapsed, ClientOptions.OutputOriginalData ? desResult.OriginalData : null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), desResult.Data, desResult.Error);
} }
} }
else else
@ -229,7 +229,7 @@ namespace CryptoExchange.Net
var error = parseResult.Success ? ParseErrorResponse(parseResult.Data) : parseResult.Error!; var error = parseResult.Success ? ParseErrorResponse(parseResult.Data) : parseResult.Error!;
if(error.Code == null || error.Code == 0) if(error.Code == null || error.Code == 0)
error.Code = (int)response.StatusCode; error.Code = (int)response.StatusCode;
return new WebCallResult<T>(statusCode, headers, data, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error); return new WebCallResult<T>(statusCode, headers, sw.Elapsed, data, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, error);
} }
} }
catch (HttpRequestException requestException) catch (HttpRequestException requestException)
@ -237,7 +237,7 @@ namespace CryptoExchange.Net
// Request exception, can't reach server for instance // Request exception, can't reach server for instance
var exceptionInfo = requestException.ToLogString(); var exceptionInfo = requestException.ToLogString();
log.Write(LogLevel.Warning, $"[{request.RequestId}] Request exception: " + exceptionInfo); log.Write(LogLevel.Warning, $"[{request.RequestId}] Request exception: " + exceptionInfo);
return new WebCallResult<T>(null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo)); return new WebCallResult<T>(null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError(exceptionInfo));
} }
catch (OperationCanceledException canceledException) catch (OperationCanceledException canceledException)
{ {
@ -245,13 +245,13 @@ namespace CryptoExchange.Net
{ {
// Cancellation token canceled by caller // Cancellation token canceled by caller
log.Write(LogLevel.Warning, $"[{request.RequestId}] Request canceled by cancellation token"); log.Write(LogLevel.Warning, $"[{request.RequestId}] Request canceled by cancellation token");
return new WebCallResult<T>(null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError()); return new WebCallResult<T>(null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new CancellationRequestedError());
} }
else else
{ {
// Request timed out // Request timed out
log.Write(LogLevel.Warning, $"[{request.RequestId}] Request timed out: " + canceledException.ToLogString()); log.Write(LogLevel.Warning, $"[{request.RequestId}] Request timed out: " + canceledException.ToLogString());
return new WebCallResult<T>(null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"[{request.RequestId}] Request timed out")); return new WebCallResult<T>(null, null, null, null, request.Uri.ToString(), request.Content, request.Method, request.GetHeaders(), default, new WebError($"[{request.RequestId}] Request timed out"));
} }
} }
} }

View File

@ -68,7 +68,7 @@ namespace CryptoExchange.Net
if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < TimeSpan.FromHours(1))) if (!timeSyncParams.SyncTime || (DateTime.UtcNow - timeSyncParams.TimeSyncState.LastSyncTime < TimeSpan.FromHours(1)))
{ {
timeSyncParams.TimeSyncState.Semaphore.Release(); timeSyncParams.TimeSyncState.Semaphore.Release();
return new WebCallResult<bool>(null, null, null, null, null, null, null, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, true, null);
} }
var localTime = DateTime.UtcNow; var localTime = DateTime.UtcNow;
@ -106,7 +106,7 @@ namespace CryptoExchange.Net
} }
} }
return new WebCallResult<bool>(null, null, null, null, null, null, null, true, null); return new WebCallResult<bool>(null, null, null, null, null, null, null, null, true, null);
} }
} }
} }

View File

@ -145,6 +145,26 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
public class WebCallResult : CallResult public class WebCallResult : CallResult
{ {
/// <summary>
/// The request http method
/// </summary>
public HttpMethod? RequestMethod { get; set; }
/// <summary>
/// The headers sent with the request
/// </summary>
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? RequestHeaders { get; set; }
/// <summary>
/// The url which was requested
/// </summary>
public string? RequestUrl { get; set; }
/// <summary>
/// The body of the request
/// </summary>
public string? RequestBody { get; set; }
/// <summary> /// <summary>
/// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this. /// The status code of the response. Note that a OK status does not always indicate success, check the Success parameter for this.
/// </summary> /// </summary>
@ -155,21 +175,48 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? ResponseHeaders { get; set; } public IEnumerable<KeyValuePair<string, IEnumerable<string>>>? ResponseHeaders { get; set; }
/// <summary>
/// The time between sending the request and receiving the response
/// </summary>
public TimeSpan? ResponseTime { get; set; }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
/// <param name="code">Status code</param> /// <param name="code"></param>
/// <param name="responseHeaders">Response headers</param> /// <param name="responseHeaders"></param>
/// <param name="error">Error</param> /// <param name="responseTime"></param>
private WebCallResult( /// <param name="requestUrl"></param>
/// <param name="requestBody"></param>
/// <param name="requestMethod"></param>
/// <param name="requestHeaders"></param>
/// <param name="error"></param>
public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
TimeSpan? responseTime,
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? requestHeaders,
Error? error) : base(error) Error? error) : base(error)
{ {
ResponseHeaders = responseHeaders;
ResponseStatusCode = code; ResponseStatusCode = code;
ResponseHeaders = responseHeaders;
ResponseTime = responseTime;
RequestUrl = requestUrl;
RequestBody = requestBody;
RequestHeaders = requestHeaders;
RequestMethod = requestMethod;
} }
/// <summary>
/// ctor
/// </summary>
/// <param name="error"></param>
public WebCallResult(Error error): base(error) { }
/// <summary> /// <summary>
/// Return the result as an error result /// Return the result as an error result
/// </summary> /// </summary>
@ -177,7 +224,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public WebCallResult AsError(Error error) public WebCallResult AsError(Error error)
{ {
return new WebCallResult(ResponseStatusCode, ResponseHeaders, error); return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
} }
@ -227,6 +274,7 @@ namespace CryptoExchange.Net.Objects
/// </summary> /// </summary>
/// <param name="code"></param> /// <param name="code"></param>
/// <param name="responseHeaders"></param> /// <param name="responseHeaders"></param>
/// <param name="responseTime"></param>
/// <param name="originalData"></param> /// <param name="originalData"></param>
/// <param name="requestUrl"></param> /// <param name="requestUrl"></param>
/// <param name="requestBody"></param> /// <param name="requestBody"></param>
@ -237,6 +285,7 @@ namespace CryptoExchange.Net.Objects
public WebCallResult( public WebCallResult(
HttpStatusCode? code, HttpStatusCode? code,
IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders, IEnumerable<KeyValuePair<string, IEnumerable<string>>>? responseHeaders,
TimeSpan? responseTime,
string? originalData, string? originalData,
string? requestUrl, string? requestUrl,
string? requestBody, string? requestBody,
@ -247,6 +296,8 @@ namespace CryptoExchange.Net.Objects
{ {
ResponseStatusCode = code; ResponseStatusCode = code;
ResponseHeaders = responseHeaders; ResponseHeaders = responseHeaders;
ResponseTime = responseTime;
RequestUrl = requestUrl; RequestUrl = requestUrl;
RequestBody = requestBody; RequestBody = requestBody;
RequestHeaders = requestHeaders; RequestHeaders = requestHeaders;
@ -257,7 +308,7 @@ namespace CryptoExchange.Net.Objects
/// Create a new error result /// Create a new error result
/// </summary> /// </summary>
/// <param name="error">The error</param> /// <param name="error">The error</param>
public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, default, error) { } public WebCallResult(Error? error) : this(null, null, null, null, null, null, null, null, default, error) { }
/// <summary> /// <summary>
/// Copy the WebCallResult to a new data type /// Copy the WebCallResult to a new data type
@ -267,7 +318,25 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> As<K>([AllowNull] K data) public new WebCallResult<K> As<K>([AllowNull] K data)
{ {
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error); return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, data, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public WebCallResult AsDataless()
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, Error);
}
/// <summary>
/// Copy as a dataless result
/// </summary>
/// <returns></returns>
public WebCallResult AsDatalessError(Error error)
{
return new WebCallResult(ResponseStatusCode, ResponseHeaders, ResponseTime, RequestUrl, RequestBody, RequestMethod, RequestHeaders, error);
} }
/// <summary> /// <summary>
@ -278,7 +347,7 @@ namespace CryptoExchange.Net.Objects
/// <returns></returns> /// <returns></returns>
public new WebCallResult<K> AsError<K>(Error error) public new WebCallResult<K> AsError<K>(Error error)
{ {
return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error); return new WebCallResult<K>(ResponseStatusCode, ResponseHeaders, ResponseTime, OriginalData, RequestUrl, RequestBody, RequestMethod, RequestHeaders, default, error);
} }
} }
} }