diff --git a/api/packages/game_domain/lib/src/models/leaderboard_player.dart b/api/packages/game_domain/lib/src/models/leaderboard_player.dart index 39258f8ff..b3b1fb003 100644 --- a/api/packages/game_domain/lib/src/models/leaderboard_player.dart +++ b/api/packages/game_domain/lib/src/models/leaderboard_player.dart @@ -10,7 +10,7 @@ part 'leaderboard_player.g.dart'; class LeaderboardPlayer extends Equatable { /// {@macro leaderboard_player} const LeaderboardPlayer({ - required this.id, + required this.userId, required this.initials, required this.score, }); @@ -22,7 +22,7 @@ class LeaderboardPlayer extends Equatable { /// Unique identifier of the leaderboard player object /// and session id for the player. @JsonKey() - final String id; + final String userId; /// Number of crosswords solved. @JsonKey() @@ -36,5 +36,5 @@ class LeaderboardPlayer extends Equatable { Map toJson() => _$LeaderboardPlayerToJson(this); @override - List get props => [id, score, initials]; + List get props => [userId, score, initials]; } diff --git a/api/packages/game_domain/lib/src/models/leaderboard_player.g.dart b/api/packages/game_domain/lib/src/models/leaderboard_player.g.dart index 3a9e4b119..7cd486de2 100644 --- a/api/packages/game_domain/lib/src/models/leaderboard_player.g.dart +++ b/api/packages/game_domain/lib/src/models/leaderboard_player.g.dart @@ -8,14 +8,14 @@ part of 'leaderboard_player.dart'; LeaderboardPlayer _$LeaderboardPlayerFromJson(Map json) => LeaderboardPlayer( - id: json['id'] as String, + userId: json['userId'] as String, initials: json['initials'] as String, score: json['score'] as int, ); Map _$LeaderboardPlayerToJson(LeaderboardPlayer instance) => { - 'id': instance.id, + 'userId': instance.userId, 'score': instance.score, 'initials': instance.initials, }; diff --git a/api/packages/game_domain/test/src/models/leaderboard_player_test.dart b/api/packages/game_domain/test/src/models/leaderboard_player_test.dart index 2335e3fa4..59d37b624 100644 --- a/api/packages/game_domain/test/src/models/leaderboard_player_test.dart +++ b/api/packages/game_domain/test/src/models/leaderboard_player_test.dart @@ -8,7 +8,7 @@ void main() { test('can be instantiated', () { expect( LeaderboardPlayer( - id: 'id', + userId: 'id', initials: 'TST', score: 10, ), @@ -17,7 +17,7 @@ void main() { }); final leaderboardPlayer = LeaderboardPlayer( - id: 'id', + userId: 'id', initials: 'TST', score: 20, ); @@ -46,13 +46,13 @@ void main() { test('supports equality', () { expect( - LeaderboardPlayer(id: '', initials: 'TST', score: 20), - equals(LeaderboardPlayer(id: '', initials: 'TST', score: 20)), + LeaderboardPlayer(userId: '', initials: 'TST', score: 20), + equals(LeaderboardPlayer(userId: '', initials: 'TST', score: 20)), ); expect( LeaderboardPlayer( - id: '', + userId: '', initials: 'TST', score: 20, ), @@ -63,7 +63,7 @@ void main() { expect( LeaderboardPlayer( - id: 'id', + userId: 'id', initials: 'WOW', score: 20, ), diff --git a/api/packages/leaderboard_repository/lib/src/leaderboard_repository.dart b/api/packages/leaderboard_repository/lib/src/leaderboard_repository.dart index e7f4e6714..2491efabb 100644 --- a/api/packages/leaderboard_repository/lib/src/leaderboard_repository.dart +++ b/api/packages/leaderboard_repository/lib/src/leaderboard_repository.dart @@ -24,7 +24,7 @@ class LeaderboardRepository { return results .map( (e) => LeaderboardPlayer.fromJson({ - 'id': e.id, + 'userId': e.id, ...e.data, }), ) @@ -32,12 +32,15 @@ class LeaderboardRepository { } /// Saves player to the leaderboard. - Future addPlayerToLeaderboard({ + Future addPlayerToLeaderboard({ required LeaderboardPlayer leaderboardPlayer, }) async { - return _dbClient.add( + return _dbClient.set( 'leaderboard', - leaderboardPlayer.toJson(), + DbEntityRecord( + id: leaderboardPlayer.userId, + data: leaderboardPlayer.toJson()..remove('userId'), + ), ); } diff --git a/api/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart b/api/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart index 817a1c14e..fe68ea6d2 100644 --- a/api/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart +++ b/api/packages/leaderboard_repository/test/src/leaderboard_repository_test.dart @@ -1,4 +1,6 @@ // ignore_for_file: prefer_const_constructors +// ignore_for_file: prefer_const_literals_to_create_immutables + import 'package:db_client/db_client.dart'; import 'package:game_domain/game_domain.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; @@ -35,12 +37,12 @@ void main() { group('getLeaderboard', () { test('returns list of leaderboard players', () async { const playerOne = LeaderboardPlayer( - id: 'id', + userId: 'id', initials: 'AAA', score: 20, ); const playerTwo = LeaderboardPlayer( - id: 'id2', + userId: 'id2', initials: 'BBB', score: 10, ); @@ -82,23 +84,29 @@ void main() { }); group('addPlayerToLeaderboard', () { - test('returns empty list if results are empty', () async { + test('calls set with correct entity and record', () async { final leaderboardPlayer = LeaderboardPlayer( - id: 'id', + userId: 'user-id', initials: 'initials', score: 40, ); - when(() => dbClient.add('leaderboard', leaderboardPlayer.toJson())) - .thenAnswer((_) async { - return '12345678'; - }); + final record = DbEntityRecord( + id: 'user-id', + data: { + 'initials': 'initials', + 'score': 40, + }, + ); + + when(() => dbClient.set('leaderboard', record)) + .thenAnswer((_) async {}); - final response = await leaderboardRepository.addPlayerToLeaderboard( + await leaderboardRepository.addPlayerToLeaderboard( leaderboardPlayer: leaderboardPlayer, ); - expect(response, equals('12345678')); + verify(() => dbClient.set('leaderboard', record)).called(1); }); }); diff --git a/api/test/routes/game/leaderboard/initials/index_test.dart b/api/test/routes/game/leaderboard/initials/index_test.dart index 161308b94..6dde9751e 100644 --- a/api/test/routes/game/leaderboard/initials/index_test.dart +++ b/api/test/routes/game/leaderboard/initials/index_test.dart @@ -1,6 +1,9 @@ +// ignore_for_file: prefer_const_constructors + import 'dart:io'; import 'package:dart_frog/dart_frog.dart'; +import 'package:game_domain/game_domain.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:test/test.dart'; @@ -29,7 +32,6 @@ void main() { .thenAnswer((_) async => blacklist); request = _MockRequest(); - when(() => request.method).thenReturn(HttpMethod.post); context = _MockRequestContext(); when(() => context.request).thenReturn(request); @@ -37,69 +39,133 @@ void main() { .thenReturn(leaderboardRepository); }); - test('responds with a 204 on success', () async { + test('calls addPlayerToLeaderboard with player leaderboard information', + () async { + final leaderboardPlayer = LeaderboardPlayer( + userId: 'user-id', + initials: 'AAA', + score: 10, + ); + when( () => leaderboardRepository.addPlayerToLeaderboard( - leaderboardPlayer: any(), + leaderboardPlayer: leaderboardPlayer, ), - ).thenAnswer((_) async => 'id'); + ).thenAnswer((_) async {}); - when(request.json).thenAnswer( - (_) async => { - 'initials': 'AAA', - 'score': 10, - }, + when(() => request.method).thenReturn(HttpMethod.post); + + when(request.json).thenAnswer((_) async => leaderboardPlayer.toJson()); + + await route.onRequest(context); + + verify( + () => leaderboardRepository.addPlayerToLeaderboard( + leaderboardPlayer: leaderboardPlayer, + ), + ).called(1); + }); + + test('responds with a 204 on success', () async { + final leaderboardPlayer = LeaderboardPlayer( + userId: 'user-id', + initials: 'AAA', + score: 10, ); + when(() => request.method).thenReturn(HttpMethod.post); + + when( + () => leaderboardRepository.addPlayerToLeaderboard( + leaderboardPlayer: leaderboardPlayer, + ), + ).thenAnswer((_) async {}); + + when(request.json).thenAnswer((_) async => leaderboardPlayer.toJson()); + final response = await route.onRequest(context); expect(response.statusCode, equals(HttpStatus.noContent)); }); test('responds with a 400 when request is invalid', () async { + when(() => request.method).thenReturn(HttpMethod.post); when(request.json).thenAnswer((_) async => {'test': 'test'}); + final response = await route.onRequest(context); expect(response.statusCode, equals(HttpStatus.badRequest)); }); test('responds with a 400 when initials are blacklisted', () async { + when(() => request.method).thenReturn(HttpMethod.post); when(request.json).thenAnswer( (_) async => { + 'userId': 'user-id', 'initials': 'CCC', 'score': 10, }, ); + final response = await route.onRequest(context); expect(response.statusCode, equals(HttpStatus.badRequest)); }); test('responds with a 400 when lowercase initials are blacklisted', () async { + when(() => request.method).thenReturn(HttpMethod.post); when(request.json).thenAnswer( (_) async => { + 'userId': 'user-id', 'initials': 'ccc', - 'scoreCardId': scoreCardId, + 'score': 10, }, ); - final response = await route.onRequest(context); - expect(response.statusCode, equals(HttpStatus.badRequest)); - }); - test("responds with a 400 when lowercase initials aren't 3 characters long", - () async { - when(request.json).thenAnswer( - (_) async => { - 'initials': 'aaaa', - 'scoreCardId': scoreCardId, - }, - ); final response = await route.onRequest(context); expect(response.statusCode, equals(HttpStatus.badRequest)); }); - test('allows only post methods', () async { - when(() => request.method).thenReturn(HttpMethod.get); - final response = await route.onRequest(context); - expect(response.statusCode, equals(HttpStatus.methodNotAllowed)); - }); + test( + 'responds with a 400 when initials are less than 3 characters long', + () async { + when(() => request.method).thenReturn(HttpMethod.post); + when(request.json).thenAnswer( + (_) async => { + 'userId': 'user-id', + 'initials': 'aa', + 'score': 10, + }, + ); + + final response = await route.onRequest(context); + expect(response.statusCode, equals(HttpStatus.badRequest)); + }, + ); + + test( + 'responds with a 400 when initials are more than 3 characters long', + () async { + when(() => request.method).thenReturn(HttpMethod.post); + when(request.json).thenAnswer( + (_) async => { + 'userId': 'user-id', + 'initials': 'aaaa', + 'score': 10, + }, + ); + + final response = await route.onRequest(context); + expect(response.statusCode, equals(HttpStatus.badRequest)); + }, + ); + + for (final httpMethod in HttpMethod.values.toList() + ..remove(HttpMethod.post)) { + test('does not allow $httpMethod', () async { + when(() => request.method).thenReturn(httpMethod); + + final response = await route.onRequest(context); + expect(response.statusCode, equals(HttpStatus.methodNotAllowed)); + }); + } }); } diff --git a/api/test/routes/game/leaderboard/results/index_test.dart b/api/test/routes/game/leaderboard/results/index_test.dart index dc58216a1..76a2f5c1e 100644 --- a/api/test/routes/game/leaderboard/results/index_test.dart +++ b/api/test/routes/game/leaderboard/results/index_test.dart @@ -23,17 +23,17 @@ void main() { const leaderboardPlayers = [ LeaderboardPlayer( - id: 'id', + userId: 'id', score: 1, initials: 'AAA', ), LeaderboardPlayer( - id: 'id2', + userId: 'id2', score: 2, initials: 'BBB', ), LeaderboardPlayer( - id: 'id3', + userId: 'id3', score: 3, initials: 'CCC', ),