From 47b74e3a9d744b1778d6d5ed272f4ab2c3b80b47 Mon Sep 17 00:00:00 2001 From: jonathandaniels-vgv <102978796+jonathandaniels-vgv@users.noreply.github.com> Date: Mon, 25 Apr 2022 12:39:28 -0700 Subject: [PATCH] feat(leaderboard_repository): enable ability to check if initials are allowed (#226) * feat(leaderboard_repository): enable ability to check if a username is allowed * chore: addressed review feedback * chore: addressed review feedback --- .../lib/src/leaderboard_repository.dart | 35 +++++++ .../test/src/leaderboard_repository_test.dart | 92 +++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/packages/leaderboard_repository/lib/src/leaderboard_repository.dart b/packages/leaderboard_repository/lib/src/leaderboard_repository.dart index 30f6810f..9d8b2434 100644 --- a/packages/leaderboard_repository/lib/src/leaderboard_repository.dart +++ b/packages/leaderboard_repository/lib/src/leaderboard_repository.dart @@ -72,6 +72,20 @@ class FetchPlayerRankingException extends LeaderboardException { ); } +/// {@template fetch_prohibited_initials_exception} +/// Exception thrown when failure occurs while fetching prohibited initials. +/// {@endtemplate} +class FetchProhibitedInitialsException extends LeaderboardException { + /// {@macro fetch_prohibited_initials_exception} + const FetchProhibitedInitialsException( + Object error, + StackTrace stackTrace, + ) : super( + error, + stackTrace, + ); +} + /// {@template leaderboard_repository} /// Repository to access leaderboard data in Firebase Cloud Firestore. /// {@endtemplate} @@ -152,4 +166,25 @@ class LeaderboardRepository { throw FetchPlayerRankingException(error, stackTrace); } } + + /// Determines if the given [initials] are allowed. + Future areInitialsAllowed({required String initials}) async { + // Initials can only be three uppercase A-Z letters + final initialsRegex = RegExp(r'^[A-Z]{3}$'); + if (!initialsRegex.hasMatch(initials)) { + return false; + } + + try { + final document = await _firebaseFirestore + .collection('prohibitedInitials') + .doc('list') + .get(); + final prohibitedInitials = + document.get('prohibitedInitials') as List; + return !prohibitedInitials.contains(initials); + } on Exception catch (error, stackTrace) { + throw FetchProhibitedInitialsException(error, stackTrace); + } + } } diff --git a/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart b/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart index 1341d3f4..9d31983f 100644 --- a/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart +++ b/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart @@ -21,6 +21,9 @@ class MockQueryDocumentSnapshot extends Mock class MockDocumentReference extends Mock implements DocumentReference> {} +class MockDocumentSnapshot extends Mock + implements DocumentSnapshot> {} + void main() { group('LeaderboardRepository', () { late FirebaseFirestore firestore; @@ -223,5 +226,94 @@ void main() { ); }); }); + + group('areInitialsAllowed', () { + late LeaderboardRepository leaderboardRepository; + late CollectionReference> collectionReference; + late DocumentReference> documentReference; + late DocumentSnapshot> documentSnapshot; + + setUp(() async { + collectionReference = MockCollectionReference(); + documentReference = MockDocumentReference(); + documentSnapshot = MockDocumentSnapshot(); + leaderboardRepository = LeaderboardRepository(firestore); + + when(() => firestore.collection('prohibitedInitials')) + .thenReturn(collectionReference); + when(() => collectionReference.doc('list')) + .thenReturn(documentReference); + when(() => documentReference.get()) + .thenAnswer((_) async => documentSnapshot); + when(() => documentSnapshot.get('prohibitedInitials')) + .thenReturn(['BAD']); + }); + + test('returns true if initials are three letters and allowed', () async { + final isUsernameAllowedResponse = + await leaderboardRepository.areInitialsAllowed( + initials: 'ABC', + ); + expect( + isUsernameAllowedResponse, + isTrue, + ); + }); + + test( + 'returns false if initials are shorter than 3 characters', + () async { + final areInitialsAllowedResponse = + await leaderboardRepository.areInitialsAllowed(initials: 'AB'); + expect(areInitialsAllowedResponse, isFalse); + }, + ); + + test( + 'returns false if initials are longer than 3 characters', + () async { + final areInitialsAllowedResponse = + await leaderboardRepository.areInitialsAllowed(initials: 'ABCD'); + expect(areInitialsAllowedResponse, isFalse); + }, + ); + + test( + 'returns false if initials contain a lowercase letter', + () async { + final areInitialsAllowedResponse = + await leaderboardRepository.areInitialsAllowed(initials: 'AbC'); + expect(areInitialsAllowedResponse, isFalse); + }, + ); + + test( + 'returns false if initials contain a special character', + () async { + final areInitialsAllowedResponse = + await leaderboardRepository.areInitialsAllowed(initials: 'A@C'); + expect(areInitialsAllowedResponse, isFalse); + }, + ); + + test('returns false if initials are forbidden', () async { + final areInitialsAllowedResponse = + await leaderboardRepository.areInitialsAllowed(initials: 'BAD'); + expect(areInitialsAllowedResponse, isFalse); + }); + + test( + 'throws FetchProhibitedInitialsException when Exception occurs ' + 'when trying to retrieve information from firestore', + () async { + when(() => firestore.collection('prohibitedInitials')) + .thenThrow(Exception('oops')); + expect( + () => leaderboardRepository.areInitialsAllowed(initials: 'ABC'), + throwsA(isA()), + ); + }, + ); + }); }); }