import 'package:bloc_test/bloc_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:formz/formz.dart'; import 'package:mocktail/mocktail.dart'; import 'package:social_app/core/form_inputs/form_inputs.dart'; import 'package:social_app/features/auth/data/auth_repository.dart'; import 'package:social_app/features/auth/data/models/auth_response.dart'; import 'package:social_app/features/auth/data/models/signup_request.dart'; import 'package:social_app/features/auth/presentation/cubits/register_cubit.dart'; import 'package:social_app/core/api/api_exception.dart'; class MockAuthRepository extends Mock implements AuthRepository {} void main() { late RegisterCubit cubit; late MockAuthRepository mockRepository; setUp(() { mockRepository = MockAuthRepository(); cubit = RegisterCubit(mockRepository); registerFallbackValue( SignupStartRequest(username: '', email: '', password: ''), ); registerFallbackValue(SignupResendRequest(email: '')); }); tearDown(() { cubit.close(); }); group('RegisterCubit', () { test('initial state has pure status', () { expect(cubit.state.status, FormzSubmissionStatus.initial); }); blocTest( 'usernameChanged updates username', build: () => cubit, act: (c) => c.usernameChanged('testuser'), expect: () => [isA()], ); blocTest( 'emailChanged updates email', build: () => cubit, act: (c) => c.emailChanged('test@example.com'), expect: () => [isA()], ); blocTest( 'passwordChanged updates password', build: () => cubit, act: (c) => c.passwordChanged('password123'), expect: () => [isA()], ); }); group('sendCodeSilently', () { blocTest( 'sets isSending to true then false on success', build: () => cubit, seed: () => RegisterState( username: const Username.dirty('testuser'), email: const Email.dirty('test@example.com'), password: const Password.dirty('password123'), ), setUp: () { when(() => mockRepository.signupStart(any())).thenAnswer( (_) async => SignupStartResponse( status: 'ok', email: 'test@example.com', message: 'Code sent', ), ); }, act: (c) => c.sendCodeSilently(), expect: () => [ predicate((state) => state.isSending == true), predicate( (state) => state.isSending == false && state.codeSent == true && state.pendingEmail == 'test@example.com' && state.errorMessage == null, ), ], verify: (_) { verify(() => mockRepository.signupStart(any())).called(1); }, ); blocTest( 'restores isSending to false and sets errorMessage and failure status on error', build: () => cubit, seed: () => RegisterState( username: const Username.dirty('testuser'), email: const Email.dirty('test@example.com'), password: const Password.dirty('password123'), ), setUp: () { when( () => mockRepository.signupStart(any()), ).thenThrow(ServerException('Network error')); }, act: (c) => c.sendCodeSilently(), expect: () => [ predicate((state) => state.isSending == true), predicate( (state) => state.isSending == false && state.errorMessage == 'Network error' && state.status == FormzSubmissionStatus.failure, ), ], verify: (_) { verify(() => mockRepository.signupStart(any())).called(1); }, ); blocTest( 'does not call signupStart when input is invalid', build: () => cubit, seed: () => RegisterState( username: const Username.dirty(''), email: const Email.dirty('invalid'), password: const Password.dirty(''), ), act: (c) => c.sendCodeSilently(), expect: () => [], verify: (_) { verifyNever(() => mockRepository.signupStart(any())); }, ); blocTest( 'returns early when isSending is true', build: () => cubit, seed: () => RegisterState( username: const Username.dirty('testuser'), email: const Email.dirty('test@example.com'), password: const Password.dirty('password123'), isSending: true, ), act: (c) => c.sendCodeSilently(), expect: () => [], verify: (_) { verifyNever(() => mockRepository.signupStart(any())); }, ); }); group('resendCode', () { blocTest( 'returns false and sets failure status when pendingEmail is null', build: () => cubit, seed: () => RegisterState(pendingEmail: null), act: (c) => c.resendCode(), expect: () => [ isA() .having((s) => s.status, 'status', FormzSubmissionStatus.failure) .having((s) => s.errorMessage, 'errorMessage', '验证码发送失败,请返回上一步重试'), ], verify: (_) { verifyNever(() => mockRepository.signupResend(any())); }, ); blocTest( 'returns true and sets status on success', build: () => cubit, seed: () => RegisterState(pendingEmail: 'test@example.com'), setUp: () { when(() => mockRepository.signupResend(any())).thenAnswer( (_) async => SignupStartResponse( status: 'ok', email: 'test@example.com', message: 'Code sent', ), ); }, act: (c) => c.resendCode(), expect: () => [ isA().having( (s) => s.status, 'status', FormzSubmissionStatus.inProgress, ), isA().having( (s) => s.status, 'status', FormzSubmissionStatus.success, ), ], verify: (_) { verify(() => mockRepository.signupResend(any())).called(1); }, ); blocTest( 'returns false on error', build: () => cubit, seed: () => RegisterState(pendingEmail: 'test@example.com'), setUp: () { when( () => mockRepository.signupResend(any()), ).thenThrow(ServerException('Network error')); }, act: (c) => c.resendCode(), expect: () => [ isA().having( (s) => s.status, 'status', FormzSubmissionStatus.inProgress, ), isA().having( (s) => s.status, 'status', FormzSubmissionStatus.failure, ), ], ); }); }