Skip to content

Commit

Permalink
feat: adds player leaderboard route
Browse files Browse the repository at this point in the history
  • Loading branch information
AyadLaouissi committed Mar 7, 2024
1 parent 535557b commit 7a32d89
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
Expand All @@ -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()
Expand All @@ -36,5 +36,5 @@ class LeaderboardPlayer extends Equatable {
Map<String, dynamic> toJson() => _$LeaderboardPlayerToJson(this);

@override
List<Object?> get props => [id, score, initials];
List<Object?> get props => [userId, score, initials];
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ void main() {
test('can be instantiated', () {
expect(
LeaderboardPlayer(
id: 'id',
userId: 'id',
initials: 'TST',
score: 10,
),
Expand All @@ -17,7 +17,7 @@ void main() {
});

final leaderboardPlayer = LeaderboardPlayer(
id: 'id',
userId: 'id',
initials: 'TST',
score: 20,
);
Expand Down Expand Up @@ -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,
),
Expand All @@ -63,7 +63,7 @@ void main() {

expect(
LeaderboardPlayer(
id: 'id',
userId: 'id',
initials: 'WOW',
score: 20,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,23 @@ class LeaderboardRepository {
return results
.map(
(e) => LeaderboardPlayer.fromJson({
'id': e.id,
'userId': e.id,
...e.data,
}),
)
.toList();
}

/// Saves player to the leaderboard.
Future<String> addPlayerToLeaderboard({
Future<void> addPlayerToLeaderboard({
required LeaderboardPlayer leaderboardPlayer,
}) async {
return _dbClient.add(
return _dbClient.set(
'leaderboard',
leaderboardPlayer.toJson(),
DbEntityRecord(
id: leaderboardPlayer.userId,
data: leaderboardPlayer.toJson()..remove('userId'),
),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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,
);
Expand Down Expand Up @@ -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);
});
});

Expand Down
118 changes: 92 additions & 26 deletions api/test/routes/game/leaderboard/initials/index_test.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -29,77 +32,140 @@ void main() {
.thenAnswer((_) async => blacklist);

request = _MockRequest();
when(() => request.method).thenReturn(HttpMethod.post);

context = _MockRequestContext();
when(() => context.request).thenReturn(request);
when(() => context.read<LeaderboardRepository>())
.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));
});
}
});
}
6 changes: 3 additions & 3 deletions api/test/routes/game/leaderboard/results/index_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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',
),
Expand Down

0 comments on commit 7a32d89

Please sign in to comment.