Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: add userId to Word #593

Merged
merged 7 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class CrosswordRepository {
/// solvedTimestamp attribute.
/// The second value returns true if the answer was previously answered.
Future<(bool, bool)> answerWord(
String userId,
String wordId,
Mascot mascot,
String userAnswer,
Expand Down Expand Up @@ -134,6 +135,13 @@ class CrosswordRepository {
);
}

if (word.userId == userId) {
throw CrosswordRepositoryBadRequestException(
'Word with id $wordId was already solved by current user',
StackTrace.current,
);
}

// The word has been solved previously
if (word.solvedTimestamp != null) {
return (true, true);
Expand All @@ -143,6 +151,7 @@ class CrosswordRepository {
answer: correctAnswer.answer,
solvedTimestamp: clock.now().millisecondsSinceEpoch,
mascot: mascot,
userId: userId,
);
updatedSection = updatedSection.copyWith(
words: [...section.words..remove(word), solvedWord],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,12 @@ stackTrace: $stackTrace
''';
}
}

/// {@template crossword_repository_bad_request_exception}
/// Exception thrown when a bad request is made to the [CrosswordRepository].
/// {@endtemplate}
class CrosswordRepositoryBadRequestException
extends CrosswordRepositoryException {
/// {@macro crossword_repository_bad_request_exception}
CrosswordRepositoryBadRequestException(super.cause, super.stackTrace);
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ void main() {
axis: WordAxis.horizontal,
answer: 'hap y',
clue: '',
userId: 'userId',
);

final word4 = Word(
Expand Down Expand Up @@ -296,7 +297,12 @@ void main() {
final time = DateTime.now();
final clock = Clock.fixed(time);
await withClock(clock, () async {
final valid = await repository.answerWord('4', Mascot.dino, 'solved');
final valid = await repository.answerWord(
'userId',
'4',
Mascot.dino,
'solved',
);
expect(valid, (true, true));
});
});
Expand All @@ -306,8 +312,12 @@ void main() {
final time = DateTime.now();
final clock = Clock.fixed(time);
await withClock(clock, () async {
final valid =
await repository.answerWord('1', Mascot.dino, 'flutter');
final valid = await repository.answerWord(
'userId',
'1',
Mascot.dino,
'flutter',
);
expect(valid, (true, false));

verify(
Expand Down Expand Up @@ -339,6 +349,7 @@ void main() {
solvedTimestamp: time.millisecondsSinceEpoch,
mascot: Mascot.dino,
answer: 'flutter',
userId: 'userId',
)
.toJson(),
word2.copyWith(answer: 'bl ').toJson(),
Expand All @@ -351,7 +362,12 @@ void main() {
});

test('returns (false, false) if answer is incorrect', () async {
final valid = await repository.answerWord('1', Mascot.dino, 'android');
final valid = await repository.answerWord(
'userId',
'1',
Mascot.dino,
'android',
);
expect(valid, (false, false));
});

Expand All @@ -362,7 +378,12 @@ void main() {
() => dbClient.getById(answersCollection, 'fake'),
).thenAnswer((_) async => null);
expect(
() => repository.answerWord('fake', Mascot.dino, 'flutter'),
() => repository.answerWord(
'userId',
'fake',
Mascot.dino,
'flutter',
),
throwsA(isA<CrosswordRepositoryException>()),
);
},
Expand Down Expand Up @@ -395,7 +416,12 @@ void main() {
).thenAnswer((_) async => answersRecord);

expect(
() => repository.answerWord('3', Mascot.sparky, 'happy'),
() => repository.answerWord(
'userId2',
'3',
Mascot.sparky,
'happy',
),
throwsA(isA<CrosswordRepositoryException>()),
);
},
Expand All @@ -412,7 +438,12 @@ void main() {
).thenAnswer((_) async => []);

expect(
() => repository.answerWord('1', Mascot.dino, 'flutter'),
() => repository.answerWord(
'userId',
'1',
Mascot.dino,
'flutter',
),
throwsA(isA<CrosswordRepositoryException>()),
);
},
Expand All @@ -434,11 +465,57 @@ void main() {
() => dbClient.getById(answersCollection, 'fake'),
).thenAnswer((_) async => answersRecord);
expect(
() => repository.answerWord('fake', Mascot.dino, 'flutter'),
() => repository.answerWord(
'userId',
'fake',
Mascot.dino,
'flutter',
),
throwsA(isA<CrosswordRepositoryException>()),
);
},
);

test(
'throws $CrosswordRepositoryBadRequestException if user '
'has already solved word',
() async {
final answersRecord = _MockDbEntityRecord();
when(() => answersRecord.id).thenReturn('3');
when(() => answersRecord.data).thenReturn({
'userId': 'userId',
'answer': 'happy',
'sections': [
{'x': 1, 'y': 1},
],
'collidedWords': <Map<String, dynamic>>[
{
'wordId': '6',
'position': 1,
'character': 'l',
'sections': [
{'x': 1, 'y': 1},
{'x': 1, 'y': 2},
],
}
],
});

when(
() => dbClient.getById(answersCollection, '3'),
).thenAnswer((_) async => answersRecord);

expect(
() => repository.answerWord(
'userId',
'3',
Mascot.sparky,
'happy',
),
throwsA(isA<CrosswordRepositoryBadRequestException>()),
);
},
);
});

group('updateSolvedWordsCount', () {
Expand Down
8 changes: 8 additions & 0 deletions api/packages/game_domain/lib/src/models/word.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class Word extends Equatable {
required this.answer,
this.solvedTimestamp,
this.mascot,
this.userId,
});

/// {@macro word}
Expand Down Expand Up @@ -68,6 +69,10 @@ class Word extends Equatable {
@JsonKey()
final Mascot? mascot;

/// The user id that first solved the word.
@JsonKey()
final String? userId;

/// Returns the solved characters with the index position and character
/// solved.
Map<int, String> get solvedCharacters {
Expand Down Expand Up @@ -96,6 +101,7 @@ class Word extends Equatable {
String? answer,
int? solvedTimestamp,
Mascot? mascot,
String? userId,
}) {
return Word(
id: id ?? this.id,
Expand All @@ -105,6 +111,7 @@ class Word extends Equatable {
answer: answer ?? this.answer,
solvedTimestamp: solvedTimestamp ?? this.solvedTimestamp,
mascot: mascot ?? this.mascot,
userId: userId ?? this.userId,
);
}

Expand All @@ -117,6 +124,7 @@ class Word extends Equatable {
answer,
solvedTimestamp,
mascot,
userId,
];
}

Expand Down
2 changes: 2 additions & 0 deletions api/packages/game_domain/lib/src/models/word.g.dart

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 @@ -19,6 +19,7 @@ void main() {
clue: 'clue',
solvedTimestamp: 1234,
mascot: Mascot.android,
userId: 'userId',
),
],
);
Expand All @@ -37,6 +38,7 @@ void main() {
'clue': 'clue',
'solvedTimestamp': 1234,
'mascot': 'android',
'userId': 'userId',
},
],
}),
Expand All @@ -56,6 +58,7 @@ void main() {
'clue': 'clue',
'solvedTimestamp': 1234,
'mascot': 'android',
'userId': 'userId',
},
],
};
Expand All @@ -75,6 +78,7 @@ void main() {
clue: 'clue',
solvedTimestamp: 1234,
mascot: Mascot.android,
userId: 'userId',
),
],
),
Expand Down
1 change: 1 addition & 0 deletions api/packages/game_domain/test/src/models/word_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ void main() {
'clue': 'clue',
'solvedTimestamp': 0,
'mascot': 'sparky',
'userId': null,
}),
);
});
Expand Down
6 changes: 6 additions & 0 deletions api/routes/game/answer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Future<Response> _onPost(RequestContext context) async {

try {
final (valid, preSolved) = await crosswordRepository.answerWord(
user.id,
jsgalarraga marked this conversation as resolved.
Show resolved Hide resolved
wordId,
player.mascot,
answer,
Expand All @@ -52,6 +53,11 @@ Future<Response> _onPost(RequestContext context) async {
}

return Response.json(body: {'points': points});
} on CrosswordRepositoryBadRequestException catch (e) {
return Response(
body: e.toString(),
statusCode: HttpStatus.badRequest,
);
} catch (e) {
return Response(
body: e.toString(),
Expand Down
Loading