import 'package:flutter_test/flutter_test.dart'; import 'package:patbond_flutter/core/network/api_exception.dart'; import 'package:patbond_flutter/features/pets/pet_exceptions.dart'; import 'package:patbond_flutter/features/pets/pet_models.dart'; import 'package:patbond_flutter/features/pets/pets_controller.dart'; import 'package:patbond_flutter/features/pets/pets_repository.dart'; import '../../helpers/pet_test_helpers.dart'; /// 假仓库:各方法可注入行为,未注入的方法抛 UnimplementedError。 class FakePetsRepository implements PetsRepository { Future> Function()? listPetsHandler; Future Function(CreatePetRequest)? createPetHandler; Future Function(String)? getPetHandler; Future Function(String, UpdatePetRequest)? updatePetHandler; @override Future> listPets() => listPetsHandler?.call() ?? Future.value(const []); @override Future createPet(CreatePetRequest request) => createPetHandler!(request); @override Future getPet(String petId) => getPetHandler!(petId); @override Future updatePet(String petId, UpdatePetRequest request) => updatePetHandler!(petId, request); @override dynamic noSuchMethod(Invocation invocation) => throw UnimplementedError('${invocation.memberName}'); } Pet pet(String id, {String name = '豆豆', int version = 1}) => Pet.fromJson( samplePetJson(overrides: {'id': id, 'name': name, 'version': version}), ); void main() { late FakePetsRepository repository; late PetsController controller; setUp(() { repository = FakePetsRepository(); controller = PetsController(repository: repository); }); test('初始态为 initial,不发请求', () { expect(controller.phase, PetsLoadPhase.initial); expect(controller.pets, isEmpty); expect(controller.isEmpty, isFalse); }); test('refresh 成功:loading → ready,列表就位', () async { repository.listPetsHandler = () async => [pet('p-1'), pet('p-2')]; final phases = []; controller.addListener(() => phases.add(controller.phase)); await controller.refresh(); expect(phases, [PetsLoadPhase.loading, PetsLoadPhase.ready]); expect(controller.pets.map((p) => p.id), ['p-1', 'p-2']); expect(controller.isEmpty, isFalse); expect(controller.lastError, isNull); }); test('refresh 空列表:ready 且 isEmpty(新用户空态)', () async { repository.listPetsHandler = () async => []; await controller.refresh(); expect(controller.phase, PetsLoadPhase.ready); expect(controller.isEmpty, isTrue); }); test('refresh 失败:error 态保留类型化错误,重试可恢复', () async { repository.listPetsHandler = () async => throw const ApiNetworkException('断网'); await controller.refresh(); expect(controller.phase, PetsLoadPhase.error); expect(controller.lastError, isA()); repository.listPetsHandler = () async => [pet('p-1')]; await controller.refresh(); expect(controller.phase, PetsLoadPhase.ready); expect(controller.lastError, isNull); expect(controller.pets, hasLength(1)); }); test('createPet:成功后插入列表头', () async { repository.listPetsHandler = () async => [pet('p-1')]; await controller.refresh(); repository.createPetHandler = (request) async => pet('p-2', name: '咪咪'); final created = await controller.createPet( const CreatePetRequest( name: '咪咪', species: PetSpecies.cat, sex: PetSex.female, customBreedName: '狸花', ), ); expect(created.id, 'p-2'); expect(controller.pets.map((p) => p.id), ['p-2', 'p-1']); }); test('createPet 失败(40903):类型化异常外抛,列表不变', () async { repository.listPetsHandler = () async => [pet('p-1')]; await controller.refresh(); repository.createPetHandler = (request) async => throw const MicrochipTakenException(message: '芯片号已被登记'); await expectLater( controller.createPet( const CreatePetRequest( name: '咪咪', species: PetSpecies.cat, sex: PetSex.female, customBreedName: '狸花', ), ), throwsA(isA()), ); expect(controller.pets, hasLength(1)); }); test('updatePet:成功后同步列表内存副本', () async { repository.listPetsHandler = () async => [pet('p-1', version: 1)]; await controller.refresh(); repository.updatePetHandler = (petId, request) async => pet('p-1', name: '豆豆二世', version: 2); await controller.updatePet( 'p-1', const UpdatePetRequest(version: 1, name: '豆豆二世'), ); expect(controller.pets.single.name, '豆豆二世'); expect(controller.pets.single.version, 2); }); test('updatePet 版本冲突(40902):异常外抛供页面提示刷新重提', () async { repository.updatePetHandler = (petId, request) async => throw const PetVersionConflictException(message: '数据已被修改'); await expectLater( controller.updatePet('p-1', const UpdatePetRequest(version: 1)), throwsA(isA()), ); }); test('getPet:详情结果回写列表副本', () async { repository.listPetsHandler = () async => [pet('p-1', version: 1)]; await controller.refresh(); repository.getPetHandler = (petId) async => pet('p-1', version: 5); final detail = await controller.getPet('p-1'); expect(detail.version, 5); expect(controller.pets.single.version, 5); }); }