162 lines
5.2 KiB
Dart
162 lines
5.2 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:http/testing.dart';
|
|
|
|
import 'package:sxchnge_kyc/sxchnge_kyc.dart';
|
|
|
|
String response({String status = 'pending'}) => jsonEncode({
|
|
'status': 'success',
|
|
'data': {
|
|
'status': status,
|
|
'submissionId': 'submission',
|
|
'profile': {
|
|
'fullName': 'Legal Name',
|
|
'dateOfBirth': '1990-04-23',
|
|
'residenceCountry': 'PL',
|
|
},
|
|
},
|
|
});
|
|
void main() {
|
|
test('an approved review with an unfinished upgrade stays pending and cannot resubmit', () async {
|
|
final payload = jsonDecode(response()) as Map<String, dynamic>;
|
|
(payload['data'] as Map<String, dynamic>)['upgradePending'] = true;
|
|
final service = BackendIdentityService(
|
|
baseUri: Uri.parse('https://api.example.test'),
|
|
accessToken: () => 'token',
|
|
client: MockClient((_) async => http.Response(jsonEncode(payload), 200)),
|
|
);
|
|
addTearDown(service.close);
|
|
final result = await service.fetch();
|
|
expect(result.status, IdentityStatus.pending);
|
|
expect(result.upgradePending, true);
|
|
expect(result.canSubmit, false);
|
|
});
|
|
test(
|
|
'uploads profile plus both files, credentials scoped to current session',
|
|
() async {
|
|
final service = BackendIdentityService(
|
|
baseUri: Uri.parse('https://api.example.test/api/v1'),
|
|
accessToken: () => 'session-token',
|
|
client: MockClient((request) async {
|
|
expect(request.url.path, '/api/v1/accounts/identity');
|
|
expect(request.headers['Authorization'], 'Bearer session-token');
|
|
expect(request.followRedirects, false);
|
|
expect(
|
|
request.headers['content-type'],
|
|
contains('multipart/form-data'),
|
|
);
|
|
expect(request.body, contains('name="document"'));
|
|
expect(request.body, contains('name="selfie"'));
|
|
expect(request.body, contains('"fullName":"Legal Name"'));
|
|
expect(request.body, contains('stable-key'));
|
|
expect(request.body, isNot(contains('customer_ref')));
|
|
return http.Response(response(), 200);
|
|
}),
|
|
);
|
|
addTearDown(service.close);
|
|
final result = await service.submit(
|
|
profile: const IdentityProfile(
|
|
fullName: 'Legal Name',
|
|
dateOfBirth: '1990-04-23',
|
|
residenceCountry: 'PL',
|
|
),
|
|
passport: Photo(Uint8List.fromList([1])),
|
|
selfie: Photo(Uint8List.fromList([2])),
|
|
idempotencyKey: 'stable-key',
|
|
);
|
|
expect(result.status, IdentityStatus.pending);
|
|
expect(result.simulated, false);
|
|
},
|
|
);
|
|
test('session switch rejects a late response', () async {
|
|
var token = 'first';
|
|
final pending = Completer<http.Response>();
|
|
final service = BackendIdentityService(
|
|
baseUri: Uri.parse('https://api.example.test'),
|
|
accessToken: () => token,
|
|
client: MockClient((_) => pending.future),
|
|
);
|
|
addTearDown(service.close);
|
|
final result = service.fetch();
|
|
token = 'second';
|
|
pending.complete(http.Response(response(), 200));
|
|
await expectLater(
|
|
result,
|
|
throwsA(
|
|
isA<KycException>().having(
|
|
(e) => e.code,
|
|
'code',
|
|
KycError.sessionExpired,
|
|
),
|
|
),
|
|
);
|
|
});
|
|
test(
|
|
'production limits preserve exact decimals and never use demo rows',
|
|
() async {
|
|
final service = BackendIdentityService(
|
|
baseUri: Uri.parse('https://api.example.test'),
|
|
accessToken: () => 'token',
|
|
client: MockClient((request) async {
|
|
expect(request.url.path, '/accounts/limits');
|
|
return http.Response(
|
|
jsonEncode({
|
|
'status': 'success',
|
|
'data': {
|
|
'tier': 'verified',
|
|
'profileRef': 'verified-policy',
|
|
'entries': [
|
|
{
|
|
'operation': 'Transfer',
|
|
'period': 'day',
|
|
'currency': 'BTC',
|
|
'cap': '0.12345678',
|
|
'remaining': '0.10000001',
|
|
'used': '0.02',
|
|
'reserved': '0.00345677',
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
200,
|
|
);
|
|
}),
|
|
);
|
|
addTearDown(service.close);
|
|
final result = await BackendLimitsService(service).fetch();
|
|
expect(result.rows, isEmpty);
|
|
expect(result.simulated, false);
|
|
expect(result.entries.single.cap, '0.12345678');
|
|
expect(result.tier, AccountTier.verified);
|
|
},
|
|
);
|
|
test(
|
|
'unavailable, conflict and expired sessions retain typed recovery',
|
|
() async {
|
|
for (final pair in [
|
|
(503, KycError.unavailable),
|
|
(409, KycError.conflict),
|
|
(401, KycError.sessionExpired),
|
|
]) {
|
|
final service = BackendIdentityService(
|
|
baseUri: Uri.parse('https://api.example.test'),
|
|
accessToken: () => 'token',
|
|
client: MockClient(
|
|
(_) async => http.Response('private upstream details', pair.$1),
|
|
),
|
|
);
|
|
addTearDown(service.close);
|
|
await expectLater(
|
|
service.fetch(),
|
|
throwsA(isA<KycException>().having((e) => e.code, 'code', pair.$2)),
|
|
);
|
|
}
|
|
},
|
|
);
|
|
}
|