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 '../../helpers/pet_test_helpers.dart'; 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 => [buildPet('p-1'), buildPet('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 => [buildPet('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 => [buildPet('p-1')]; await controller.refresh(); repository.createPetHandler = (request) async => buildPet('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 => [buildPet('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 => [buildPet('p-1', version: 1)]; await controller.refresh(); repository.updatePetHandler = (petId, request) async => buildPet('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 => [buildPet('p-1', version: 1)]; await controller.refresh(); repository.getPetHandler = (petId) async => buildPet('p-1', version: 5); final detail = await controller.getPet('p-1'); expect(detail.version, 5); expect(controller.pets.single.version, 5); }); test('loadBreeds:按物种缓存,仓库只打一次', () async { var calls = 0; repository.listBreedsHandler = (species) async { calls++; return [Breed.fromJson(sampleBreedJson())]; }; final first = await controller.loadBreeds(PetSpecies.dog); final second = await controller.loadBreeds(PetSpecies.dog); expect(first.single.displayName, '柴犬'); expect(identical(first, second), isTrue); expect(calls, 1); await controller.loadBreeds(PetSpecies.cat); expect(calls, 2); }); test('loadBreeds 失败:异常外抛且不缓存,重试可恢复', () async { var calls = 0; repository.listBreedsHandler = (species) async { calls++; if (calls == 1) throw const ApiNetworkException('断网'); return [Breed.fromJson(sampleBreedJson())]; }; await expectLater( controller.loadBreeds(PetSpecies.dog), throwsA(isA()), ); final breeds = await controller.loadBreeds(PetSpecies.dog); expect(breeds, hasLength(1)); }); test('reset:登出清空回 initial(含品种缓存)', () async { repository.listPetsHandler = () async => [buildPet('p-1')]; var breedCalls = 0; repository.listBreedsHandler = (species) async { breedCalls++; return [Breed.fromJson(sampleBreedJson())]; }; await controller.refresh(); await controller.loadBreeds(PetSpecies.dog); controller.reset(); expect(controller.phase, PetsLoadPhase.initial); expect(controller.pets, isEmpty); expect(controller.lastError, isNull); await controller.loadBreeds(PetSpecies.dog); expect(breedCalls, 2); }); }