Skip to content

Commit 09a30c9

Browse files
authored
added tests for Coinbase (#856)
1 parent b595119 commit 09a30c9

File tree

2 files changed

+88
-16
lines changed

2 files changed

+88
-16
lines changed

src/ExchangeSharp/API/Common/APIRequestMaker.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The above copyright notice and this permission notice shall be included in all c
1616
using System.Linq;
1717
using System.Net;
1818
using System.Net.Http;
19+
using System.Net.Http.Headers;
1920
using System.Threading;
2021
using System.Threading.Tasks;
2122

@@ -143,14 +144,14 @@ public Dictionary<string, IReadOnlyList<string>> Headers
143144
}
144145
}
145146

146-
/// <summary>
147-
/// Constructor
148-
/// </summary>
149-
/// <param name="api">API</param>
150-
public APIRequestMaker(IAPIRequestHandler api)
151-
{
152-
this.api = api;
153-
}
147+
/// <summary>
148+
/// Constructor
149+
/// </summary>
150+
/// <param name="api">API</param>
151+
public APIRequestMaker(IAPIRequestHandler api) => this.api = api;
152+
153+
/// <summary> Additional headers to add to each request (for testing) </summary>
154+
static internal (string, string)? AdditionalHeader = null;
154155

155156
/// <summary>
156157
/// Make a request to a path on the API
@@ -184,6 +185,10 @@ public APIRequestMaker(IAPIRequestHandler api)
184185
request.AddHeader("accept-language", "en-US,en;q=0.5");
185186
request.AddHeader("content-type", api.RequestContentType);
186187
request.AddHeader("user-agent", BaseAPI.RequestUserAgent);
188+
if (AdditionalHeader != null)
189+
{
190+
request.AddHeader(AdditionalHeader.Value.Item1, AdditionalHeader.Value.Item2);
191+
}
187192
request.Timeout = (int)api.RequestTimeout.TotalMilliseconds;
188193
await api.ProcessRequestAsync(request, payload);
189194

tests/ExchangeSharpTests/ExchangeCoinbaseAPITests.cs

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,91 @@
77
using FluentAssertions;
88
using Microsoft.VisualStudio.TestTools.UnitTesting;
99
using Newtonsoft.Json;
10+
using NSubstitute.ExceptionExtensions;
11+
using static System.Net.WebRequestMethods;
1012

1113
namespace ExchangeSharpTests
1214
{
1315
[TestClass]
1416
public sealed class ExchangeCoinbaseAPITests
1517
{
16-
private async Task<ExchangeCoinbaseAPI> MakeMockRequestMaker(string response = null)
17-
{
18-
var requestMaker = new MockAPIRequestMaker();
19-
if (response != null)
20-
{
21-
requestMaker.GlobalResponse = response;
22-
}
18+
private async Task<ExchangeCoinbaseAPI> CreateSandboxApiAsync(string response = null)
19+
{ // per Coinbase: All responses are static and pre-defined. https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-sandbox. Thus, no need to use MockAPIRequestMaker and just connect to real sandbox every time
20+
//var requestMaker = new MockAPIRequestMaker();
21+
//if (response != null)
22+
//{
23+
// requestMaker.GlobalResponse = response;
24+
//}
2325
var api = (
2426
await ExchangeAPI.GetExchangeAPIAsync(ExchangeName.Coinbase) as ExchangeCoinbaseAPI
2527
)!;
26-
api.RequestMaker = requestMaker;
28+
//api.RequestMaker = requestMaker;
29+
api.BaseUrl = "https://api-sandbox.coinbase.com/api/v3/brokerage";
2730
return api;
2831
}
32+
33+
[TestMethod]
34+
public async Task PlaceOrderAsync_Success()
35+
{
36+
var api = await CreateSandboxApiAsync();
37+
{ // per Coinbase: Users can make API requests to Advanced sandbox API without authentication. https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-sandbox. Thus, no need to set api.PublicApiKey and api.PrivateApiKey
38+
var orderRequest = new ExchangeOrderRequest
39+
{
40+
MarketSymbol = "BTC-USD",
41+
Amount = 0.0001m,
42+
Price = 10000m,
43+
IsBuy = true,
44+
OrderType = OrderType.Limit
45+
};
46+
47+
var orderResult = await api.PlaceOrderAsync(orderRequest);
48+
orderResult.Should().NotBeNull("an order result should be returned");
49+
orderResult.ClientOrderId.Should().Be("sandbox_success_order", "this is what the sandbox returns");
50+
orderResult.OrderId.Should().Be("f898eaf4-6ffc-47be-a159-7ff292e5cdcf", "this is what the sandbox returns");
51+
orderResult.MarketSymbol.Should().Be(orderRequest.MarketSymbol, "the market symbol should be the same as requested");
52+
orderResult.IsBuy.Should().Be(false, "the sandbox always returns a SELL, even if a buy is submitted");
53+
orderResult.Result.Should().Be(ExchangeAPIOrderResult.PendingOpen, "the order should be placed successfully in sandbox");
54+
}
55+
}
56+
57+
[TestMethod]
58+
public async Task PlaceOrderAsync_Failure()
59+
{
60+
var api = await CreateSandboxApiAsync();
61+
{ // per Coinbase: Users can make API requests to Advanced sandbox API without authentication. https://docs.cdp.coinbase.com/advanced-trade/docs/rest-api-sandbox. Thus, no need to set api.PublicApiKey and api.PrivateApiKey
62+
var orderRequest = new ExchangeOrderRequest
63+
{
64+
MarketSymbol = "BTC-USD",
65+
Amount = 0.0001m,
66+
Price = 10000m,
67+
IsBuy = true,
68+
OrderType = OrderType.Limit
69+
};
70+
APIRequestMaker.AdditionalHeader = ("X-Sandbox", "PostOrder_insufficient_fund");
71+
var orderResult = await api.PlaceOrderAsync(orderRequest);
72+
orderResult.Should().NotBeNull("an order result should be returned");
73+
orderResult.ClientOrderId.Should().Be("9dde8003-fc68-4ec1-96ab-5a759e5f4f5c", "this is what the sandbox returns");
74+
orderResult.OrderId.Should().BeNull("this is what the sandbox returns");
75+
orderResult.MarketSymbol.Should().Be(orderRequest.MarketSymbol, "because the market symbol should be the same as requested");
76+
orderResult.IsBuy.Should().Be(orderRequest.IsBuy, "this is what was requested");
77+
orderResult.Result.Should().Be(ExchangeAPIOrderResult.Rejected, "testing for failure");
78+
}
79+
}
80+
81+
[TestMethod]
82+
public async Task CancelOrderAsync_Success()
83+
{
84+
var api = await CreateSandboxApiAsync();
85+
await api.CancelOrderAsync("1");
86+
}
87+
88+
[TestMethod]
89+
public async Task CancelOrderAsync_Failure()
90+
{
91+
var api = await CreateSandboxApiAsync();
92+
APIRequestMaker.AdditionalHeader = ("X-Sandbox", "CancelOrders_failure");
93+
Func<Task> act = () => api.CancelOrderAsync("1");
94+
await act.Should().ThrowAsync<APIException>();
95+
}
2996
}
3097
}

0 commit comments

Comments
 (0)