- Move 01_contract_mapping files to archive - Add alt_api_result.dart contract implementation - Add alt_api_result_test.dart
138 lines
4.6 KiB
Dart
138 lines
4.6 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:alt_client/src/contracts/alt_api_result.dart';
|
|
import 'package:alt_client/src/generated/alt/v1/backtest.pb.dart';
|
|
import 'package:alt_client/src/generated/alt/v1/common.pb.dart';
|
|
import 'package:alt_client/src/generated/alt/v1/market.pb.dart';
|
|
|
|
void main() {
|
|
group('AltApiError mapping', () {
|
|
test('fromErrorInfo preserves stable code and message', () {
|
|
final info = ErrorInfo(code: 'NOT_FOUND', message: 'instrument missing');
|
|
|
|
final error = AltApiError.fromErrorInfo(info);
|
|
|
|
expect(error.code, equals('NOT_FOUND'));
|
|
expect(error.message, equals('instrument missing'));
|
|
});
|
|
|
|
test('keeps empty fields when ErrorInfo is empty', () {
|
|
final error = AltApiError.fromErrorInfo(ErrorInfo());
|
|
|
|
expect(error.code, isEmpty);
|
|
expect(error.message, isEmpty);
|
|
});
|
|
|
|
test('value equality is based on code and message', () {
|
|
final a = AltApiError.fromErrorInfo(
|
|
ErrorInfo(code: 'E1', message: 'm'),
|
|
);
|
|
final b = const AltApiError(code: 'E1', message: 'm');
|
|
|
|
expect(a, equals(b));
|
|
expect(a.hashCode, equals(b.hashCode));
|
|
});
|
|
});
|
|
|
|
group('AltApiResult variants', () {
|
|
test('success carries the decoded value', () {
|
|
const result = AltApiResult<int>.success(42);
|
|
|
|
expect(result, isA<AltApiSuccess<int>>());
|
|
expect((result as AltApiSuccess<int>).value, equals(42));
|
|
});
|
|
|
|
test('apiError carries the mapped error', () {
|
|
final error = AltApiError.fromErrorInfo(
|
|
ErrorInfo(code: 'BAD', message: 'rejected'),
|
|
);
|
|
|
|
final result = AltApiResult<int>.apiError(error);
|
|
|
|
expect(result, isA<AltApiFailure<int>>());
|
|
expect((result as AltApiFailure<int>).error, equals(error));
|
|
});
|
|
|
|
test('transportFailure stays a stable state without ErrorInfo', () {
|
|
const result = AltApiResult<int>.transportFailure(
|
|
AltTransportKind.timeout,
|
|
detail: 'no response within 5s',
|
|
);
|
|
|
|
expect(result, isA<AltTransportFailure<int>>());
|
|
final failure = result as AltTransportFailure<int>;
|
|
expect(failure.kind, equals(AltTransportKind.timeout));
|
|
expect(failure.detail, equals('no response within 5s'));
|
|
});
|
|
|
|
test('transportFailure supports a bare kind with no detail', () {
|
|
const result =
|
|
AltApiResult<int>.transportFailure(AltTransportKind.disconnected);
|
|
|
|
final failure = result as AltTransportFailure<int>;
|
|
expect(failure.kind, equals(AltTransportKind.disconnected));
|
|
expect(failure.detail, isNull);
|
|
});
|
|
|
|
test('exhaustive switch covers every variant', () {
|
|
String describe(AltApiResult<int> result) {
|
|
return switch (result) {
|
|
AltApiSuccess<int>() => 'success',
|
|
AltApiFailure<int>() => 'apiError',
|
|
AltTransportFailure<int>() => 'transport',
|
|
};
|
|
}
|
|
|
|
expect(describe(const AltApiResult<int>.success(1)), equals('success'));
|
|
expect(
|
|
describe(
|
|
const AltApiResult<int>.apiError(
|
|
AltApiError(code: 'E', message: 'm'),
|
|
),
|
|
),
|
|
equals('apiError'),
|
|
);
|
|
expect(
|
|
describe(
|
|
const AltApiResult<int>.transportFailure(AltTransportKind.unknown),
|
|
),
|
|
equals('transport'),
|
|
);
|
|
});
|
|
});
|
|
|
|
group('altResponseError extraction', () {
|
|
test('reads typed error from each supported response', () {
|
|
final error = ErrorInfo(code: 'X', message: 'boom');
|
|
|
|
final responses = <Object>[
|
|
ListInstrumentsResponse(error: error),
|
|
ListBarsResponse(error: error),
|
|
StartBacktestResponse(error: error),
|
|
GetBacktestRunResponse(error: error),
|
|
GetBacktestResultResponse(error: error),
|
|
ListBacktestRunsResponse(error: error),
|
|
GetBacktestRunDetailResponse(error: error),
|
|
CompareBacktestRunsResponse(error: error),
|
|
];
|
|
|
|
for (final response in responses) {
|
|
final extracted = altResponseError(response);
|
|
expect(extracted, isNotNull, reason: '${response.runtimeType}');
|
|
expect(extracted!.code, equals('X'));
|
|
expect(extracted.message, equals('boom'));
|
|
}
|
|
});
|
|
|
|
test('returns null when a supported response has no error set', () {
|
|
expect(altResponseError(ListInstrumentsResponse()), isNull);
|
|
expect(altResponseError(ListBarsResponse()), isNull);
|
|
expect(altResponseError(GetBacktestRunDetailResponse()), isNull);
|
|
});
|
|
|
|
test('returns null for unsupported response types', () {
|
|
expect(altResponseError(HelloResponse()), isNull);
|
|
expect(altResponseError(ListInstrumentsRequest()), isNull);
|
|
expect(altResponseError('not a message'), isNull);
|
|
});
|
|
});
|
|
}
|