diff --git a/test/services/api_service_test.dart b/test/services/api_service_test.dart new file mode 100644 index 0000000..1f7a745 --- /dev/null +++ b/test/services/api_service_test.dart @@ -0,0 +1,100 @@ +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); + }); + + 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 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, + ); + } +}