Modify HTTP to take in proxy info rather than rely on the tor service singleton

This commit is contained in:
julian 2023-09-08 14:41:39 -06:00
parent 6d0bb27711
commit 9d6cdfd2c1
6 changed files with 250 additions and 226 deletions

View file

@ -1,9 +1,9 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:socks5_proxy/socks_client.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/utilities/logger.dart';
// WIP wrapper layer
@ -11,28 +11,29 @@ import 'package:stackwallet/utilities/logger.dart';
// TODO expand this class
class Response {
final int code;
final String body;
final List<int> bodyBytes;
Response(this.body, this.code);
String get body => utf8.decode(bodyBytes, allowMalformed: true);
Response(this.bodyBytes, this.code);
}
class HTTP {
/// Visible for testing so we can override with a mock TorService
@visibleForTesting
TorService torService = TorService.sharedInstance;
Future<Response> get({
required Uri url,
Map<String, String>? headers,
required bool routeOverTor,
required ({
InternetAddress host,
int port,
})? proxyInfo,
}) async {
final httpClient = HttpClient();
try {
if (routeOverTor) {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
ProxySettings(
torService.proxyInfo.host,
torService.proxyInfo.port,
proxyInfo.host,
proxyInfo.port,
),
]);
}
@ -46,8 +47,9 @@ class HTTP {
}
final response = await request.close();
return Response(
await response.transform(utf8.decoder).join(),
await _bodyBytes(response),
response.statusCode,
);
} catch (e, s) {
@ -66,15 +68,18 @@ class HTTP {
Map<String, String>? headers,
Object? body,
Encoding? encoding,
required bool routeOverTor,
required ({
InternetAddress host,
int port,
})? proxyInfo,
}) async {
final httpClient = HttpClient();
try {
if (routeOverTor) {
if (proxyInfo != null) {
SocksTCPClient.assignToHttpClient(httpClient, [
ProxySettings(
torService.proxyInfo.host,
torService.proxyInfo.port,
proxyInfo.host,
proxyInfo.port,
),
]);
}
@ -92,7 +97,7 @@ class HTTP {
final response = await request.close();
return Response(
await response.transform(utf8.decoder).join(),
await _bodyBytes(response),
response.statusCode,
);
} catch (e, s) {
@ -105,4 +110,18 @@ class HTTP {
httpClient.close(force: true);
}
}
Future<Uint8List> _bodyBytes(HttpClientResponse response) {
final completer = Completer<Uint8List>();
final List<int> bytes = [];
response.listen(
(data) {
bytes.addAll(data);
},
onDone: () => completer.complete(
Uint8List.fromList(bytes),
),
);
return completer.future;
}
}

View file

@ -28,6 +28,7 @@ import 'package:stackwallet/models/isar/exchange_cache/pair.dart';
import 'package:stackwallet/networking/http.dart';
import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart';
import 'package:stackwallet/services/exchange/exchange_response.dart';
import 'package:stackwallet/services/tor_service.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart';
@ -38,10 +39,12 @@ class ChangeNowAPI {
static const String apiVersion = "/v1";
static const String apiVersionV2 = "/v2";
HTTP client = HTTP();
final HTTP client;
ChangeNowAPI._();
static final ChangeNowAPI _instance = ChangeNowAPI._();
@visibleForTesting
ChangeNowAPI({HTTP? http}) : client = http ?? HTTP();
static final ChangeNowAPI _instance = ChangeNowAPI();
static ChangeNowAPI get instance => _instance;
Uri _buildUri(String path, Map<String, dynamic>? params) {
@ -57,7 +60,8 @@ class ChangeNowAPI {
final response = await client.get(
url: uri,
headers: {'Content-Type': 'application/json'},
routeOverTor: Prefs.instance.useTor,
proxyInfo:
Prefs.instance.useTor ? TorService.sharedInstance.proxyInfo : null,
);
String? data;
try {
@ -86,7 +90,8 @@ class ChangeNowAPI {
// 'Content-Type': 'application/json',
'x-changenow-api-key': apiKey,
},
routeOverTor: Prefs.instance.useTor,
proxyInfo:
Prefs.instance.useTor ? TorService.sharedInstance.proxyInfo : null,
);
final data = response.body;
@ -109,7 +114,8 @@ class ChangeNowAPI {
url: uri,
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body),
routeOverTor: Prefs.instance.useTor,
proxyInfo:
Prefs.instance.useTor ? TorService.sharedInstance.proxyInfo : null,
);
String? data;

View file

@ -802,14 +802,6 @@ class MockChangeNowAPI extends _i1.Mock implements _i14.ChangeNowAPI {
),
) as _i2.HTTP);
@override
set client(_i2.HTTP? _client) => super.noSuchMethod(
Invocation.setter(
#client,
_client,
),
returnValueForMissingStub: null,
);
@override
_i7.Future<_i3.ExchangeResponse<List<_i15.Currency>>> getAvailableCurrencies({
bool? fixedRate,
bool? active,
@ -1073,8 +1065,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i14.ChangeNowAPI {
[],
{#apiKey: apiKey},
),
returnValue: _i7.Future<
_i3.ExchangeResponse<List<_i20.FixedRateMarket>>>.value(
returnValue: _i7
.Future<_i3.ExchangeResponse<List<_i20.FixedRateMarket>>>.value(
_FakeExchangeResponse_1<List<_i20.FixedRateMarket>>(
this,
Invocation.method(
@ -1115,8 +1107,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i14.ChangeNowAPI {
#apiKey: apiKey,
},
),
returnValue: _i7.Future<
_i3.ExchangeResponse<_i21.ExchangeTransaction>>.value(
returnValue: _i7
.Future<_i3.ExchangeResponse<_i21.ExchangeTransaction>>.value(
_FakeExchangeResponse_1<_i21.ExchangeTransaction>(
this,
Invocation.method(
@ -1172,8 +1164,8 @@ class MockChangeNowAPI extends _i1.Mock implements _i14.ChangeNowAPI {
#apiKey: apiKey,
},
),
returnValue: _i7.Future<
_i3.ExchangeResponse<_i21.ExchangeTransaction>>.value(
returnValue: _i7
.Future<_i3.ExchangeResponse<_i21.ExchangeTransaction>>.value(
_FakeExchangeResponse_1<_i21.ExchangeTransaction>(
this,
Invocation.method(
@ -1197,35 +1189,35 @@ class MockChangeNowAPI extends _i1.Mock implements _i14.ChangeNowAPI {
)),
) as _i7.Future<_i3.ExchangeResponse<_i21.ExchangeTransaction>>);
@override
_i7.Future<_i3.ExchangeResponse<_i22.ExchangeTransactionStatus>>
getTransactionStatus({
_i7.Future<
_i3
.ExchangeResponse<_i22.ExchangeTransactionStatus>> getTransactionStatus({
required String? id,
String? apiKey,
}) =>
(super.noSuchMethod(
Invocation.method(
#getTransactionStatus,
[],
{
#id: id,
#apiKey: apiKey,
},
),
returnValue: _i7.Future<
_i3.ExchangeResponse<_i22.ExchangeTransactionStatus>>.value(
_FakeExchangeResponse_1<_i22.ExchangeTransactionStatus>(
this,
Invocation.method(
#getTransactionStatus,
[],
{
#id: id,
#apiKey: apiKey,
},
),
)),
) as _i7
.Future<_i3.ExchangeResponse<_i22.ExchangeTransactionStatus>>);
(super.noSuchMethod(
Invocation.method(
#getTransactionStatus,
[],
{
#id: id,
#apiKey: apiKey,
},
),
returnValue: _i7
.Future<_i3.ExchangeResponse<_i22.ExchangeTransactionStatus>>.value(
_FakeExchangeResponse_1<_i22.ExchangeTransactionStatus>(
this,
Invocation.method(
#getTransactionStatus,
[],
{
#id: id,
#apiKey: apiKey,
},
),
)),
) as _i7.Future<_i3.ExchangeResponse<_i22.ExchangeTransactionStatus>>);
@override
_i7.Future<_i3.ExchangeResponse<List<_i23.Pair>>>
getAvailableFloatingRatePairs({bool? includePartners = false}) =>

View file

@ -20,16 +20,17 @@ void main() {
group("getAvailableCurrencies", () {
test("getAvailableCurrencies succeeds without options", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSON), 200));
Response(utf8.encode(jsonEncode(availableCurrenciesJSON)), 200));
final result = await ChangeNowAPI.instance.getAvailableCurrencies();
final result = await instance.getAvailableCurrencies();
expect(result.exception, null);
expect(result.value == null, false);
@ -38,17 +39,16 @@ void main() {
test("getAvailableCurrencies succeeds with active option", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies?active=true"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONActive), 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode(jsonEncode(availableCurrenciesJSONActive)), 200));
final result =
await ChangeNowAPI.instance.getAvailableCurrencies(active: true);
final result = await instance.getAvailableCurrencies(active: true);
expect(result.exception, null);
expect(result.value == null, false);
@ -57,17 +57,16 @@ void main() {
test("getAvailableCurrencies succeeds with fixedRate option", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies?fixedRate=true"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONFixedRate), 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode(jsonEncode(availableCurrenciesJSONFixedRate)), 200));
final result =
await ChangeNowAPI.instance.getAvailableCurrencies(fixedRate: true);
final result = await instance.getAvailableCurrencies(fixedRate: true);
expect(result.exception, null);
expect(result.value == null, false);
@ -77,18 +76,19 @@ void main() {
test("getAvailableCurrencies succeeds with fixedRate and active options",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/currencies?fixedRate=true&active=true"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(availableCurrenciesJSONActiveFixedRate), 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode(jsonEncode(availableCurrenciesJSONActiveFixedRate)),
200));
final result = await ChangeNowAPI.instance
.getAvailableCurrencies(active: true, fixedRate: true);
final result =
await instance.getAvailableCurrencies(active: true, fixedRate: true);
expect(result.exception, null);
expect(result.value == null, false);
@ -99,16 +99,16 @@ void main() {
"getAvailableCurrencies fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response('{"some unexpected": "but valid json data"}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode('{"some unexpected": "but valid json data"}'), 200));
final result = await ChangeNowAPI.instance.getAvailableCurrencies();
final result = await instance.getAvailableCurrencies();
expect(
result.exception!.type, ExchangeExceptionType.serializeResponseError);
@ -117,15 +117,15 @@ void main() {
test("getAvailableCurrencies fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response("", 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(""), 400));
final result = await ChangeNowAPI.instance.getAvailableCurrencies();
final result = await instance.getAvailableCurrencies();
expect(
result.exception!.type, ExchangeExceptionType.serializeResponseError);
@ -136,17 +136,16 @@ void main() {
group("getPairedCurrencies", () {
test("getPairedCurrencies succeeds without fixedRate option", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies-to/XMR"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(getPairedCurrenciesJSON), 200));
Response(utf8.encode(jsonEncode(getPairedCurrenciesJSON)), 200));
final result =
await ChangeNowAPI.instance.getPairedCurrencies(ticker: "XMR");
final result = await instance.getPairedCurrencies(ticker: "XMR");
expect(result.exception, null);
expect(result.value == null, false);
@ -155,18 +154,18 @@ void main() {
test("getPairedCurrencies succeeds with fixedRate option", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/currencies-to/XMR?fixedRate=true"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(getPairedCurrenciesJSONFixedRate), 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode(jsonEncode(getPairedCurrenciesJSONFixedRate)), 200));
final result = await ChangeNowAPI.instance
.getPairedCurrencies(ticker: "XMR", fixedRate: true);
final result =
await instance.getPairedCurrencies(ticker: "XMR", fixedRate: true);
expect(result.exception, null);
expect(result.value == null, false);
@ -177,17 +176,16 @@ void main() {
"getPairedCurrencies fails with ChangeNowExceptionType.serializeResponseError A",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies-to/XMR"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response('[{"some unexpected": "but valid json data"}]', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode('[{"some unexpected": "but valid json data"}]'), 200));
final result =
await ChangeNowAPI.instance.getPairedCurrencies(ticker: "XMR");
final result = await instance.getPairedCurrencies(ticker: "XMR");
expect(
result.exception!.type, ExchangeExceptionType.serializeResponseError);
@ -196,16 +194,16 @@ void main() {
test("getPairedCurrencies fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse("https://api.ChangeNow.io/v1/currencies"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response("", 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(""), 400));
final result = await ChangeNowAPI.instance
.getPairedCurrencies(ticker: "XMR", fixedRate: true);
final result =
await instance.getPairedCurrencies(ticker: "XMR", fixedRate: true);
expect(result.exception!.type, ExchangeExceptionType.generic);
expect(result.value == null, true);
@ -215,17 +213,17 @@ void main() {
group("getMinimalExchangeAmount", () {
test("getMinimalExchangeAmount succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer(
(realInvocation) async => Response('{"minAmount": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"minAmount": 42}'), 200));
final result = await ChangeNowAPI.instance.getMinimalExchangeAmount(
final result = await instance.getMinimalExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
apiKey: "testAPIKEY",
@ -240,16 +238,17 @@ void main() {
"getMinimalExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result = await ChangeNowAPI.instance.getMinimalExchangeAmount(
final result = await instance.getMinimalExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
apiKey: "testAPIKEY",
@ -262,16 +261,16 @@ void main() {
test("getMinimalExchangeAmount fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/min-amount/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('', 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result = await ChangeNowAPI.instance.getMinimalExchangeAmount(
final result = await instance.getMinimalExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
apiKey: "testAPIKEY",
@ -286,18 +285,19 @@ void main() {
group("getEstimatedExchangeAmount", () {
test("getEstimatedExchangeAmount succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
'{"estimatedAmount": 58.4142873, "transactionSpeedForecast": "10-60", "warningMessage": null}',
utf8.encode(
'{"estimatedAmount": 58.4142873, "transactionSpeedForecast": "10-60", "warningMessage": null}'),
200));
final result = await ChangeNowAPI.instance.getEstimatedExchangeAmount(
final result = await instance.getEstimatedExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
fromAmount: Decimal.fromInt(42),
@ -313,16 +313,17 @@ void main() {
"getEstimatedExchangeAmount fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result = await ChangeNowAPI.instance.getEstimatedExchangeAmount(
final result = await instance.getEstimatedExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
fromAmount: Decimal.fromInt(42),
@ -336,16 +337,16 @@ void main() {
test("getEstimatedExchangeAmount fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/exchange-amount/42/xmr_btc?api_key=testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('', 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result = await ChangeNowAPI.instance.getEstimatedExchangeAmount(
final result = await instance.getEstimatedExchangeAmount(
fromTicker: "xmr",
toTicker: "btc",
fromAmount: Decimal.fromInt(42),
@ -366,9 +367,9 @@ void main() {
// Uri.parse(
// "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
// headers: {'Content-Type': 'application/json'},
// routeOverTor: false,
// proxyInfo: null,
// )).thenAnswer((realInvocation) async =>
// Response(jsonEncode(estFixedRateExchangeAmountJSON), 200));
// Response(utf8.encode(jsonEncode(estFixedRateExchangeAmountJSON )), 200));
//
// final result =
// await ChangeNow.instance.getEstimatedFixedRateExchangeAmount(
@ -394,7 +395,7 @@ void main() {
// Uri.parse(
// "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
// headers: {'Content-Type': 'application/json'},
// routeOverTor: false,
// proxyInfo: null,
// )).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
//
// final result =
@ -419,7 +420,7 @@ void main() {
// Uri.parse(
// "https://api.ChangeNow.io/v1/exchange-amount/fixed-rate/10/xmr_btc?api_key=testAPIKEY&useRateId=true"),
// headers: {'Content-Type': 'application/json'},
// routeOverTor: false,
// proxyInfo: null,
// )).thenAnswer((realInvocation) async => Response('', 400));
//
// final result =
@ -438,17 +439,17 @@ void main() {
group("getAvailableFixedRateMarkets", () {
test("getAvailableFixedRateMarkets succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(fixedRateMarketsJSON), 200));
Response(utf8.encode(jsonEncode(fixedRateMarketsJSON)), 200));
final result = await ChangeNowAPI.instance.getAvailableFixedRateMarkets(
final result = await instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY",
);
@ -461,16 +462,17 @@ void main() {
"getAvailableFixedRateMarkets fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result = await ChangeNowAPI.instance.getAvailableFixedRateMarkets(
final result = await instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY",
);
@ -481,16 +483,16 @@ void main() {
test("getAvailableFixedRateMarkets fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('', 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result = await ChangeNowAPI.instance.getAvailableFixedRateMarkets(
final result = await instance.getAvailableFixedRateMarkets(
apiKey: "testAPIKEY",
);
@ -503,20 +505,19 @@ void main() {
group("createStandardExchangeTransaction", () {
test("createStandardExchangeTransaction succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
encoding: null,
)).thenAnswer((realInvocation) async =>
Response(jsonEncode(createStandardTransactionResponse), 200));
)).thenAnswer((realInvocation) async => Response(
utf8.encode(jsonEncode(createStandardTransactionResponse)), 200));
final result =
await ChangeNowAPI.instance.createStandardExchangeTransaction(
final result = await instance.createStandardExchangeTransaction(
fromTicker: "xmr",
toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -535,19 +536,19 @@ void main() {
"createStandardExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
encoding: null,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result =
await ChangeNowAPI.instance.createStandardExchangeTransaction(
final result = await instance.createStandardExchangeTransaction(
fromTicker: "xmr",
toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -565,19 +566,18 @@ void main() {
test("createStandardExchangeTransaction fails for any other reason",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse("https://api.ChangeNow.io/v1/transactions/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from":"xmr","to":"btc","address":"bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5","amount":"0.3","flow":"standard","extraId":"","userId":"","contactEmail":"","refundAddress":"888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H","refundExtraId":""}',
encoding: null,
)).thenAnswer((realInvocation) async => Response('', 400));
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result =
await ChangeNowAPI.instance.createStandardExchangeTransaction(
final result = await instance.createStandardExchangeTransaction(
fromTicker: "xmr",
toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -595,22 +595,26 @@ void main() {
group("createFixedRateExchangeTransaction", () {
test("createFixedRateExchangeTransaction succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":"","amount":"0.3"}',
encoding: null,
)).thenAnswer((realInvocation) async => Response(
'{"payinAddress": "33eFX2jfeWbXMSmRe9ewUUTrmSVSxZi5cj", "payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886","payoutExtraId": "", "fromCurrency": "btc", "toCurrency": "eth", "refundAddress": "","refundExtraId": "","validUntil": "2019-09-09T14:01:04.921Z","id": "a5c73e2603f40d","amount": 62.9737711}',
utf8.encode(
'{"payinAddress": "33eFX2jfeWbXMSmRe9ewUUTrmSVSxZi5cj", "payoutAddress":'
' "0x57f31ad4b64095347F87eDB1675566DAfF5EC886","payoutExtraId": "",'
' "fromCurrency": "btc", "toCurrency": "eth", "refundAddress": "",'
'"refundExtraId": "","validUntil": "2019-09-09T14:01:04.921Z","id":'
' "a5c73e2603f40d","amount": 62.9737711}'),
200));
final result =
await ChangeNowAPI.instance.createFixedRateExchangeTransaction(
final result = await instance.createFixedRateExchangeTransaction(
fromTicker: "btc",
toTicker: "eth",
receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886",
@ -630,21 +634,20 @@ void main() {
"createFixedRateExchangeTransaction fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from":"btc","to":"eth","address":"0x57f31ad4b64095347F87eDB1675566DAfF5EC886","amount":"0.3","flow":"fixed-rate","extraId":"","userId":"","contactEmail":"","refundAddress":"","refundExtraId":"","rateId":""}',
encoding: null,
)).thenAnswer((realInvocation) async =>
Response('{"id": "a5c73e2603f40d","amount": 62.9737711}', 200));
)).thenAnswer((realInvocation) async => Response(
utf8.encode('{"id": "a5c73e2603f40d","amount": 62.9737711}'), 200));
final result =
await ChangeNowAPI.instance.createFixedRateExchangeTransaction(
final result = await instance.createFixedRateExchangeTransaction(
fromTicker: "btc",
toTicker: "eth",
receivingAddress: "0x57f31ad4b64095347F87eDB1675566DAfF5EC886",
@ -662,20 +665,19 @@ void main() {
test("createFixedRateExchangeTransaction fails for any other reason",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.post(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/fixed-rate/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
body:
'{"from": "btc","to": "eth","address": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "amount": "1.12345","extraId": "", "userId": "","contactEmail": "","refundAddress": "", "refundExtraId": "", "rateId": "" }',
encoding: null,
)).thenAnswer((realInvocation) async => Response('', 400));
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result =
await ChangeNowAPI.instance.createFixedRateExchangeTransaction(
final result = await instance.createFixedRateExchangeTransaction(
fromTicker: "xmr",
toTicker: "btc",
receivingAddress: "bc1qu58svs9983e2vuyqh7gq7ratf8k5qehz5k0cn5",
@ -695,18 +697,24 @@ void main() {
group("getTransactionStatus", () {
test("getTransactionStatus succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
'{"status": "waiting", "payinAddress": "32Ge2ci26rj1sRGw2NjiQa9L7Xvxtgzhrj", "payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", "fromCurrency": "btc", "toCurrency": "eth", "id": "50727663e5d9a4", "updatedAt": "2019-08-22T14:47:49.943Z", "expectedSendAmount": 1, "expectedReceiveAmount": 52.31667, "createdAt": "2019-08-22T14:47:49.943Z", "isPartner": false}',
utf8.encode(
'{"status": "waiting", "payinAddress": "32Ge2ci26rj1sRGw2NjiQa9L7Xvxtgzhrj", '
'"payoutAddress": "0x57f31ad4b64095347F87eDB1675566DAfF5EC886", '
'"fromCurrency": "btc", "toCurrency": "eth", "id": "50727663e5d9a4", '
'"updatedAt": "2019-08-22T14:47:49.943Z", "expectedSendAmount": 1, '
'"expectedReceiveAmount": 52.31667, "createdAt": "2019-08-22T14:47:49.943Z",'
' "isPartner": false}'),
200));
final result = await ChangeNowAPI.instance.getTransactionStatus(
final result = await instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY",
);
@ -720,16 +728,17 @@ void main() {
"getTransactionStatus fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result = await ChangeNowAPI.instance.getTransactionStatus(
final result = await instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY",
);
@ -741,16 +750,16 @@ void main() {
test("getTransactionStatus fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/transactions/47F87eDB1675566DAfF5EC886/testAPIKEY"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('', 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result = await ChangeNowAPI.instance.getTransactionStatus(
final result = await instance.getTransactionStatus(
id: "47F87eDB1675566DAfF5EC886",
apiKey: "testAPIKEY",
);
@ -764,18 +773,17 @@ void main() {
group("getAvailableFloatingRatePairs", () {
test("getAvailableFloatingRatePairs succeeds", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async =>
Response('["btc_xmr","btc_firo","btc_doge","eth_ltc"]', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(
utf8.encode('["btc_xmr","btc_firo","btc_doge","eth_ltc"]'), 200));
final result =
await ChangeNowAPI.instance.getAvailableFloatingRatePairs();
final result = await instance.getAvailableFloatingRatePairs();
expect(result.exception, null);
expect(result.value == null, false);
@ -786,17 +794,17 @@ void main() {
"getAvailableFloatingRatePairs fails with ChangeNowExceptionType.serializeResponseError",
() async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('{"error": 42}', 200));
proxyInfo: null,
)).thenAnswer((realInvocation) async =>
Response(utf8.encode('{"error": 42}'), 200));
final result =
await ChangeNowAPI.instance.getAvailableFloatingRatePairs();
final result = await instance.getAvailableFloatingRatePairs();
expect(
result.exception!.type, ExchangeExceptionType.serializeResponseError);
@ -805,17 +813,16 @@ void main() {
test("getAvailableFloatingRatePairs fails for any other reason", () async {
final client = MockHTTP();
ChangeNowAPI.instance.client = client;
final instance = ChangeNowAPI(http: client);
when(client.get(
url: Uri.parse(
"https://api.ChangeNow.io/v1/market-info/available-pairs?includePartners=false"),
headers: {'Content-Type': 'application/json'},
routeOverTor: false,
)).thenAnswer((realInvocation) async => Response('', 400));
proxyInfo: null,
)).thenAnswer((realInvocation) async => Response(utf8.encode(''), 400));
final result =
await ChangeNowAPI.instance.getAvailableFloatingRatePairs();
final result = await instance.getAvailableFloatingRatePairs();
expect(
result.exception!.type, ExchangeExceptionType.serializeResponseError);

View file

@ -4,7 +4,8 @@
// ignore_for_file: no_leading_underscores_for_library_prefixes
import 'dart:async' as _i3;
import 'dart:convert' as _i4;
import 'dart:convert' as _i5;
import 'dart:io' as _i4;
import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/networking/http.dart' as _i2;
@ -42,7 +43,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
_i3.Future<_i2.Response> get({
required Uri? url,
Map<String, String>? headers,
required bool? routeOverTor,
required ({_i4.InternetAddress host, int port})? proxyInfo,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -51,7 +52,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
{
#url: url,
#headers: headers,
#routeOverTor: routeOverTor,
#proxyInfo: proxyInfo,
},
),
returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0(
@ -62,7 +63,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
{
#url: url,
#headers: headers,
#routeOverTor: routeOverTor,
#proxyInfo: proxyInfo,
},
),
)),
@ -72,8 +73,8 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
required Uri? url,
Map<String, String>? headers,
Object? body,
_i4.Encoding? encoding,
required bool? routeOverTor,
_i5.Encoding? encoding,
required ({_i4.InternetAddress host, int port})? proxyInfo,
}) =>
(super.noSuchMethod(
Invocation.method(
@ -84,7 +85,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
#headers: headers,
#body: body,
#encoding: encoding,
#routeOverTor: routeOverTor,
#proxyInfo: proxyInfo,
},
),
returnValue: _i3.Future<_i2.Response>.value(_FakeResponse_0(
@ -97,7 +98,7 @@ class MockHTTP extends _i1.Mock implements _i2.HTTP {
#headers: headers,
#body: body,
#encoding: encoding,
#routeOverTor: routeOverTor,
#proxyInfo: proxyInfo,
},
),
)),

View file

@ -537,7 +537,6 @@ class MockTransactionNotificationTracker extends _i1.Mock
Invocation.getter(#confirmeds),
returnValue: <String>[],
) as List<String>);
@override
bool wasNotifiedPending(String? txid) => (super.noSuchMethod(
Invocation.method(