Files
game-tracker/test/db_tests/values/score_test.dart
Felix Kirchner 541cbe9a54
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 40s
Pull Request Pipeline / lint (pull_request) Successful in 45s
Overhauled score tests
2026-04-12 00:07:16 +02:00

773 lines
21 KiB
Dart

import 'package:clock/clock.dart';
import 'package:drift/drift.dart' hide isNull, isNotNull;
import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score.dart';
void main() {
late AppDatabase database;
late Player testPlayer1;
late Player testPlayer2;
late Player testPlayer3;
late Game testGame;
late Match testMatch1;
late Match testMatch2;
final fixedDate = DateTime(2025, 11, 19, 00, 11, 23);
final fakeClock = Clock(() => fixedDate);
setUp(() async {
database = AppDatabase(
DatabaseConnection(
NativeDatabase.memory(),
// Recommended for widget tests to avoid test errors.
closeStreamsSynchronously: true,
),
);
withClock(fakeClock, () {
testPlayer1 = Player(name: 'Alice', description: '');
testPlayer2 = Player(name: 'Bob', description: '');
testPlayer3 = Player(name: 'Charlie', description: '');
testGame = Game(
name: 'Test Game',
ruleset: Ruleset.singleWinner,
description: 'A test game',
color: GameColor.blue,
icon: '',
);
testMatch1 = Match(
name: 'Test Match 1',
game: testGame,
players: [testPlayer1, testPlayer2],
notes: '',
);
testMatch2 = Match(
name: 'Test Match 2',
game: testGame,
players: [testPlayer2, testPlayer3],
notes: '',
);
});
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3],
);
await database.gameDao.addGame(game: testGame);
await database.matchDao.addMatch(match: testMatch1);
await database.matchDao.addMatch(match: testMatch2);
});
tearDown(() async {
await database.close();
});
group('Score Tests', () {
group('Adding and Fetching scores', () {
test('Single Score', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(score, isNotNull);
expect(score!.roundNumber, 1);
expect(score.score, 10);
expect(score.change, 10);
});
test('Multiple Scores', () async {
final entryList = [
Score(roundNumber: 1, score: 5, change: 5),
Score(roundNumber: 2, score: 12, change: 7),
Score(roundNumber: 3, score: 18, change: 6),
];
await database.scoreDao.addScoresAsList(
scores: entryList,
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
final scores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(scores, isNotNull);
// Scores should be returned in order of round number
for (int i = 0; i < entryList.length; i++) {
expect(scores[i].roundNumber, entryList[i].roundNumber);
expect(scores[i].score, entryList[i].score);
expect(scores[i].change, entryList[i].change);
}
});
});
group('Undesirable values', () {
test('Score & Round can have negative values', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: -2,
score: -10,
change: -10,
);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: -2,
);
expect(score, isNotNull);
expect(score!.roundNumber, -2);
expect(score.score, -10);
expect(score.change, -10);
});
test('Score & Round can have zero values', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 0,
score: 0,
change: 0,
);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 0,
);
expect(score, isNotNull);
expect(score!.score, 0);
expect(score.change, 0);
});
test('Getting score for a non-existent entities returns null', () async {
var score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: -1,
);
expect(score, isNull);
score = await database.scoreDao.getScore(
playerId: 'non-existin-player',
matchId: testMatch1.id,
);
expect(score, isNull);
score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: 'non-existing-match',
);
expect(score, isNull);
});
test('Getting score for a non-match player returns null', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer3.id,
matchId: testMatch2.id,
roundNumber: 1,
score: 10,
change: 10,
);
var score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch2.id,
roundNumber: 1,
);
expect(score, isNull);
});
});
group('Scores in matches', () {
test('getAllMatchScores()', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 20,
change: 20,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 25,
change: 15,
);
final scores = await database.scoreDao.getAllMatchScores(
matchId: testMatch1.id,
);
expect(scores.length, 2);
expect(scores[testPlayer1.id]!.length, 2);
expect(scores[testPlayer2.id]!.length, 1);
});
test('getAllMatchScores() with no scores saved', () async {
final scores = await database.scoreDao.getAllMatchScores(
matchId: testMatch1.id,
);
expect(scores.isEmpty, true);
});
test('getAllPlayerScoresInMatch()', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 25,
change: 15,
);
await database.scoreDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 30,
change: 30,
);
final playerScores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(playerScores.length, 2);
expect(playerScores[0].roundNumber, 1);
expect(playerScores[1].roundNumber, 2);
expect(playerScores[0].score, 10);
expect(playerScores[1].score, 25);
expect(playerScores[0].change, 10);
expect(playerScores[1].change, 15);
});
test('getAllPlayerScoresInMatch() with no scores saved', () async {
final playerScores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(playerScores.isEmpty, true);
});
test('Scores are isolated across different matches', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch2.id,
roundNumber: 1,
score: 50,
change: 50,
);
final match1Scores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(match1Scores.length, 1);
expect(match1Scores[0].score, 10);
expect(match1Scores[0].change, 10);
final match2Scores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch2.id,
);
expect(match2Scores.length, 1);
expect(match2Scores[0].score, 50);
expect(match2Scores[0].change, 50);
});
});
group('Updating scores', () {
test('updateScore()', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 15,
change: 5,
);
final updated = await database.scoreDao.updateScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
newScore: 50,
newChange: 40,
);
expect(updated, true);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
);
expect(score, isNotNull);
expect(score!.score, 50);
expect(score.change, 40);
});
test('Updating a non-existent score returns false', () async {
final updated = await database.scoreDao.updateScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
newScore: 20,
newChange: 20,
);
expect(updated, false);
});
});
group('Deleting scores', () {
test('deleteScore() ', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
final deleted = await database.scoreDao.deleteScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(deleted, true);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(score, isNull);
});
test('Deleting a non-existent score returns false', () async {
final deleted = await database.scoreDao.deleteScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(deleted, false);
});
test('deleteAllScoresForMatch() works correctly', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 20,
change: 20,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch2.id,
roundNumber: 1,
score: 15,
change: 15,
);
final deleted = await database.scoreDao.deleteAllScoresForMatch(
matchId: testMatch1.id,
);
expect(deleted, true);
final match1Scores = await database.scoreDao.getAllMatchScores(
matchId: testMatch1.id,
);
expect(match1Scores.length, 0);
final match2Scores = await database.scoreDao.getAllMatchScores(
matchId: testMatch2.id,
);
expect(match2Scores.length, 1);
});
test('deleteAllScoresForPlayerInMatch() works correctly', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 15,
change: 5,
);
await database.scoreDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 6,
change: 6,
);
final deleted = await database.scoreDao.deleteAllScoresForPlayerInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(deleted, true);
final player1Scores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(player1Scores.length, 0);
final player2Scores = await database.scoreDao.getAllPlayerScoresInMatch(
playerId: testPlayer2.id,
matchId: testMatch1.id,
);
expect(player2Scores.length, 1);
});
});
group('Score Aggregations & Edge Cases', () {
test('getLatestRoundNumber()', () async {
var latestRound = await database.scoreDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, isNull);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
latestRound = await database.scoreDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 1);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 5,
score: 50,
change: 40,
);
latestRound = await database.scoreDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 5);
});
test('getLatestRoundNumber() with non-consecutive rounds', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 5,
score: 50,
change: 40,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 3,
score: 30,
change: 20,
);
final latestRound = await database.scoreDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 5);
});
test('getTotalScoreForPlayer()', () async {
var totalScore = await database.scoreDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(totalScore, 0);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 25,
change: 15,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 3,
score: 40,
change: 15,
);
totalScore = await database.scoreDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(totalScore, 40);
});
test('getTotalScoreForPlayer() ignores round score', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 25,
change: 25,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 25,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 3,
score: 25,
change: 25,
);
final totalScore = await database.scoreDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
// Should return the sum of all changes
expect(totalScore, 60);
});
test('Adding the same score twice replaces the existing one', () async {
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 10,
change: 10,
);
await database.scoreDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
score: 20,
change: 20,
);
final score = await database.scoreDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(score, isNotNull);
expect(score!.score, 20);
expect(score.change, 20);
});
});
group('Handling Winner', () {
test('hasWinner() works correctly', () async {
var hasWinner = await database.scoreDao.hasWinner(
matchId: testMatch1.id,
);
expect(hasWinner, false);
await database.scoreDao.setWinner(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
hasWinner = await database.scoreDao.hasWinner(matchId: testMatch1.id);
expect(hasWinner, true);
});
test('getWinnersForMatch() returns correct winner', () async {
var winner = await database.scoreDao.getWinner(matchId: testMatch1.id);
expect(winner, isNull);
await database.scoreDao.setWinner(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
winner = await database.scoreDao.getWinner(matchId: testMatch1.id);
expect(winner, isNotNull);
expect(winner!.id, testPlayer1.id);
});
test('removeWinner() works correctly', () async {
var removed = await database.scoreDao.removeWinner(
matchId: testMatch1.id,
);
expect(removed, false);
await database.scoreDao.setWinner(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
removed = await database.scoreDao.removeWinner(matchId: testMatch1.id);
expect(removed, true);
var winner = await database.scoreDao.getWinner(matchId: testMatch1.id);
expect(winner, isNull);
});
});
group('Handling Looser', () {
test('hasLooser() works correctly', () async {
var hasLooser = await database.scoreDao.hasLooser(
matchId: testMatch1.id,
);
expect(hasLooser, false);
await database.scoreDao.setLooser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
hasLooser = await database.scoreDao.hasLooser(matchId: testMatch1.id);
expect(hasLooser, true);
});
test('getLooser() returns correct winner', () async {
var looser = await database.scoreDao.getLooser(matchId: testMatch1.id);
expect(looser, isNull);
await database.scoreDao.setLooser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
looser = await database.scoreDao.getLooser(matchId: testMatch1.id);
expect(looser, isNotNull);
expect(looser!.id, testPlayer1.id);
});
test('removeLooser() works correctly', () async {
var removed = await database.scoreDao.removeLooser(
matchId: testMatch1.id,
);
expect(removed, false);
await database.scoreDao.setLooser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
removed = await database.scoreDao.removeLooser(matchId: testMatch1.id);
expect(removed, true);
var looser = await database.scoreDao.getLooser(matchId: testMatch1.id);
expect(looser, isNull);
});
});
});
}