-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add api client to app * feat: create score card object * feat: add create score call to leaderboard resource * feat: add create score endpoint * feat: create score card entry in database * fix: endpoint * fix: remove fake app check token * chore: use tear offs
- Loading branch information
1 parent
20105e1
commit 2c5a9d7
Showing
21 changed files
with
491 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import 'package:equatable/equatable.dart'; | ||
import 'package:game_domain/game_domain.dart'; | ||
import 'package:json_annotation/json_annotation.dart'; | ||
|
||
part 'score_card.g.dart'; | ||
|
||
/// {@template score_card} | ||
/// A class representing the user session's score and streak count. | ||
/// {@endtemplate} | ||
@JsonSerializable(ignoreUnannotated: true) | ||
class ScoreCard extends Equatable { | ||
/// {@macro score_card} | ||
const ScoreCard({ | ||
required this.id, | ||
this.totalScore = 0, | ||
this.streak = 0, | ||
this.mascot = Mascots.dash, | ||
this.initials = '', | ||
}); | ||
|
||
/// {@macro score_card} | ||
factory ScoreCard.fromJson(Map<String, dynamic> json) => | ||
_$ScoreCardFromJson(json); | ||
|
||
/// Unique identifier of the score object and session id for the player. | ||
@JsonKey() | ||
final String id; | ||
|
||
/// Total score of the player. | ||
@JsonKey() | ||
final int totalScore; | ||
|
||
/// Streak count of the player. | ||
@JsonKey() | ||
final int streak; | ||
|
||
/// Mascot of the player. | ||
@JsonKey() | ||
final Mascots mascot; | ||
|
||
/// Initials of the player. | ||
@JsonKey() | ||
final String initials; | ||
|
||
/// Returns a json representation from this instance. | ||
Map<String, dynamic> toJson() => _$ScoreCardToJson(this); | ||
|
||
@override | ||
List<Object?> get props => [ | ||
id, | ||
totalScore, | ||
streak, | ||
mascot, | ||
initials, | ||
]; | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
70 changes: 70 additions & 0 deletions
70
api/packages/game_domain/test/src/models/score_card_test.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// ignore_for_file: prefer_const_constructors | ||
// ignore_for_file: prefer_const_literals_to_create_immutables | ||
|
||
import 'package:game_domain/game_domain.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
void main() { | ||
group('ScoreCard', () { | ||
test('can be instantiated', () { | ||
final scoreCard = ScoreCard(id: 'id'); | ||
|
||
expect(scoreCard, isNotNull); | ||
}); | ||
|
||
test('can be instantiated with default values', () { | ||
final scoreCard = ScoreCard(id: 'id'); | ||
|
||
expect(scoreCard.totalScore, equals(0)); | ||
expect(scoreCard.streak, equals(0)); | ||
expect(scoreCard.mascot, equals(Mascots.dash)); | ||
expect(scoreCard.initials, equals('')); | ||
}); | ||
|
||
test('creates correct json object', () { | ||
final scoreCard = ScoreCard( | ||
id: 'id', | ||
totalScore: 10, | ||
streak: 5, | ||
mascot: Mascots.android, | ||
initials: 'ABC', | ||
); | ||
|
||
final json = scoreCard.toJson(); | ||
|
||
expect( | ||
json, | ||
equals({ | ||
'id': 'id', | ||
'totalScore': 10, | ||
'streak': 5, | ||
'mascot': 'android', | ||
'initials': 'ABC', | ||
}), | ||
); | ||
}); | ||
|
||
test('creates correct ScoreCard object from json', () { | ||
final scoreCard = ScoreCard.fromJson({ | ||
'id': 'id', | ||
'totalScore': 10, | ||
'streak': 5, | ||
'mascot': 'android', | ||
'initials': 'ABC', | ||
}); | ||
|
||
expect( | ||
scoreCard, | ||
equals( | ||
ScoreCard( | ||
id: 'id', | ||
totalScore: 10, | ||
streak: 5, | ||
mascot: Mascots.android, | ||
initials: 'ABC', | ||
), | ||
), | ||
); | ||
}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import 'dart:async'; | ||
import 'dart:io'; | ||
|
||
import 'package:dart_frog/dart_frog.dart'; | ||
import 'package:jwt_middleware/jwt_middleware.dart'; | ||
import 'package:leaderboard_repository/leaderboard_repository.dart'; | ||
import 'package:logging/logging.dart'; | ||
|
||
FutureOr<Response> onRequest(RequestContext context) async { | ||
if (context.request.method == HttpMethod.post) { | ||
return _onPost(context); | ||
} | ||
return Response(statusCode: HttpStatus.methodNotAllowed); | ||
} | ||
|
||
Future<Response> _onPost(RequestContext context) async { | ||
final leaderboardRepository = context.read<LeaderboardRepository>(); | ||
|
||
// TODO(jaime): get the user from the session once implemented | ||
const user = AuthenticatedUser('id'); | ||
// final user = context.read<AuthenticatedUser>(); | ||
|
||
final json = await context.request.json() as Map<String, dynamic>; | ||
final initials = json['initials'] as String?; | ||
final mascot = json['mascot'] as String?; | ||
|
||
if (initials == null || mascot == null) { | ||
return Response(statusCode: HttpStatus.badRequest); | ||
} | ||
|
||
try { | ||
await leaderboardRepository.createScore(user.id, initials, mascot); | ||
} catch (e, s) { | ||
context.read<Logger>().severe('Error creating a player score', e, s); | ||
rethrow; | ||
} | ||
|
||
return Response(statusCode: HttpStatus.created); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import 'dart:io'; | ||
|
||
import 'package:dart_frog/dart_frog.dart'; | ||
import 'package:leaderboard_repository/leaderboard_repository.dart'; | ||
import 'package:logging/logging.dart'; | ||
import 'package:mocktail/mocktail.dart'; | ||
import 'package:test/test.dart'; | ||
|
||
import '../../../routes/game/create_score.dart' as route; | ||
|
||
class _MockLeaderboardRepository extends Mock | ||
implements LeaderboardRepository {} | ||
|
||
class _MockRequest extends Mock implements Request {} | ||
|
||
class _MockRequestContext extends Mock implements RequestContext {} | ||
|
||
class _MockLogger extends Mock implements Logger {} | ||
|
||
void main() { | ||
late LeaderboardRepository leaderboardRepository; | ||
late Request request; | ||
late RequestContext context; | ||
late Logger logger; | ||
|
||
setUp(() { | ||
leaderboardRepository = _MockLeaderboardRepository(); | ||
request = _MockRequest(); | ||
logger = _MockLogger(); | ||
|
||
context = _MockRequestContext(); | ||
when(() => context.request).thenReturn(request); | ||
when(() => context.read<LeaderboardRepository>()) | ||
.thenReturn(leaderboardRepository); | ||
when(() => context.read<Logger>()).thenReturn(logger); | ||
}); | ||
|
||
group('POST', () { | ||
test( | ||
'responds with a HttpStatus.created status when creating the ' | ||
'score succeeds', | ||
() async { | ||
when(() => request.method).thenReturn(HttpMethod.post); | ||
when(request.json) | ||
.thenAnswer((_) async => {'initials': 'AAA', 'mascot': 'dash'}); | ||
when(() => leaderboardRepository.createScore(any(), any(), any())) | ||
.thenAnswer((_) async {}); | ||
|
||
final response = await route.onRequest(context); | ||
expect(response.statusCode, equals(HttpStatus.created)); | ||
}, | ||
); | ||
|
||
test( | ||
'responds with a HttpStatus.badRequest status when the initials ' | ||
'or mascot parameters are missing', | ||
() async { | ||
when(() => request.method).thenReturn(HttpMethod.post); | ||
when(request.json).thenAnswer((_) async => {'key': 'value'}); | ||
|
||
final response = await route.onRequest(context); | ||
expect(response.statusCode, equals(HttpStatus.badRequest)); | ||
}, | ||
); | ||
|
||
test( | ||
'rethrows exception if creating the score fails', | ||
() async { | ||
when(() => request.method).thenReturn(HttpMethod.post); | ||
when(request.json) | ||
.thenAnswer((_) async => {'initials': 'AAA', 'mascot': 'dash'}); | ||
when(() => leaderboardRepository.createScore(any(), any(), any())) | ||
.thenThrow(Exception()); | ||
|
||
final response = route.onRequest(context); | ||
expect(response, throwsA(isA<Exception>())); | ||
}, | ||
); | ||
}); | ||
|
||
group('Other http methods', () { | ||
for (final httpMethod in HttpMethod.values.toList() | ||
..remove(HttpMethod.post)) { | ||
test('are not allowed: $httpMethod', () async { | ||
when(() => request.method).thenReturn(httpMethod); | ||
|
||
final response = await route.onRequest(context); | ||
expect(response.statusCode, equals(HttpStatus.methodNotAllowed)); | ||
}); | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.