import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:pad_scanner/services/api_service.dart'; void main() { group('ApiService.registerLocation', () { test('returns ok on 200', () async { final mockClient = _MockClient((request) async { return http.Response( jsonEncode({ 'zongpai_no': '26B1', 'location_code': 'A01-02-03', 'created_at': '2026-05-11T10:00:00', }), 200, headers: {'content-type': 'application/json; charset=utf-8'}, ); }); final svc = ApiService(client: mockClient); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: '26B1', locationCode: 'A01-02-03', ); expect(result.success, isTrue); expect(result.isDuplicate, isFalse); expect(result.isOffShelfSuccess, isFalse); }); test('returns off-shelf success on 200 with previous_location', () async { final mockClient = _MockClient((request) async { return http.Response( jsonEncode({ 'zongpai_no': '26B1', 'location_code': 'TRANS-01', 'previous_location': 'A01-02-03', 'created_at': '2026-05-13T10:00:00', }), 200, headers: {'content-type': 'application/json; charset=utf-8'}, ); }); final svc = ApiService(client: mockClient); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: '26B1', locationCode: 'TRANS-01', ); expect(result.success, isTrue); expect(result.isOffShelfSuccess, isTrue); expect(result.isDuplicate, isFalse); }); test( 'returns duplicate on 409 with location_code and registered_at', () async { final mockClient = _MockClient((request) async { return http.Response( jsonEncode({ 'error_code': 'DUPLICATE_LOCATION', 'message': '该总排号已存在货位记录', 'location_code': 'A01-02-03', 'registered_at': '2026-05-11T10:00:00', }), 409, headers: {'content-type': 'application/json; charset=utf-8'}, ); }); final svc = ApiService(client: mockClient); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: '26B1', locationCode: 'A02-01-01', ); expect(result.success, isFalse); expect(result.isDuplicate, isTrue); expect(result.duplicateInfo?['location_code'], 'A01-02-03'); expect(result.duplicateInfo?['registered_at'], '2026-05-11T10:00:00'); }, ); test('returns already off shelf on 409 ALREADY_OFF_SHELF', () async { final mockClient = _MockClient((request) async { return http.Response( jsonEncode({ 'error_code': 'ALREADY_OFF_SHELF', 'message': '该总排号已下架至转运区域,不可重新上架', 'location_code': 'TRANS-01', 'registered_at': '2026-05-13T10:00:00', }), 409, headers: {'content-type': 'application/json; charset=utf-8'}, ); }); final svc = ApiService(client: mockClient); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: '26B1', locationCode: 'A02-01-01', ); expect(result.success, isFalse); expect(result.isDuplicate, isFalse); expect(result.isAlreadyOffShelf, isTrue); expect(result.errorMessage, '该总排号已下架至转运区域,不可重新上架'); expect(result.conflictInfo?['location_code'], 'TRANS-01'); }); test('returns error on 400 with message', () async { final mockClient = _MockClient((request) async { return http.Response( jsonEncode({ 'error_code': 'INVALID_ZONGPAI', 'message': 'INVALID_ZONGPAI', }), 400, headers: {'content-type': 'application/json; charset=utf-8'}, ); }); final svc = ApiService(client: mockClient); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: 'INVALID', locationCode: 'A01-02-03', ); expect(result.success, isFalse); expect(result.isDuplicate, isFalse); expect(result.errorMessage, 'INVALID_ZONGPAI'); }); test('returns error on network failure', () async { final mockClient = _MockClient((request) async { throw Exception('Connection refused'); }); final svc = ApiService(client: mockClient, timeout: Duration(seconds: 1)); final result = await svc.registerLocation( baseUrl: 'http://localhost', zongpaiNo: '26B1', locationCode: 'A01-02-03', ); expect(result.success, isFalse); expect(result.errorMessage, '网络异常,请检查网络连接'); }); }); } class _MockClient extends http.BaseClient { final Future Function(http.BaseRequest) _handler; _MockClient(this._handler); @override Future send(http.BaseRequest request) async { final response = await _handler(request); return http.StreamedResponse( http.ByteStream.fromBytes(response.bodyBytes), response.statusCode, headers: response.headers, reasonPhrase: response.reasonPhrase, ); } }