feat: integrate enakgame and voucher
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
Build & Deploy iOS to TestFlight / build-and-deploy (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:enaklo/common/api/interceptors/unauthorized_interceptor.dart';
|
||||
import 'package:enaklo/domain/wallet/wallet.dart';
|
||||
import 'package:enaklo/infrastructure/wallet/wallet_dtos.dart';
|
||||
import 'package:enaklo/infrastructure/wallet/wallet_response.dart';
|
||||
import 'package:enaklo/presentation/pages/reward/reward_ui.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
Response<dynamic> _response(int status, Object? data) => Response(
|
||||
requestOptions: RequestOptions(path: '/'),
|
||||
statusCode: status,
|
||||
data: data,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _error(String code, String entity, String cause) => {
|
||||
'success': false,
|
||||
'data': null,
|
||||
'errors': [
|
||||
{'code': code, 'entity': entity, 'cause': cause},
|
||||
],
|
||||
};
|
||||
|
||||
void main() {
|
||||
group('token ditolak (§2.2)', () {
|
||||
test('401 dan 304 dari auth_handler berarti login ulang', () {
|
||||
expect(isTokenRejected(_response(401, null)), isTrue);
|
||||
expect(
|
||||
isTokenRejected(_response(400, _error('304', 'auth_handler', 'x'))),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
|
||||
test('304 aturan bisnis dan 403 PIN tidak dianggap logout', () {
|
||||
expect(
|
||||
isTokenRejected(
|
||||
_response(400, _error('304', 'wallet_service', 'not enough')),
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
expect(
|
||||
isTokenRejected(
|
||||
_response(403, {
|
||||
'success': false,
|
||||
'data': {'code': 'PIN_NOT_SET'},
|
||||
'errors': [
|
||||
{'code': 'PIN_NOT_SET'},
|
||||
],
|
||||
}),
|
||||
),
|
||||
isFalse,
|
||||
);
|
||||
});
|
||||
|
||||
test('parseWalletFailure memetakan auth_handler ke unauthorized', () {
|
||||
final failure = parseWalletFailure(
|
||||
_response(400, _error('304', 'auth_handler', 'token expired')),
|
||||
);
|
||||
expect(failure, const WalletFailure.unauthorized());
|
||||
});
|
||||
|
||||
test('parseWalletFailure tetap membaca error PIN dari data', () {
|
||||
final failure = parseWalletFailure(
|
||||
_response(400, {
|
||||
'success': false,
|
||||
'data': {'code': 'PIN_INVALID', 'remaining_attempts': 3},
|
||||
'errors': [
|
||||
{'code': 'PIN_INVALID'},
|
||||
],
|
||||
}),
|
||||
);
|
||||
expect(failure, const WalletFailure.pinInvalid(3));
|
||||
});
|
||||
});
|
||||
|
||||
group('tipe mutasi (§4.2)', () {
|
||||
test('tipe game dan voucher dikenali, PAYMENT tidak lagi', () {
|
||||
expect(
|
||||
WalletTransactionType.fromApi('GAME_REWARD'),
|
||||
WalletTransactionType.gameReward,
|
||||
);
|
||||
expect(
|
||||
WalletTransactionType.fromApi('REWARD_REDEEM_REFUND'),
|
||||
WalletTransactionType.rewardRedeemRefund,
|
||||
);
|
||||
expect(
|
||||
WalletTransactionType.fromApi('PAYMENT'),
|
||||
WalletTransactionType.unknown,
|
||||
);
|
||||
});
|
||||
|
||||
test('filter per tab hanya menawarkan tipe milik mata uang itu', () {
|
||||
final point = WalletTransactionType.filterableFor(WalletCurrency.point);
|
||||
final coin = WalletTransactionType.filterableFor(WalletCurrency.coin);
|
||||
|
||||
expect(point, contains(WalletTransactionType.rewardRedeem));
|
||||
expect(point, isNot(contains(WalletTransactionType.gameSpend)));
|
||||
expect(coin, contains(WalletTransactionType.gameSpendRefund));
|
||||
expect(coin, isNot(contains(WalletTransactionType.exchangeIn)));
|
||||
expect(point, isNot(contains(WalletTransactionType.unknown)));
|
||||
});
|
||||
});
|
||||
|
||||
group('penolakan tukar voucher (§9.2)', () {
|
||||
({String message, bool reloadCatalog}) describe(String cause) =>
|
||||
describeVoucherRedeemFailure(WalletFailure.rejected(cause));
|
||||
|
||||
test('setiap cause punya pesan ramah', () {
|
||||
expect(
|
||||
describe('not enough EnakPoint').message,
|
||||
'EnakPoint kamu kurang.',
|
||||
);
|
||||
expect(
|
||||
describe(
|
||||
'this voucher can be redeemed at most 2 times per customer',
|
||||
).message,
|
||||
'Kamu sudah mencapai batas penukaran voucher ini.',
|
||||
);
|
||||
expect(
|
||||
describe('the customer is not active').message,
|
||||
'Akun tidak aktif.',
|
||||
);
|
||||
});
|
||||
|
||||
test('voucher habis / tidak tersedia memuat ulang katalog', () {
|
||||
for (final cause in [
|
||||
'the voucher is out of stock',
|
||||
'the voucher is not available',
|
||||
'the voucher cannot be redeemed yet',
|
||||
'the voucher has ended',
|
||||
'the voucher is not available yet',
|
||||
]) {
|
||||
expect(describe(cause).reloadCatalog, isTrue, reason: cause);
|
||||
}
|
||||
expect(describe('not enough EnakPoint').reloadCatalog, isFalse);
|
||||
});
|
||||
|
||||
test('cause tidak pernah ditampilkan mentah', () {
|
||||
const cause =
|
||||
'this Idempotency-Key was already used to redeem another voucher';
|
||||
expect(describe(cause).message, isNot(contains('Idempotency')));
|
||||
});
|
||||
});
|
||||
|
||||
group('DTO dari contoh dokumen', () {
|
||||
test(
|
||||
'voucher katalog (§9.1): stok null tidak dihitung, 0 berarti habis',
|
||||
() {
|
||||
Voucher voucher(int? available) => VoucherDto.fromJson({
|
||||
'id': 'v1',
|
||||
'name': 'Kopi Susu Gratis',
|
||||
'voucher_type': 'FREE_ITEM',
|
||||
'face_value': 20000,
|
||||
'point_cost': 15000,
|
||||
'max_per_customer': null,
|
||||
'valid_until': '2026-12-31T16:59:59Z',
|
||||
'terms': {'a': 'b'},
|
||||
'available': available,
|
||||
}).toDomain();
|
||||
|
||||
expect(voucher(null).isOutOfStock, isFalse);
|
||||
expect(voucher(0).isOutOfStock, isTrue);
|
||||
expect(voucher(120).type, VoucherType.freeItem);
|
||||
},
|
||||
);
|
||||
|
||||
test('hasil tukar voucher (§9.2)', () {
|
||||
final result = VoucherRedemptionDto.fromJson({
|
||||
'id': 'r1',
|
||||
'voucher_id': 'v1',
|
||||
'voucher_name': 'Kopi Susu Gratis',
|
||||
'voucher_type': 'FREE_ITEM',
|
||||
'status': 'COMPLETED',
|
||||
'face_value': 20000,
|
||||
'point_cost': 15000,
|
||||
'code': 'KOPI-7F3C-2291',
|
||||
'code_expires_at': '2026-12-31T16:59:59Z',
|
||||
'point_balance': 2500,
|
||||
'replayed': true,
|
||||
}).toRedeemResult();
|
||||
|
||||
expect(result.pointBalance, 2500);
|
||||
expect(result.replayed, isTrue);
|
||||
expect(result.redemption.code, 'KOPI-7F3C-2291');
|
||||
expect(result.redemption.status, VoucherRedemptionStatus.completed);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user