import 'package:dio/dio.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http_mock_adapter/http_mock_adapter.dart'; import 'package:jiu_client/core/api/api_client.dart'; import 'package:jiu_client/core/exceptions.dart'; import 'package:jiu_client/repositories/license_repository.dart'; const _baseUrl = 'http://localhost:8080/api/v1'; class _TestApiClient extends ApiClient { final Dio _testDio; _TestApiClient(this._testDio) : super(token: 'test-token'); @override Future get(String path, {Map? params}) => _testDio.get(path, queryParameters: params); @override Future post(String path, {dynamic data}) => _testDio.post(path, data: data); } void main() { late Dio dio; late DioAdapter adapter; late LicenseRepository repo; setUp(() { dio = Dio(BaseOptions(baseUrl: _baseUrl)); adapter = DioAdapter(dio: dio, matcher: const FullHttpRequestMatcher()); repo = LicenseRepository(_TestApiClient(dio)); }); group('LicenseRepository.cancelPurchase()', () { test('200 canceled=true → 返回 true', () async { adapter.onPost( '/license/purchase/pay-abc123/cancel', (server) => server.reply(200, { 'data': {'canceled': true} }), ); final ok = await repo.cancelPurchase('pay-abc123'); expect(ok, isTrue); }); test('404 订单不存在 → 返回 false(不抛异常)', () async { adapter.onPost( '/license/purchase/pay-missing/cancel', (server) => server.reply(404, {'error': '订单不存在'}), ); final ok = await repo.cancelPurchase('pay-missing'); expect(ok, isFalse); }); test('409 已支付不可取消 → 返回 false(不抛异常)', () async { adapter.onPost( '/license/purchase/pay-paid1/cancel', (server) => server.reply(409, {'error': '订单已支付,无法取消'}), ); final ok = await repo.cancelPurchase('pay-paid1'); expect(ok, isFalse); }); test('其它错误(如 5xx)→ 抛 AppException', () async { adapter.onPost( '/license/purchase/pay-err/cancel', (server) => server.reply(502, {'error': '取消失败,请稍后重试'}), ); await expectLater( repo.cancelPurchase('pay-err'), throwsA(isA()), ); }); }); group('LicenseRepository.listPurchases()', () { test('返回订单列表 + 汇总', () async { adapter.onGet( '/license/purchases', (server) => server.reply(200, { 'data': { 'items': [ { 'out_trade_no': 'pay-1', 'product_biz_code': 'annual', 'amount_minor': 299900, 'currency': 'CNY', 'amount': '2999.00', 'status': 'paid', 'pay_url': '', 'user_name': '张三', 'created_at': '2026-07-01T10:00:00Z', 'paid_at': '2026-07-01T10:05:00Z', }, ], 'total': 1, 'summary': { 'paid_total_minor': 299900, 'paid_count': 1, 'pending_count': 0, 'total_count': 1, }, }, }), queryParameters: {'page': 1, 'page_size': 20}, ); final result = await repo.listPurchases(); expect(result.total, 1); expect(result.items.single.outTradeNo, 'pay-1'); expect(result.summary.paidCount, 1); }); test('status 筛选透传查询参数', () async { adapter.onGet( '/license/purchases', (server) => server.reply(200, { 'data': {'items': [], 'total': 0}, }), queryParameters: {'page': 1, 'page_size': 20, 'status': 'pending'}, ); final result = await repo.listPurchases(status: 'pending'); expect(result.items, isEmpty); }); test('失败 → 抛 AppException', () async { adapter.onGet( '/license/purchases', (server) => server.reply(500, {'error': '获取订单列表失败'}), queryParameters: {'page': 1, 'page_size': 20}, ); await expectLater( repo.listPurchases(), throwsA(isA()), ); }); }); }