Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/system.text.json #98

Merged
merged 11 commits into from
Mar 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Kraken.Net.Converters;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using NUnit.Framework.Legacy;

namespace Kraken.Net.UnitTests.ConvertersTests.StreamOrderbookConvertersTests
{
Expand Down Expand Up @@ -53,14 +54,14 @@ public void Event_Should_ParseCountOfFour()
{
var testObj = StreamOrderBookConverter.Convert(this._fiveElements!);

Assert.AreEqual(2, testObj!.Data.Asks.Count());
Assert.AreEqual(1, testObj.Data.Bids.Count());
Assert.AreEqual(1234, testObj.ChannelId);
Assert.AreEqual("book-10", testObj.ChannelName);
Assert.AreEqual("XBT/USD", testObj.Symbol);
Assert.That(2 == testObj!.Data.Asks.Count());
Assert.That(1 == testObj.Data.Bids.Count());
Assert.That(1234 == testObj.ChannelId);
Assert.That("book-10" == testObj.ChannelName);
Assert.That("XBT/USD" == testObj.Symbol);

Assert.AreEqual("0.40100000", testObj.Data.Asks.ElementAt(1).RawQuantity);
Assert.AreEqual("5542.50000", testObj.Data.Asks.ElementAt(1).RawPrice);
Assert.That("0.40100000" == testObj.Data.Asks.ElementAt(1).RawQuantity);
Assert.That("5542.50000" == testObj.Data.Asks.ElementAt(1).RawPrice);
}
}
}
25 changes: 17 additions & 8 deletions Kraken.Net.UnitTests/JsonToObjectComparer.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using CryptoExchange.Net.Converters;
using CryptoExchange.Net.Converters.JsonNet;
using CryptoExchange.Net.Objects;
using Kraken.Net.UnitTests.TestImplementations;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using System;
using System.Collections;
using System.Collections.Generic;
Expand Down Expand Up @@ -86,7 +88,7 @@ public async Task ProcessSubject<K>(
var result = (CallResult)await TestHelpers.InvokeAsync(method, getSubject(client), input.ToArray());

// asset
Assert.Null(result.Error, method.Name);
ClassicAssert.Null(result.Error, method.Name);

var resultProp = result.GetType().GetProperty("Data", BindingFlags.Public | BindingFlags.Instance);
if (resultProp == null)
Expand Down Expand Up @@ -118,8 +120,10 @@ internal static void ProcessData(string method,
var resultProperties = resultData.GetType().GetProperties().Select(p => (p, (JsonPropertyAttribute)p.GetCustomAttributes(typeof(JsonPropertyAttribute), true).SingleOrDefault()));
var jsonObject = JToken.Parse(json);
if (useNestedJsonPropertyForAllCompare?.Any() == true)
{
foreach (var c in useNestedJsonPropertyForAllCompare)
jsonObject = jsonObject[c];
}

if (useNestedJsonPropertyForCompare?.ContainsKey(method) == true)
{
Expand Down Expand Up @@ -169,9 +173,10 @@ internal static void ProcessData(string method,
enumerator.MoveNext();
if (jObj.Type == JTokenType.Object)
{
if (enumerator.Current is IDictionary)
ProcessDictionary(method, (IDictionary)enumerator.Current, (JObject)jObj, ignoreProperties);

if (enumerator.Current is IDictionary dictionary)
{
ProcessDictionary(method, dictionary, (JObject)jObj, ignoreProperties);
}
else
{
foreach (var subProp in ((JObject)jObj).Properties())
Expand Down Expand Up @@ -252,10 +257,8 @@ private static void CheckObject(string method, JProperty prop, object obj, Dicti

// Property has a value
var property = resultProperties.SingleOrDefault(p => p.Item2?.PropertyName == prop.Name).p;
if (property is null)
property = resultProperties.SingleOrDefault(p => p.p.Name == prop.Name).p;
if (property is null)
property = resultProperties.SingleOrDefault(p => p.p.Name.ToUpperInvariant() == prop.Name.ToUpperInvariant()).p;
property ??= resultProperties.SingleOrDefault(p => p.p.Name == prop.Name).p;
property ??= resultProperties.SingleOrDefault(p => p.p.Name.ToUpperInvariant() == prop.Name.ToUpperInvariant()).p;

if (property is null)
{
Expand All @@ -274,7 +277,9 @@ private static void CheckPropertyValue(string method, JToken propValue, object p
{
if ((propValue.Type == JTokenType.Integer || propValue.Type == JTokenType.String)
&& propValue.ToString() == "0" && (info.PropertyType == typeof(DateTime) || info.PropertyType == typeof(DateTime?)))
{
return;
}

// Property value not correct
throw new Exception($"{method}: Property `{propertyName}` has no value while input json `{propName}` has value {propValue}");
Expand Down Expand Up @@ -371,7 +376,9 @@ private static void CheckPropertyValue(string method, JToken propValue, object p
{
if (info.GetCustomAttribute<JsonConverterAttribute>(true) == null
&& info.GetCustomAttribute<JsonPropertyAttribute>(true)?.ItemConverterType == null)
{
CheckValues(method, propertyName, (JValue)propValue, propertyValue);
}
}
}
}
Expand Down Expand Up @@ -410,7 +417,9 @@ private static void CheckValues(string method, string property, JValue jsonValue
// timestamp, hard to check..
}
else if (jsonValue.Value.ToString().ToLowerInvariant() != objectValue.ToString().ToLowerInvariant())
{
throw new Exception($"{method}: {property} not equal: {jsonValue.Value<string>()} vs {objectValue.ToString()}");
}
}
else if (jsonValue.Type == JTokenType.Integer)
{
Expand Down
8 changes: 4 additions & 4 deletions Kraken.Net.UnitTests/Kraken.Net.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="NUnit" Version="4.1.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
108 changes: 84 additions & 24 deletions Kraken.Net.UnitTests/KrakenClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
using Kraken.Net.Clients.SpotApi;
using Kraken.Net.ExtensionMethods;
using CryptoExchange.Net.Objects.Sockets;
using System.Collections.Generic;
using Kraken.Net.Objects.Models.Futures;
using NUnit.Framework.Legacy;

namespace Kraken.Net.UnitTests
{
Expand Down Expand Up @@ -57,8 +60,8 @@ public class KrakenClientTests
// var data = result.GetType().GetProperty("Data").GetValue(result);

// assert
// Assert.AreEqual(true, callResult);
// Assert.IsTrue(TestHelpers.AreEqual(expected, data), method.Name);
// ClassicAssert.AreSame(true, callResult);
// Assert.That(TestHelpers.AreEqual(expected, data), method.Name);
// }
//}

Expand All @@ -73,9 +76,9 @@ public async Task TestErrorResult_Should_ResultInFailedCall()
var result = await client.SpotApi.ExchangeData.GetSymbolsAsync();

// assert
Assert.IsFalse(result.Success);
Assert.IsTrue(result.Error!.Message.Contains("first error"));
Assert.IsTrue(result.Error!.Message.Contains("another error"));
ClassicAssert.IsFalse(result.Success);
Assert.That(result.Error!.Message.Contains("first error"));
Assert.That(result.Error!.Message.Contains("another error"));
}

[TestCase()]
Expand All @@ -88,9 +91,9 @@ public async Task TestHttpError_Should_ResultInFailedCall()
var result = await client.SpotApi.ExchangeData.GetSymbolsAsync();

// assert
Assert.IsFalse(result.Success);
Assert.IsTrue(result.ResponseStatusCode == System.Net.HttpStatusCode.BadRequest);
Assert.IsTrue(result.Error!.ToString().Contains("Error request"));
ClassicAssert.IsFalse(result.Success);
Assert.That(result.ResponseStatusCode == System.Net.HttpStatusCode.BadRequest);
Assert.That(result.Error!.ToString().Contains("Error request"));
}

public string SerializeExpected<T>(T data)
Expand Down Expand Up @@ -145,8 +148,8 @@ public void CheckValidKrakenWebsocketSymbol(string symbol, bool isValid)
public void CheckRestInterfaces()
{
var assembly = Assembly.GetAssembly(typeof(KrakenRestClientSpotApi));
var ignore = new string[] { "IKrakenClientSpot" };
var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("IKrakenClientSpot") && !ignore.Contains(t.Name));
var ignore = new string[] { "IKrakenRestClientSpot" };
var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("IKrakenRestClientSpot") && !ignore.Contains(t.Name));

foreach (var clientInterface in clientInterfaces)
{
Expand All @@ -155,31 +158,88 @@ public void CheckRestInterfaces()
foreach (var method in implementation.GetMethods().Where(m => m.ReturnType.IsAssignableTo(typeof(Task))))
{
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
Assert.NotNull(interfaceMethod, $"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
ClassicAssert.NotNull(interfaceMethod, $"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
methods++;
}
Debug.WriteLine($"{clientInterface.Name} {methods} methods validated");
}
}

[Test]
public void CheckSocketInterfaces()
[TestCase()]
public async Task ReceivingHttpErrorWithNoJsonOnSpotApi_Should_ReturnErrorAndNotSuccess()
{
var assembly = Assembly.GetAssembly(typeof(KrakenSocketClient));
var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("IKrakenSocketClientSpot"));
// arrange
var client = TestHelpers.CreateClient();
TestHelpers.SetResponse((KrakenRestClient)client, "", System.Net.HttpStatusCode.BadGateway);

foreach (var clientInterface in clientInterfaces)
// act
var result = await client.SpotApi.ExchangeData.GetTickersAsync();

// assert
ClassicAssert.IsFalse(result.Success);
ClassicAssert.IsNotNull(result.Error);
Assert.That(System.Net.HttpStatusCode.BadGateway == result.ResponseStatusCode);
}

[TestCase()]
public async Task ReceivingErrorOnSpotApi_Should_ReturnErrorAndNotSuccess()
{
// arrange
var client = TestHelpers.CreateClient();
var resultObj = new KrakenResult()
{
var implementation = assembly.GetTypes().Single(t => t.IsAssignableTo(clientInterface) && t != clientInterface);
int methods = 0;
foreach (var method in implementation.GetMethods().Where(m => m.ReturnType.IsAssignableTo(typeof(Task<CallResult<UpdateSubscription>>))))
Error = new List<string>
{
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
Assert.NotNull(interfaceMethod, $"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
methods++;
"Error occured"
}
Debug.WriteLine($"{clientInterface.Name} {methods} methods validated");
}
};

TestHelpers.SetResponse((KrakenRestClient)client, JsonConvert.SerializeObject(resultObj));

// act
var result = await client.SpotApi.ExchangeData.GetAssetsAsync();

// assert
ClassicAssert.IsFalse(result.Success);
ClassicAssert.IsNotNull(result.Error);
Assert.That(result.Error.Message == "Error occured");
}

[TestCase()]
public async Task ReceivingHttpErrorWithNoJsonOnFuturesApi_Should_ReturnErrorAndNotSuccess()
{
// arrange
var client = TestHelpers.CreateClient();
TestHelpers.SetResponse((KrakenRestClient)client, "", System.Net.HttpStatusCode.BadGateway);

// act
var result = await client.FuturesApi.ExchangeData.GetTickersAsync();

// assert
ClassicAssert.IsFalse(result.Success);
ClassicAssert.IsNotNull(result.Error);
Assert.That(System.Net.HttpStatusCode.BadGateway == result.ResponseStatusCode);
}

[TestCase()]
public async Task ReceivingErrorOnFuturesApi_Should_ReturnErrorAndNotSuccess()
{
// arrange
var client = TestHelpers.CreateClient();
var resultObj = new KrakenFuturesResult()
{
Error = "Error occured"
};

TestHelpers.SetResponse((KrakenRestClient)client, JsonConvert.SerializeObject(resultObj), System.Net.HttpStatusCode.BadRequest);

// act
var result = await client.FuturesApi.ExchangeData.GetTickersAsync();

// assert
ClassicAssert.IsFalse(result.Success);
ClassicAssert.IsNotNull(result.Error);
Assert.That(result.Error.Message == "Error occured");
}
}
}
37 changes: 32 additions & 5 deletions Kraken.Net.UnitTests/KrakenSocketClientTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
using CryptoExchange.Net;
using CryptoExchange.Net.Objects;
using CryptoExchange.Net.Objects.Sockets;
using Kraken.Net.Clients;
using Kraken.Net.UnitTests.TestImplementations;
using Kucoin.Net.UnitTests.TestImplementations;
using Newtonsoft.Json.Linq;
using NUnit.Framework;
using NUnit.Framework.Legacy;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace Kraken.Net.UnitTests
Expand All @@ -22,11 +29,11 @@ public async Task Subscribe_Should_SucceedIfAckResponse()
var subTask = client.SpotApi.SubscribeToTickerUpdatesAsync("XBT/EUR", test => { });
await Task.Delay(10);
var id = JToken.Parse(socket.LastSendMessage!)["reqid"];
await socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"subscribed\", \"reqid\":{id}}}");
socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"subscribed\", \"reqid\":{id}}}");
var subResult = subTask.Result;

// assert
Assert.IsTrue(subResult.Success);
Assert.That(subResult.Success);
}

[Test]
Expand All @@ -41,12 +48,32 @@ public async Task Subscribe_Should_FailIfNotAckResponse()
var subTask = client.SpotApi.SubscribeToTickerUpdatesAsync("XBT/EUR", test => { });
await Task.Delay(10);
var id = JToken.Parse(socket.LastSendMessage!)["reqid"];
await socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"error\", \"errormessage\": \"Failed to sub\", \"reqid\":{id}}}");
socket.InvokeMessage($"{{\"channelID\": 1, \"status\": \"error\", \"errormessage\": \"Failed to sub\", \"reqid\":{id}}}");
var subResult = subTask.Result;

// assert
Assert.IsFalse(subResult.Success);
Assert.IsTrue(subResult.Error!.Message.Contains("Failed to sub"));
ClassicAssert.IsFalse(subResult.Success);
Assert.That(subResult.Error!.Message.Contains("Failed to sub"));
}

[Test]
public void CheckSocketInterfaces()
{
var assembly = Assembly.GetAssembly(typeof(KrakenSocketClient));
var clientInterfaces = assembly.GetTypes().Where(t => t.Name.StartsWith("IKrakenSocketClientSpot"));

foreach (var clientInterface in clientInterfaces)
{
var implementation = assembly.GetTypes().Single(t => t.IsAssignableTo(clientInterface) && t != clientInterface);
int methods = 0;
foreach (var method in implementation.GetMethods().Where(m => m.ReturnType.IsAssignableTo(typeof(Task<CallResult<UpdateSubscription>>))))
{
var interfaceMethod = clientInterface.GetMethod(method.Name, method.GetParameters().Select(p => p.ParameterType).ToArray());
ClassicAssert.NotNull(interfaceMethod, $"Missing interface for method {method.Name} in {implementation.Name} implementing interface {clientInterface.Name}");
methods++;
}
Debug.WriteLine($"{clientInterface.Name} {methods} methods validated");
}
}
}
}
12 changes: 5 additions & 7 deletions Kraken.Net.UnitTests/TestImplementations/TestSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public class TestSocket: IWebsocket
public event Func<Task> OnReconnecting;
#pragma warning restore 0067
public event Func<int, Task> OnRequestSent;
public event Func<WebSocketMessageType, Stream, Task> OnStreamMessage;
public event Action<WebSocketMessageType, ReadOnlyMemory<byte>> OnStreamMessage;
public event Func<Exception, Task> OnError;
public event Func<Task> OnOpen;

Expand Down Expand Up @@ -94,16 +94,14 @@ public void InvokeOpen()
OnOpen?.Invoke();
}

public async Task InvokeMessage(string data)
public void InvokeMessage(string data)
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(data));
await OnStreamMessage?.Invoke(WebSocketMessageType.Text, stream);
OnStreamMessage?.Invoke(WebSocketMessageType.Text, new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(data)));
}

public async Task InvokeMessage<T>(T data)
public void InvokeMessage<T>(T data)
{
var stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)));
await OnStreamMessage?.Invoke(WebSocketMessageType.Text, stream);
OnStreamMessage?.Invoke(WebSocketMessageType.Text, new ReadOnlyMemory<byte>(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data))));
}

public void InvokeError(Exception error)
Expand Down
Loading
Loading