mirror of
https://github.com/JKorf/CryptoExchange.Net
synced 2025-06-08 16:36:15 +00:00
Unit tests updated
This commit is contained in:
parent
64a66d4206
commit
b4d45b4194
@ -1,227 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CryptoExchange.Net.Authentication;
|
|
||||||
using CryptoExchange.Net.Interfaces;
|
|
||||||
using CryptoExchange.Net.Logging;
|
|
||||||
using CryptoExchange.Net.Objects;
|
|
||||||
using CryptoExchange.Net.RateLimiter;
|
|
||||||
using Moq;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using NUnit.Framework;
|
|
||||||
|
|
||||||
namespace CryptoExchange.Net.UnitTests
|
|
||||||
{
|
|
||||||
[TestFixture()]
|
|
||||||
public class ExchangeClientTests
|
|
||||||
{
|
|
||||||
//[TestCase(null, null)]
|
|
||||||
//[TestCase("", "")]
|
|
||||||
//[TestCase("test", null)]
|
|
||||||
//[TestCase("test", "")]
|
|
||||||
//[TestCase(null, "test")]
|
|
||||||
//[TestCase("", "test")]
|
|
||||||
//public void SettingEmptyValuesForAPICredentials_Should_ThrowException(string key, string secret)
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var client = PrepareClient("");
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// // assert
|
|
||||||
// Assert.Throws(typeof(ArgumentException), () => client.SetApiCredentails(key, secret));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void SettingLogOutput_Should_RedirectLogOutput()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var stringBuilder = new StringBuilder();
|
|
||||||
// var client = PrepareClient("{}", true, LogVerbosity.Debug, new StringWriter(stringBuilder));
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// client.TestCall();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsFalse(string.IsNullOrEmpty(stringBuilder.ToString()));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void ObjectDeserializationFail_Should_GiveFailedResult()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var errorMessage = "TestErrorMessage";
|
|
||||||
// var client = PrepareClient(JsonConvert.SerializeObject(errorMessage));
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var result = client.TestCall();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsFalse(result.Success);
|
|
||||||
// Assert.AreNotEqual(0, result.Error.Code);
|
|
||||||
// Assert.IsTrue(result.Error.Message.Contains(errorMessage));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void InvalidJson_Should_GiveFailedResult()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var errorMessage = "TestErrorMessage";
|
|
||||||
// var client = PrepareClient(JsonConvert.SerializeObject(errorMessage));
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var result = client.TestCall();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsFalse(result.Success);
|
|
||||||
// Assert.AreNotEqual(0, result.Error.Code);
|
|
||||||
// Assert.IsTrue(result.Error.Message.Contains(errorMessage));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void WhenUsingRateLimiterTotalRequests_Should_BeDelayed()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var client = PrepareClient(JsonConvert.SerializeObject(new TestObject()));
|
|
||||||
// client.AddRateLimiter(new RateLimiterTotal(1, TimeSpan.FromSeconds(5)));
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var sw = Stopwatch.StartNew();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// sw.Stop();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsTrue(sw.ElapsedMilliseconds > 9000);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void WhenUsingRateLimiterPerEndpointRequests_Should_BeDelayed()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var client = PrepareClient(JsonConvert.SerializeObject(new TestObject()));
|
|
||||||
// client.AddRateLimiter(new RateLimiterTotal(1, TimeSpan.FromSeconds(5)));
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var sw = Stopwatch.StartNew();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// sw.Stop();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsTrue(sw.ElapsedMilliseconds > 9000);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void WhenRemovingRateLimiterRequest_Should_NoLongerBeDelayed()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var client = PrepareClient(JsonConvert.SerializeObject(new TestObject()));
|
|
||||||
// client.AddRateLimiter(new RateLimiterTotal(1, TimeSpan.FromSeconds(5)));
|
|
||||||
// client.RemoveRateLimiters();
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var sw = Stopwatch.StartNew();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// client.TestCall();
|
|
||||||
// sw.Stop();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsTrue(sw.ElapsedMilliseconds < 5000);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//[TestCase()]
|
|
||||||
//public void ReceivingErrorStatusCode_Should_NotSuccess()
|
|
||||||
//{
|
|
||||||
// // arrange
|
|
||||||
// var client = PrepareExceptionClient(JsonConvert.SerializeObject(new TestObject()), "InvalidStatusCodeResponse", 203);
|
|
||||||
|
|
||||||
// // act
|
|
||||||
// var result = client.TestCall();
|
|
||||||
|
|
||||||
// // assert
|
|
||||||
// Assert.IsFalse(result.Success);
|
|
||||||
// Assert.IsNotNull(result.Error);
|
|
||||||
// Assert.IsTrue(result.Error.Message.Contains("InvalidStatusCodeResponse"));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private TestRestClient PrepareClient(string responseData, bool withOptions = true, LogVerbosity verbosity = LogVerbosity.Warning, TextWriter tw = null)
|
|
||||||
//{
|
|
||||||
// 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.GetResponseStream()).Returns(responseStream);
|
|
||||||
|
|
||||||
// var request = new Mock<IRequest>();
|
|
||||||
// request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
|
|
||||||
// request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
|
|
||||||
// request.Setup(c => c.GetResponse()).Returns(Task.FromResult(response.Object));
|
|
||||||
|
|
||||||
// var factory = new Mock<IRequestFactory>();
|
|
||||||
// factory.Setup(c => c.Create(It.IsAny<string>()))
|
|
||||||
// .Returns(request.Object);
|
|
||||||
// TestRestClient client;
|
|
||||||
// if (withOptions)
|
|
||||||
// {
|
|
||||||
// var options = new ClientOptions()
|
|
||||||
// {
|
|
||||||
// ApiCredentials = new ApiCredentials("Test", "Test2"),
|
|
||||||
// LogVerbosity = verbosity
|
|
||||||
// };
|
|
||||||
// if (tw != null)
|
|
||||||
// options.LogWriters = new List<TextWriter>() { tw };
|
|
||||||
|
|
||||||
// client = new TestRestClient(options);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// client = new TestRestClient();
|
|
||||||
// }
|
|
||||||
// client.RequestFactory = factory.Object;
|
|
||||||
// return client;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private TestRestClient PrepareExceptionClient(string responseData, string exceptionMessage, int statusCode, bool credentials = true)
|
|
||||||
//{
|
|
||||||
// var expectedBytes = Encoding.UTF8.GetBytes(responseData);
|
|
||||||
// var responseStream = new MemoryStream();
|
|
||||||
// responseStream.Write(expectedBytes, 0, expectedBytes.Length);
|
|
||||||
// responseStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
// var we = new WebException();
|
|
||||||
// var r = new HttpWebResponse();
|
|
||||||
// var re = new HttpResponseMessage();
|
|
||||||
|
|
||||||
// typeof(HttpResponseMessage).GetField("_statusCode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(re, (HttpStatusCode)statusCode);
|
|
||||||
// typeof(HttpWebResponse).GetField("_httpResponseMessage", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(r, re);
|
|
||||||
// typeof(WebException).GetField("_message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(we, exceptionMessage);
|
|
||||||
// typeof(WebException).GetField("_response", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).SetValue(we, r);
|
|
||||||
|
|
||||||
// var response = new Mock<IResponse>();
|
|
||||||
// response.Setup(c => c.GetResponseStream()).Throws(we);
|
|
||||||
|
|
||||||
// var request = new Mock<IRequest>();
|
|
||||||
// request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
|
|
||||||
// request.Setup(c => c.GetResponse()).Returns(Task.FromResult(response.Object));
|
|
||||||
|
|
||||||
// var factory = new Mock<IRequestFactory>();
|
|
||||||
// factory.Setup(c => c.Create(It.IsAny<string>()))
|
|
||||||
// .Returns(request.Object);
|
|
||||||
|
|
||||||
// TestRestClient client = credentials ? new TestRestClient(new ClientOptions() { ApiCredentials = new ApiCredentials("Test", "Test2") }) : new TestRestClient();
|
|
||||||
// client.RequestFactory = factory.Object;
|
|
||||||
// return client;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,8 +6,12 @@ using Newtonsoft.Json;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using CryptoExchange.Net.RateLimiter;
|
||||||
|
|
||||||
namespace CryptoExchange.Net.UnitTests
|
namespace CryptoExchange.Net.UnitTests
|
||||||
{
|
{
|
||||||
@ -94,5 +98,72 @@ namespace CryptoExchange.Net.UnitTests
|
|||||||
Assert.IsTrue(result.Error.Code == 123);
|
Assert.IsTrue(result.Error.Code == 123);
|
||||||
Assert.IsTrue(result.Error.Message == "Invalid request");
|
Assert.IsTrue(result.Error.Message == "Invalid request");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SettingOptions_Should_ResultInOptionsSet()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
// act
|
||||||
|
var client = new TestRestClient(new ClientOptions()
|
||||||
|
{
|
||||||
|
BaseAddress = "http://test.address.com",
|
||||||
|
RateLimiters = new List<IRateLimiter>{new RateLimiterTotal(1, TimeSpan.FromSeconds(1))},
|
||||||
|
RateLimitingBehaviour = RateLimitingBehaviour.Fail
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(client.BaseAddress == "http://test.address.com");
|
||||||
|
Assert.IsTrue(client.RateLimiters.Count() == 1);
|
||||||
|
Assert.IsTrue(client.RateLimitBehaviour == RateLimitingBehaviour.Fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SettingRateLimitingBehaviourToFail_Should_FailLimitedRequests()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestRestClient(new ClientOptions()
|
||||||
|
{
|
||||||
|
RateLimiters = new List<IRateLimiter> { new RateLimiterTotal(1, TimeSpan.FromSeconds(1)) },
|
||||||
|
RateLimitingBehaviour = RateLimitingBehaviour.Fail
|
||||||
|
});
|
||||||
|
client.SetResponse("{\"property\": 123}");
|
||||||
|
|
||||||
|
|
||||||
|
// act
|
||||||
|
var result1 = client.Request<TestObject>().Result;
|
||||||
|
client.SetResponse("{\"property\": 123}");
|
||||||
|
var result2 = client.Request<TestObject>().Result;
|
||||||
|
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result1.Success);
|
||||||
|
Assert.IsFalse(result2.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SettingRateLimitingBehaviourToWait_Should_DelayLimitedRequests()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestRestClient(new ClientOptions()
|
||||||
|
{
|
||||||
|
RateLimiters = new List<IRateLimiter> { new RateLimiterTotal(1, TimeSpan.FromSeconds(1)) },
|
||||||
|
RateLimitingBehaviour = RateLimitingBehaviour.Wait
|
||||||
|
});
|
||||||
|
client.SetResponse("{\"property\": 123}");
|
||||||
|
|
||||||
|
|
||||||
|
// act
|
||||||
|
var sw = Stopwatch.StartNew();
|
||||||
|
var result1 = client.Request<TestObject>().Result;
|
||||||
|
client.SetResponse("{\"property\": 123}"); // reset response stream
|
||||||
|
var result2 = client.Request<TestObject>().Result;
|
||||||
|
sw.Stop();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(result1.Success);
|
||||||
|
Assert.IsTrue(result2.Success);
|
||||||
|
Assert.IsTrue(sw.ElapsedMilliseconds > 900, $"Actual: {sw.ElapsedMilliseconds}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
177
CryptoExchange.Net.UnitTests/SocketClientTests.cs
Normal file
177
CryptoExchange.Net.UnitTests/SocketClientTests.cs
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using CryptoExchange.Net.Logging;
|
||||||
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Sockets;
|
||||||
|
using CryptoExchange.Net.UnitTests.TestImplementations;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.UnitTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class SocketClientTests
|
||||||
|
{
|
||||||
|
[TestCase]
|
||||||
|
public void SettingOptions_Should_ResultInOptionsSet()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
// act
|
||||||
|
var client = new TestSocketClient(new SocketClientOptions()
|
||||||
|
{
|
||||||
|
BaseAddress = "http://test.address.com",
|
||||||
|
ReconnectInterval = TimeSpan.FromSeconds(6)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(client.BaseAddress == "http://test.address.com");
|
||||||
|
Assert.IsTrue(client.ReconnectInterval.TotalSeconds == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(true)]
|
||||||
|
[TestCase(false)]
|
||||||
|
public void ConnectSocket_Should_ReturnConnectionResult(bool canConnect)
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestSocketClient();
|
||||||
|
var socket = client.CreateSocket();
|
||||||
|
socket.CanConnect = canConnect;
|
||||||
|
|
||||||
|
// act
|
||||||
|
var connectResult = client.ConnectSocketSub(new SocketSubscription(socket));
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(connectResult.Success == canConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SocketMessages_Should_BeProcessedInDataHandlers()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestSocketClient(new SocketClientOptions() { ReconnectInterval = TimeSpan.Zero, LogVerbosity = LogVerbosity.Debug });
|
||||||
|
var socket = client.CreateSocket();
|
||||||
|
socket.ShouldReconnect = true;
|
||||||
|
socket.CanConnect = true;
|
||||||
|
socket.DisconnectTime = DateTime.UtcNow;
|
||||||
|
var sub = new SocketSubscription(socket);
|
||||||
|
var rstEvent = new ManualResetEvent(false);
|
||||||
|
JToken result = null;
|
||||||
|
sub.MessageHandlers.Add("TestHandler", (subs, data) =>
|
||||||
|
{
|
||||||
|
result = data;
|
||||||
|
rstEvent.Set();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
});
|
||||||
|
client.ConnectSocketSub(sub);
|
||||||
|
|
||||||
|
// act
|
||||||
|
socket.InvokeMessage("{\"property\": 123}");
|
||||||
|
rstEvent.WaitOne(1000);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue((int)result["property"] == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SocketMessages_Should_NotBeProcessedInSubsequentHandlersIfHandlerReturnsTrue()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestSocketClient(new SocketClientOptions() { ReconnectInterval = TimeSpan.Zero, LogVerbosity = LogVerbosity.Debug });
|
||||||
|
var socket = client.CreateSocket();
|
||||||
|
socket.ShouldReconnect = true;
|
||||||
|
socket.CanConnect = true;
|
||||||
|
socket.DisconnectTime = DateTime.UtcNow;
|
||||||
|
var sub = new SocketSubscription(socket);
|
||||||
|
var rstEvent1 = new ManualResetEvent(false);
|
||||||
|
var rstEvent2 = new ManualResetEvent(false);
|
||||||
|
JToken result1 = null;
|
||||||
|
JToken result2 = null;
|
||||||
|
sub.MessageHandlers.Add("TestHandler", (subs, data) =>
|
||||||
|
{
|
||||||
|
result1 = data;
|
||||||
|
rstEvent1.Set();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
sub.MessageHandlers.Add("TestHandlerNotHit", (subs, data) =>
|
||||||
|
{
|
||||||
|
result2 = data;
|
||||||
|
rstEvent2.Set();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
client.ConnectSocketSub(sub);
|
||||||
|
|
||||||
|
// act
|
||||||
|
socket.InvokeMessage("{\"property\": 123}");
|
||||||
|
rstEvent1.WaitOne(100);
|
||||||
|
rstEvent2.WaitOne(100);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue((int)result1["property"] == 123);
|
||||||
|
Assert.IsTrue(result2 == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void SocketMessages_Should_BeProcessedInSubsequentHandlersIfHandlerReturnsFalse()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var client = new TestSocketClient(new SocketClientOptions() { ReconnectInterval = TimeSpan.Zero, LogVerbosity = LogVerbosity.Debug });
|
||||||
|
var socket = client.CreateSocket();
|
||||||
|
socket.ShouldReconnect = true;
|
||||||
|
socket.CanConnect = true;
|
||||||
|
socket.DisconnectTime = DateTime.UtcNow;
|
||||||
|
var sub = new SocketSubscription(socket);
|
||||||
|
var rstEvent = new ManualResetEvent(false);
|
||||||
|
JToken result = null;
|
||||||
|
sub.MessageHandlers.Add("TestHandlerNotProcessing", (subs, data) =>
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
sub.MessageHandlers.Add("TestHandler", (subs, data) =>
|
||||||
|
{
|
||||||
|
result = data;
|
||||||
|
rstEvent.Set();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
client.ConnectSocketSub(sub);
|
||||||
|
|
||||||
|
// act
|
||||||
|
socket.InvokeMessage("{\"property\": 123}");
|
||||||
|
rstEvent.WaitOne(100);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue((int)result["property"] == 123);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[TestCase]
|
||||||
|
public void DisconnectedSocket_Should_Reconnect()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
bool reconnected = false;
|
||||||
|
var client = new TestSocketClient(new SocketClientOptions(){ReconnectInterval = TimeSpan.Zero ,LogVerbosity = LogVerbosity.Debug});
|
||||||
|
var socket = client.CreateSocket();
|
||||||
|
socket.ShouldReconnect = true;
|
||||||
|
socket.CanConnect = true;
|
||||||
|
socket.DisconnectTime = DateTime.UtcNow;
|
||||||
|
var sub = new SocketSubscription(socket);
|
||||||
|
client.ConnectSocketSub(sub);
|
||||||
|
var rstEvent = new ManualResetEvent(false);
|
||||||
|
client.OnReconnect += () =>
|
||||||
|
{
|
||||||
|
reconnected = true;
|
||||||
|
rstEvent.Set();
|
||||||
|
};
|
||||||
|
|
||||||
|
// act
|
||||||
|
socket.InvokeClose();
|
||||||
|
rstEvent.WaitOne(1000);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.IsTrue(reconnected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,9 +22,10 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
|||||||
|
|
||||||
public TestRestClient(ClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials))
|
public TestRestClient(ClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials))
|
||||||
{
|
{
|
||||||
|
RequestFactory = new Mock<IRequestFactory>().Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetResponse(string responseData)
|
public void SetResponse(string responseData, Stream requestStream = null)
|
||||||
{
|
{
|
||||||
var expectedBytes = Encoding.UTF8.GetBytes(responseData);
|
var expectedBytes = Encoding.UTF8.GetBytes(responseData);
|
||||||
var responseStream = new MemoryStream();
|
var responseStream = new MemoryStream();
|
||||||
@ -37,6 +38,7 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
|||||||
var request = new Mock<IRequest>();
|
var request = new Mock<IRequest>();
|
||||||
request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
|
request.Setup(c => c.Headers).Returns(new WebHeaderCollection());
|
||||||
request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
|
request.Setup(c => c.Uri).Returns(new Uri("http://www.test.com"));
|
||||||
|
request.Setup(c => c.GetRequestStream()).Returns(Task.FromResult(requestStream));
|
||||||
request.Setup(c => c.GetResponse()).Returns(Task.FromResult(response.Object));
|
request.Setup(c => c.GetResponse()).Returns(Task.FromResult(response.Object));
|
||||||
|
|
||||||
var factory = Mock.Get(RequestFactory);
|
var factory = Mock.Get(RequestFactory);
|
||||||
@ -84,9 +86,9 @@ namespace CryptoExchange.Net.UnitTests.TestImplementations
|
|||||||
.Returns(request.Object);
|
.Returns(request.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CallResult<T>> Request<T>() where T:class
|
public async Task<CallResult<T>> Request<T>(string method = "GET") where T:class
|
||||||
{
|
{
|
||||||
return await ExecuteRequest<T>(new Uri("http://www.test.com"));
|
return await ExecuteRequest<T>(new Uri("http://www.test.com"), method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Security.Authentication;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using WebSocket4Net;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||||
|
{
|
||||||
|
public class TestSocket: IWebsocket
|
||||||
|
{
|
||||||
|
public bool CanConnect { get; set; }
|
||||||
|
public bool Connected { get; set; }
|
||||||
|
|
||||||
|
public event Action OnClose;
|
||||||
|
public event Action<string> OnMessage;
|
||||||
|
public event Action<Exception> OnError;
|
||||||
|
public event Action OnOpen;
|
||||||
|
|
||||||
|
public int Id { get; }
|
||||||
|
public bool ShouldReconnect { get; set; }
|
||||||
|
public Func<byte[], string> DataInterpreter { get; set; }
|
||||||
|
public DateTime? DisconnectTime { get; set; }
|
||||||
|
public string Url { get; }
|
||||||
|
public WebSocketState SocketState { get; }
|
||||||
|
public bool IsClosed => !Connected;
|
||||||
|
public bool IsOpen => Connected;
|
||||||
|
public bool PingConnection { get; set; }
|
||||||
|
public TimeSpan PingInterval { get; set; }
|
||||||
|
public SslProtocols SSLProtocols { get; set; }
|
||||||
|
|
||||||
|
public Task<bool> Connect()
|
||||||
|
{
|
||||||
|
Connected = CanConnect;
|
||||||
|
return Task.FromResult(CanConnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Send(string data)
|
||||||
|
{
|
||||||
|
if(!Connected)
|
||||||
|
throw new Exception("Socket not connected");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Close()
|
||||||
|
{
|
||||||
|
Connected = false;
|
||||||
|
return Task.FromResult(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProxy(string host, int port)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvokeClose()
|
||||||
|
{
|
||||||
|
Connected = false;
|
||||||
|
OnClose?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvokeOpen()
|
||||||
|
{
|
||||||
|
OnOpen?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvokeMessage(string data)
|
||||||
|
{
|
||||||
|
OnMessage?.Invoke(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using System;
|
||||||
|
using CryptoExchange.Net.Interfaces;
|
||||||
|
using CryptoExchange.Net.Logging;
|
||||||
|
using CryptoExchange.Net.Objects;
|
||||||
|
using CryptoExchange.Net.Sockets;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace CryptoExchange.Net.UnitTests.TestImplementations
|
||||||
|
{
|
||||||
|
public class TestSocketClient: SocketClient
|
||||||
|
{
|
||||||
|
public Action OnReconnect { get; set; }
|
||||||
|
|
||||||
|
public TestSocketClient() : this(new SocketClientOptions())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestSocketClient(SocketClientOptions exchangeOptions) : base(exchangeOptions, exchangeOptions.ApiCredentials == null ? null : new TestAuthProvider(exchangeOptions.ApiCredentials))
|
||||||
|
{
|
||||||
|
SocketFactory = new Mock<IWebsocketFactory>().Object;
|
||||||
|
Mock.Get(SocketFactory).Setup(f => f.CreateWebsocket(It.IsAny<Log>(), It.IsAny<string>())).Returns(new TestSocket());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestSocket CreateSocket()
|
||||||
|
{
|
||||||
|
return (TestSocket)CreateSocket(BaseAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CallResult<bool> ConnectSocketSub(SocketSubscription sub)
|
||||||
|
{
|
||||||
|
return ConnectSocket(sub).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool SocketReconnect(SocketSubscription subscription, TimeSpan disconnectedTime)
|
||||||
|
{
|
||||||
|
OnReconnect?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ namespace CryptoExchange.Net
|
|||||||
{
|
{
|
||||||
public abstract class BaseClient
|
public abstract class BaseClient
|
||||||
{
|
{
|
||||||
protected string baseAddress;
|
public string BaseAddress;
|
||||||
protected Log log;
|
protected Log log;
|
||||||
protected ApiProxy apiProxy;
|
protected ApiProxy apiProxy;
|
||||||
protected AuthenticationProvider authProvider;
|
protected AuthenticationProvider authProvider;
|
||||||
@ -46,7 +46,7 @@ namespace CryptoExchange.Net
|
|||||||
log.UpdateWriters(exchangeOptions.LogWriters);
|
log.UpdateWriters(exchangeOptions.LogWriters);
|
||||||
log.Level = exchangeOptions.LogVerbosity;
|
log.Level = exchangeOptions.LogVerbosity;
|
||||||
|
|
||||||
baseAddress = exchangeOptions.BaseAddress;
|
BaseAddress = exchangeOptions.BaseAddress;
|
||||||
apiProxy = exchangeOptions.Proxy;
|
apiProxy = exchangeOptions.Proxy;
|
||||||
if (apiProxy != null)
|
if (apiProxy != null)
|
||||||
log.Write(LogVerbosity.Info, $"Setting api proxy to {exchangeOptions.Proxy.Host}:{exchangeOptions.Proxy.Port}");
|
log.Write(LogVerbosity.Info, $"Setting api proxy to {exchangeOptions.Proxy.Host}:{exchangeOptions.Proxy.Port}");
|
||||||
|
@ -10,7 +10,7 @@ namespace CryptoExchange.Net.Interfaces
|
|||||||
Uri Uri { get; }
|
Uri Uri { get; }
|
||||||
WebHeaderCollection Headers { get; set; }
|
WebHeaderCollection Headers { get; set; }
|
||||||
string Method { get; set; }
|
string Method { get; set; }
|
||||||
int Timeout { get; set; }
|
TimeSpan Timeout { get; set; }
|
||||||
void SetProxy(string host, int port);
|
void SetProxy(string host, int port);
|
||||||
|
|
||||||
string ContentType { get; set; }
|
string ContentType { get; set; }
|
||||||
|
@ -44,10 +44,10 @@ namespace CryptoExchange.Net.Requests
|
|||||||
set => request.Method = value;
|
set => request.Method = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Timeout
|
public TimeSpan Timeout
|
||||||
{
|
{
|
||||||
get => request.Timeout;
|
get => TimeSpan.FromMilliseconds(request.Timeout);
|
||||||
set => request.Timeout = value;
|
set => request.Timeout = (int)Math.Round(value.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri Uri => request.RequestUri;
|
public Uri Uri => request.RequestUri;
|
||||||
|
@ -13,7 +13,6 @@ using CryptoExchange.Net.Authentication;
|
|||||||
using CryptoExchange.Net.Interfaces;
|
using CryptoExchange.Net.Interfaces;
|
||||||
using CryptoExchange.Net.Logging;
|
using CryptoExchange.Net.Logging;
|
||||||
using CryptoExchange.Net.Objects;
|
using CryptoExchange.Net.Objects;
|
||||||
using CryptoExchange.Net.RateLimiter;
|
|
||||||
using CryptoExchange.Net.Requests;
|
using CryptoExchange.Net.Requests;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@ -25,14 +24,14 @@ namespace CryptoExchange.Net
|
|||||||
/// The factory for creating requests. Used for unit testing
|
/// The factory for creating requests. Used for unit testing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
|
public IRequestFactory RequestFactory { get; set; } = new RequestFactory();
|
||||||
|
|
||||||
protected RateLimitingBehaviour rateLimitBehaviour;
|
|
||||||
protected int requestTimeout;
|
|
||||||
|
|
||||||
protected PostParameters postParametersPosition = PostParameters.InBody;
|
protected PostParameters postParametersPosition = PostParameters.InBody;
|
||||||
protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json;
|
protected RequestBodyFormat requestBodyFormat = RequestBodyFormat.Json;
|
||||||
|
|
||||||
private List<IRateLimiter> rateLimiters;
|
protected TimeSpan RequestTimeout { get; private set; }
|
||||||
|
public RateLimitingBehaviour RateLimitBehaviour { get; private set; }
|
||||||
|
public IEnumerable<IRateLimiter> RateLimiters { get; private set; }
|
||||||
|
|
||||||
protected RestClient(ClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
protected RestClient(ClientOptions exchangeOptions, AuthenticationProvider authenticationProvider): base(exchangeOptions, authenticationProvider)
|
||||||
{
|
{
|
||||||
@ -45,11 +44,12 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="exchangeOptions">Options</param>
|
/// <param name="exchangeOptions">Options</param>
|
||||||
protected void Configure(ClientOptions exchangeOptions)
|
protected void Configure(ClientOptions exchangeOptions)
|
||||||
{
|
{
|
||||||
requestTimeout = (int)Math.Round(exchangeOptions.RequestTimeout.TotalMilliseconds, 0);
|
RequestTimeout = exchangeOptions.RequestTimeout;
|
||||||
rateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
RateLimitBehaviour = exchangeOptions.RateLimitingBehaviour;
|
||||||
rateLimiters = new List<IRateLimiter>();
|
var rateLimiters = new List<IRateLimiter>();
|
||||||
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
foreach (var rateLimiter in exchangeOptions.RateLimiters)
|
||||||
rateLimiters.Add(rateLimiter);
|
rateLimiters.Add(rateLimiter);
|
||||||
|
RateLimiters = rateLimiters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -58,7 +58,9 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="limiter">The limiter to add</param>
|
/// <param name="limiter">The limiter to add</param>
|
||||||
public void AddRateLimiter(IRateLimiter limiter)
|
public void AddRateLimiter(IRateLimiter limiter)
|
||||||
{
|
{
|
||||||
|
var rateLimiters = RateLimiters.ToList();
|
||||||
rateLimiters.Add(limiter);
|
rateLimiters.Add(limiter);
|
||||||
|
RateLimiters = rateLimiters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -66,7 +68,7 @@ namespace CryptoExchange.Net
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RemoveRateLimiters()
|
public void RemoveRateLimiters()
|
||||||
{
|
{
|
||||||
rateLimiters.Clear();
|
RateLimiters = new List<IRateLimiter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -82,7 +84,7 @@ namespace CryptoExchange.Net
|
|||||||
public virtual async Task<CallResult<long>> PingAsync()
|
public virtual async Task<CallResult<long>> PingAsync()
|
||||||
{
|
{
|
||||||
var ping = new Ping();
|
var ping = new Ping();
|
||||||
var uri = new Uri(baseAddress);
|
var uri = new Uri(BaseAddress);
|
||||||
PingReply reply;
|
PingReply reply;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -130,9 +132,9 @@ namespace CryptoExchange.Net
|
|||||||
request.SetProxy(apiProxy.Host, apiProxy.Port);
|
request.SetProxy(apiProxy.Host, apiProxy.Port);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var limiter in rateLimiters)
|
foreach (var limiter in RateLimiters)
|
||||||
{
|
{
|
||||||
var limitResult = limiter.LimitRequest(uri.AbsolutePath, rateLimitBehaviour);
|
var limitResult = limiter.LimitRequest(uri.AbsolutePath, RateLimitBehaviour);
|
||||||
if (!limitResult.Success)
|
if (!limitResult.Success)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} failed because of rate limit");
|
log.Write(LogVerbosity.Debug, $"Request {uri.AbsolutePath} failed because of rate limit");
|
||||||
@ -248,7 +250,7 @@ namespace CryptoExchange.Net
|
|||||||
var returnedData = "";
|
var returnedData = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
request.Timeout = requestTimeout;
|
request.Timeout = RequestTimeout;
|
||||||
var response = await request.GetResponse().ConfigureAwait(false);
|
var response = await request.GetResponse().ConfigureAwait(false);
|
||||||
using (var reader = new StreamReader(response.GetResponseStream()))
|
using (var reader = new StreamReader(response.GetResponseStream()))
|
||||||
{
|
{
|
||||||
|
@ -20,11 +20,11 @@ namespace CryptoExchange.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The factory for creating sockets. Used for unit testing
|
/// The factory for creating sockets. Used for unit testing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory();
|
public IWebsocketFactory SocketFactory { get; set; } = new WebsocketFactory();
|
||||||
|
|
||||||
protected List<SocketSubscription> sockets = new List<SocketSubscription>();
|
protected List<SocketSubscription> sockets = new List<SocketSubscription>();
|
||||||
|
|
||||||
protected TimeSpan reconnectInterval;
|
public TimeSpan ReconnectInterval { get; private set; }
|
||||||
protected Func<byte[], string> dataInterpreter;
|
protected Func<byte[], string> dataInterpreter;
|
||||||
|
|
||||||
protected const string DataHandlerName = "DataHandler";
|
protected const string DataHandlerName = "DataHandler";
|
||||||
@ -48,7 +48,7 @@ namespace CryptoExchange.Net
|
|||||||
/// <param name="exchangeOptions">Options</param>
|
/// <param name="exchangeOptions">Options</param>
|
||||||
protected void Configure(SocketClientOptions exchangeOptions)
|
protected void Configure(SocketClientOptions exchangeOptions)
|
||||||
{
|
{
|
||||||
reconnectInterval = exchangeOptions.ReconnectInterval;
|
ReconnectInterval = exchangeOptions.ReconnectInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -168,7 +168,7 @@ namespace CryptoExchange.Net
|
|||||||
log.Write(LogVerbosity.Info, $"Socket {socket.Id} Connection lost, will try to reconnect");
|
log.Write(LogVerbosity.Info, $"Socket {socket.Id} Connection lost, will try to reconnect");
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
Thread.Sleep(reconnectInterval);
|
Thread.Sleep(ReconnectInterval);
|
||||||
if (!socket.Connect().Result)
|
if (!socket.Connect().Result)
|
||||||
{
|
{
|
||||||
log.Write(LogVerbosity.Debug, $"Socket {socket.Id} failed to reconnect");
|
log.Write(LogVerbosity.Debug, $"Socket {socket.Id} failed to reconnect");
|
||||||
|
@ -8,7 +8,7 @@ namespace CryptoExchange.Net.Sockets
|
|||||||
private readonly SocketSubscription subscription;
|
private readonly SocketSubscription subscription;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event when the connection is lost
|
/// Event when the connection is lost. The socket will automatically reconnect when possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action ConnectionLost
|
public event Action ConnectionLost
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user