import 'dart:async'; import 'package:flutter_test/flutter_test.dart'; import 'package:sxchnge_auth/sxchnge_auth.dart'; class ControlledService implements LoginService, SignupService { final calls = >[]; LoginRequest? loginInput; SignupRequest? signupInput; Future next() { final call = Completer(); calls.add(call); return call.future; } @override Future login(LoginRequest request) { loginInput = request; return next(); } @override Future signup(SignupRequest request) { signupInput = request; return next(); } } void main() { const result = AuthResult(userId: 'demo', isSimulated: true); for (final signup in [false, true]) { group(signup ? 'signup' : 'login', () { late ControlledService service; late AuthProvider provider; Future submit() => signup ? (provider as SignupProvider).signup( name: ' Alex ', email: ' a@example.com ', password: ' secret ', locale: 'ru', ) : (provider as LoginProvider).login( email: ' a@example.com ', password: ' secret ', locale: 'ru', ); setUp(() { service = ControlledService(); provider = signup ? SignupProvider(service) : LoginProvider(service); }); test( 'loading, duplicate suppression, result and normalized input', () async { final states = []; provider.addListener(() => states.add(provider.state.status)); final first = submit(); await submit(); expect(service.calls, hasLength(1)); expect(provider.state.isLoading, isTrue); if (signup) { expect(service.signupInput!.name, 'Alex'); expect(service.signupInput!.email, 'a@example.com'); expect(service.signupInput!.password, ' secret '); expect(service.signupInput!.locale, 'ru'); } else { expect(service.loginInput!.email, 'a@example.com'); expect(service.loginInput!.password, ' secret '); expect(service.loginInput!.locale, 'ru'); } service.calls.single.complete(result); await first; expect(states, [AuthStatus.loading, AuthStatus.success]); expect(provider.state.result, same(result)); provider.dispose(); }, ); test('typed failure and retry clear previous error', () async { final first = submit(); service.calls.last.completeError( const AuthException(AuthError.unavailable), ); await first; expect(provider.state.error, AuthError.unavailable); final retry = submit(); expect(provider.state.error, isNull); service.calls.last.complete(result); await retry; expect(provider.state.status, AuthStatus.success); provider.dispose(); }); test('unknown exceptions are sanitized', () async { final first = submit(); service.calls.last.completeError(StateError('secret password')); await first; expect(provider.state.error, AuthError.unexpected); expect(provider.state.result, isNull); provider.dispose(); }); test('reset rejects stale completion during a new operation', () async { final first = submit(); provider.reset(); expect(provider.state.status, AuthStatus.idle); final second = submit(); service.calls.first.complete(result); await first; expect(provider.state.status, AuthStatus.loading); service.calls.last.complete(result); await second; expect(provider.state.status, AuthStatus.success); provider.dispose(); }); test('dispose ignores late failures and further submissions', () async { var notifications = 0; provider.addListener(() => notifications++); final pending = submit(); provider.dispose(); service.calls.last.completeError( const AuthException(AuthError.unavailable), ); await pending; await submit(); expect(notifications, 1); expect(service.calls, hasLength(1)); }); }); } test('dummy services simulate success and configured errors', () async { const login = LoginRequest( email: 'a@example.com', password: 'secret', locale: 'en', ); const signup = SignupRequest( name: 'Alex', email: 'a@example.com', password: 'secret', locale: 'ru', ); expect( (await const DummyLoginService(delay: Duration.zero).login(login)) .isSimulated, isTrue, ); final accepted = await const DummySignupService(delay: Duration.zero) .signup(signup); expect(accepted.isSimulated, isTrue); expect(accepted.delivery?.email, 'a@example.com'); expect(accepted.delivery?.isSimulated, isTrue); await expectLater( const DummyLoginService( delay: Duration.zero, failure: AuthError.invalidCredentials, ).login(login), throwsA( isA().having( (e) => e.code, 'code', AuthError.invalidCredentials, ), ), ); await expectLater( const DummySignupService( delay: Duration.zero, failure: AuthError.emailInUse, ).signup(signup), throwsA( isA().having( (e) => e.code, 'code', AuthError.emailInUse, ), ), ); }); }