From 87254ef547145c7fea49022088bd96fbf13d4fa5 Mon Sep 17 00:00:00 2001 From: Hugo Pointcheval Date: Fri, 11 Nov 2022 18:48:30 -0500 Subject: [PATCH] feat(auth): add fully functionnal mock data source --- .../lib/core/dependency_injection/get_it.dart | 26 ++- .../sign_in/widgets/sign_in_form.dart | 2 +- .../sign_up/widgets/sign_up_form.dart | 2 +- .../src/data/data_sources/data_sources.dart | 7 +- .../authentication_mock_data_source_impl.dart | 204 ++++++++++++++++++ 5 files changed, 234 insertions(+), 7 deletions(-) create mode 100644 packages/wyatt_authentication_bloc/lib/src/data/data_sources/remote/authentication_mock_data_source_impl.dart diff --git a/packages/wyatt_authentication_bloc/example_router/lib/core/dependency_injection/get_it.dart b/packages/wyatt_authentication_bloc/example_router/lib/core/dependency_injection/get_it.dart index f75d34df..a665ccb7 100644 --- a/packages/wyatt_authentication_bloc/example_router/lib/core/dependency_injection/get_it.dart +++ b/packages/wyatt_authentication_bloc/example_router/lib/core/dependency_injection/get_it.dart @@ -16,6 +16,7 @@ import 'package:get_it/get_it.dart'; import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; final getIt = GetIt.I; @@ -23,12 +24,33 @@ abstract class GetItInitializer { static Future init() async { getIt ..registerLazySingleton( - () => AuthenticationFirebaseDataSourceImpl(), + () => AuthenticationMockDataSourceImpl(registeredAccounts: [ + Pair( + AccountModel( + uid: '1', + emailVerified: true, + isAnonymous: false, + providerId: 'wyatt', + email: 'toto@test.fr', + ), + 'toto1234', + ), + Pair( + AccountModel( + uid: '2', + emailVerified: false, + isAnonymous: false, + providerId: 'wyatt', + email: 'tata@test.fr', + ), + 'tata1234', + ), + ]), ) ..registerLazySingleton>( () => AuthenticationCacheDataSourceImpl(), ); - + await getIt.allReady(); } } diff --git a/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_in/widgets/sign_in_form.dart b/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_in/widgets/sign_in_form.dart index ce5bc371..89cd4dc6 100644 --- a/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_in/widgets/sign_in_form.dart +++ b/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_in/widgets/sign_in_form.dart @@ -72,7 +72,7 @@ class SignInForm extends StatelessWidget { @override Widget build(BuildContext context) { - return SignInListener( + return SignInListener( onError: (context, status, errorMessage) => ScaffoldMessenger.of(context) ..hideCurrentSnackBar() ..showSnackBar( diff --git a/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_up/widgets/sign_up_form.dart b/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_up/widgets/sign_up_form.dart index f9a6da2e..7c62d920 100644 --- a/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_up/widgets/sign_up_form.dart +++ b/packages/wyatt_authentication_bloc/example_router/lib/presentation/features/sign_up/widgets/sign_up_form.dart @@ -109,7 +109,7 @@ class SignUpForm extends StatelessWidget { @override Widget build(BuildContext context) { - return SignUpListener( + return SignUpListener( onError: (context, status, errorMessage) => ScaffoldMessenger.of(context) ..hideCurrentSnackBar() diff --git a/packages/wyatt_authentication_bloc/lib/src/data/data_sources/data_sources.dart b/packages/wyatt_authentication_bloc/lib/src/data/data_sources/data_sources.dart index 346c8fa7..24eff56f 100644 --- a/packages/wyatt_authentication_bloc/lib/src/data/data_sources/data_sources.dart +++ b/packages/wyatt_authentication_bloc/lib/src/data/data_sources/data_sources.dart @@ -1,18 +1,19 @@ // Copyright (C) 2022 WYATT GROUP // Please see the AUTHORS file for details. -// +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . export 'local/authentication_cache_data_source_impl.dart'; export 'remote/authentication_firebase_data_source_impl.dart'; +export 'remote/authentication_mock_data_source_impl.dart'; diff --git a/packages/wyatt_authentication_bloc/lib/src/data/data_sources/remote/authentication_mock_data_source_impl.dart b/packages/wyatt_authentication_bloc/lib/src/data/data_sources/remote/authentication_mock_data_source_impl.dart new file mode 100644 index 00000000..d60ee7e8 --- /dev/null +++ b/packages/wyatt_authentication_bloc/lib/src/data/data_sources/remote/authentication_mock_data_source_impl.dart @@ -0,0 +1,204 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'dart:async'; +import 'dart:math'; + +import 'package:wyatt_authentication_bloc/wyatt_authentication_bloc.dart'; +import 'package:wyatt_type_utils/wyatt_type_utils.dart'; + +class AuthenticationMockDataSourceImpl extends AuthenticationRemoteDataSource { + Pair? _connectedMock; + Pair? _registeredMock; + final StreamController _streamAccount = StreamController(); + + final List>? registeredAccounts; + final String idToken; + + AuthenticationMockDataSourceImpl({ + this.idToken = 'fake-id-token', + this.registeredAccounts, + }); + + Future _randomDelay() async { + await Future.delayed( + Duration(milliseconds: Random().nextInt(400) + 200), + ); + return; + } + + @override + Future confirmPasswordReset({ + required String code, + required String newPassword, + }) async { + await _randomDelay(); + } + + @override + Future getIdentityToken() async { + await _randomDelay(); + return idToken; + } + + @override + Future refresh() async { + await _randomDelay(); + if (_connectedMock.isNull) { + throw RefreshFailureFirebase(); + } + final refresh = DateTime.now(); + final mock = (_connectedMock?.left as AccountModel?) + ?.copyWith(lastSignInTime: refresh); + _connectedMock = _connectedMock?.copyWith(left: mock); + _streamAccount.add(mock); + } + + @override + Future sendEmailVerification() async { + await _randomDelay(); + if (_connectedMock.isNotNull) { + final refresh = DateTime.now(); + final mock = (_connectedMock?.left as AccountModel?)?.copyWith( + emailVerified: false, + lastSignInTime: refresh, + ); + _streamAccount.add(mock); + _connectedMock = _connectedMock?.copyWith(left: mock); + return; + } + throw SendEmailVerificationFailureFirebase(); + } + + @override + Future sendPasswordResetEmail({required String email}) async { + await _randomDelay(); + if (registeredAccounts.isNotNull) { + final accounts = + registeredAccounts?.where((pair) => pair.left?.email == email); + if (accounts.isNotNullOrEmpty) { + return; + } + } + if (_registeredMock.isNotNull) { + if (_registeredMock?.left?.email != email) { + throw SendPasswordResetEmailFailureFirebase(); + } + return; + } + throw SendPasswordResetEmailFailureFirebase(); + } + + @override + Future signInAnonymously() async { + await _randomDelay(); + final creation = DateTime.now(); + final mock = AccountModel( + uid: 'mock-id-anom', + emailVerified: false, + isAnonymous: true, + providerId: 'wyatt', + creationTime: creation, + lastSignInTime: creation, + ); + _streamAccount.add(mock); + _connectedMock = _connectedMock?.copyWith(left: mock); + return Future.value(mock); + } + + @override + Future signInWithEmailAndPassword({ + required String email, + required String password, + }) async { + await _randomDelay(); + if (registeredAccounts.isNotNull) { + final accounts = + registeredAccounts?.where((pair) => pair.left?.email == email); + if (accounts.isNotNullOrEmpty) { + final account = accounts?.first; + if (account?.right != password) { + throw SignInWithCredentialFailureFirebase.fromCode('wrong-password'); + } + _streamAccount.add(account!.left); + _connectedMock = account.copyWith(); + return account.left!; + } + } + if (_registeredMock.isNotNull) { + if (_registeredMock?.left?.email != email) { + throw SignInWithCredentialFailureFirebase.fromCode('user-not-found'); + } + if (_registeredMock?.right != password) { + throw SignInWithCredentialFailureFirebase.fromCode('wrong-password'); + } + _streamAccount.add(_registeredMock!.left); + _connectedMock = _registeredMock!.copyWith(); + return _registeredMock!.left!; + } + throw SignInWithCredentialFailureFirebase(); + } + + @override + Future signOut() async { + _connectedMock = null; + _streamAccount.add(null); + } + + @override + Future signUp({ + required String email, + required String password, + }) async { + await _randomDelay(); + if (registeredAccounts.isNotNull) { + final accounts = + registeredAccounts?.where((pair) => pair.left?.email == email); + if (accounts.isNotNullOrEmpty) { + throw SignUpWithEmailAndPasswordFailureFirebase.fromCode( + 'email-already-in-use', + ); + } + } + if (_registeredMock?.left?.email == email) { + throw SignUpWithEmailAndPasswordFailureFirebase.fromCode( + 'email-already-in-use', + ); + } + final creation = DateTime.now(); + final mock = AccountModel( + uid: 'mock-id-email', + emailVerified: false, + isAnonymous: false, + providerId: 'wyatt', + email: email, + creationTime: creation, + lastSignInTime: creation, + ); + _streamAccount.add(mock); + _registeredMock = Pair(mock, password); + return Future.value(mock); + } + + @override + Stream streamAccount() => _streamAccount.stream.asBroadcastStream(); + + @override + Future verifyPasswordResetCode({required String code}) async { + await _randomDelay(); + return true; + } +}