Implementierung der Games #203

Merged
flixcoo merged 34 commits from feature/119-implementierung-der-games-2 into development 2026-05-09 17:13:46 +00:00
33 changed files with 3302 additions and 3553 deletions
Showing only changes of commit b9cfab1447 - Show all commits

View File

@@ -10,39 +10,7 @@ part 'game_dao.g.dart';
class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
GameDao(super.db);
/// Retrieves all games from the database.
Future<List<Game>> getAllGames() async {
final query = select(gameTable);
final result = await query.get();
return result
.map(
(row) => Game(
id: row.id,
name: row.name,
ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset),
description: row.description,
color: GameColor.values.firstWhere((e) => e.name == row.color),
icon: row.icon,
createdAt: row.createdAt,
),
)
.toList();
}
/// Retrieves a [Game] by its [gameId].
Future<Game> getGameById({required String gameId}) async {
final query = select(gameTable)..where((g) => g.id.equals(gameId));
final result = await query.getSingle();
return Game(
id: result.id,
name: result.name,
ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset),
description: result.description,
color: GameColor.values.firstWhere((e) => e.name == result.color),
icon: result.icon,
createdAt: result.createdAt,
);
}
/* Create */
/// Adds a new [game] to the database.
/// If a game with the same ID already exists, no action is taken.
@@ -94,12 +62,15 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
return true;
}
/// Deletes the game with the given [gameId] from the database.
/// Returns `true` if the game was deleted, `false` if the game did not exist.
Future<bool> deleteGame({required String gameId}) async {
final query = delete(gameTable)..where((g) => g.id.equals(gameId));
final rowsAffected = await query.go();
return rowsAffected > 0;
/* Read */
/// Retrieves the total count of games in the database.
Future<int> getGameCount() async {
final count =
await (selectOnly(gameTable)..addColumns([gameTable.id.count()]))
.map((row) => row.read(gameTable.id.count()))
.getSingle();
return count ?? 0;
}
/// Checks if a game with the given [gameId] exists in the database.
@@ -110,63 +81,110 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
return result != null;
}
/// Updates the name of the game with the given [gameId] to [newName].
Future<void> updateGameName({
required String gameId,
required String newName,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(name: Value(newName)),
/// Retrieves all games from the database.
Future<List<Game>> getAllGames() async {
final query = select(gameTable);
final result = await query.get();
return result
.map(
(row) => Game(
id: row.id,
name: row.name,
ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset),
description: row.description,
color: GameColor.values.firstWhere((e) => e.name == row.color),
icon: row.icon,
createdAt: row.createdAt,
),
)
.toList();
}
/// Retrieves a [Game] by its [gameId].
Future<Game> getGameById({required String gameId}) async {
final query = select(gameTable)..where((g) => g.id.equals(gameId));
final result = await query.getSingle();
return Game(
id: result.id,
name: result.name,
ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset),
description: result.description,
color: GameColor.values.firstWhere((e) => e.name == result.color),
icon: result.icon,
createdAt: result.createdAt,
);
}
/* Update */
/// Updates the name of the game with the given [gameId] to [name].
Future<bool> updateGameName({
required String gameId,
required String name,
}) async {
final rowsAffected =
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(name: Value(name)),
);
return rowsAffected > 0;
}
/// Updates the ruleset of the game with the given [gameId].
Future<void> updateGameRuleset({
Future<bool> updateGameRuleset({
required String gameId,
required Ruleset newRuleset,
required Ruleset ruleset,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(ruleset: Value(newRuleset.name)),
);
final rowsAffected =
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(ruleset: Value(ruleset.name)),
);
return rowsAffected > 0;
}
/// Updates the description of the game with the given [gameId].
Future<void> updateGameDescription({
Future<bool> updateGameDescription({
required String gameId,
required String newDescription,
required String description,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(description: Value(newDescription)),
);
final rowsAffected =
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(description: Value(description)),
);
return rowsAffected > 0;
}
/// Updates the color of the game with the given [gameId].
Future<void> updateGameColor({
Future<bool> updateGameColor({
required String gameId,
required GameColor newColor,
required GameColor color,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(color: Value(newColor.name)),
);
final rowsAffected =
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(color: Value(color.name)),
);
return rowsAffected > 0;
}
/// Updates the icon of the game with the given [gameId].
Future<void> updateGameIcon({
Future<bool> updateGameIcon({
required String gameId,
required String newIcon,
required String icon,
}) async {
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(icon: Value(newIcon)),
);
final rowsAffected =
await (update(gameTable)..where((g) => g.id.equals(gameId))).write(
GameTableCompanion(icon: Value(icon)),
);
return rowsAffected > 0;
}
/// Retrieves the total count of games in the database.
Future<int> getGameCount() async {
final count =
await (selectOnly(gameTable)..addColumns([gameTable.id.count()]))
.map((row) => row.read(gameTable.id.count()))
.getSingle();
return count ?? 0;
/* Delete */
/// Deletes the game with the given [gameId] from the database.
/// Returns `true` if the game was deleted, `false` if the game did not exist.
Future<bool> deleteGame({required String gameId}) async {
final query = delete(gameTable)..where((g) => g.id.equals(gameId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Deletes all games from the database.

View File

@@ -12,43 +12,7 @@ part 'group_dao.g.dart';
class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
GroupDao(super.db);
/// Retrieves all groups from the database.
Future<List<Group>> getAllGroups() async {
final query = select(groupTable);
final result = await query.get();
return Future.wait(
result.map((groupData) async {
final members = await db.playerGroupDao.getPlayersOfGroup(
groupId: groupData.id,
);
return Group(
id: groupData.id,
name: groupData.name,
description: groupData.description,
members: members,
createdAt: groupData.createdAt,
);
}),
);
}
/// Retrieves a [Group] by its [groupId], including its members.
Future<Group> getGroupById({required String groupId}) async {
final query = select(groupTable)..where((g) => g.id.equals(groupId));
final result = await query.getSingle();
List<Player> members = await db.playerGroupDao.getPlayersOfGroup(
groupId: groupId,
);
return Group(
id: result.id,
name: result.name,
description: result.description,
members: members,
createdAt: result.createdAt,
);
}
/* Create */
/// Adds a new group with the given [id] and [name] to the database.
/// This method also adds the group's members to the [PlayerGroupTable].
@@ -172,38 +136,44 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
});
}
/// Deletes the group with the given [id] from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteGroup({required String groupId}) async {
final query = (delete(groupTable)..where((g) => g.id.equals(groupId)));
final rowsAffected = await query.go();
return rowsAffected > 0;
/* Read */
/// Retrieves all groups from the database.
Future<List<Group>> getAllGroups() async {
final query = select(groupTable);
final result = await query.get();
return Future.wait(
result.map((groupData) async {
final members = await db.playerGroupDao.getPlayersOfGroup(
groupId: groupData.id,
);
return Group(
id: groupData.id,
name: groupData.name,
description: groupData.description,
members: members,
createdAt: groupData.createdAt,
);
}),
);
}
/// Updates the name of the group with the given [id] to [newName].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateGroupName({
required String groupId,
required String newName,
}) async {
final rowsAffected =
await (update(groupTable)..where((g) => g.id.equals(groupId))).write(
GroupTableCompanion(name: Value(newName)),
);
return rowsAffected > 0;
}
/// Retrieves a [Group] by its [groupId], including its members.
Future<Group> getGroupById({required String groupId}) async {
final query = select(groupTable)..where((g) => g.id.equals(groupId));
final result = await query.getSingle();
/// Updates the description of the group with the given [groupId] to [newDescription].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateGroupDescription({
required String groupId,
required String newDescription,
}) async {
final rowsAffected =
await (update(groupTable)..where((g) => g.id.equals(groupId))).write(
GroupTableCompanion(description: Value(newDescription)),
);
return rowsAffected > 0;
List<Player> members = await db.playerGroupDao.getPlayersOfGroup(
groupId: groupId,
);
return Group(
id: result.id,
name: result.name,
description: result.description,
members: members,
createdAt: result.createdAt,
);
}
/// Retrieves the number of groups in the database.
@@ -223,6 +193,16 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
return result != null;
}
/* Delete */
/// Deletes the group with the given [id] from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteGroup({required String groupId}) async {
final query = (delete(groupTable)..where((g) => g.id.equals(groupId)));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Deletes all groups from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteAllGroups() async {
@@ -231,47 +211,31 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
return rowsAffected > 0;
}
/// Replaces all players in a group with the provided list of players.
/// Removes all existing players from the group and adds the new players.
/// Also adds any new players to the player table if they don't exist.
/// Returns `true` if the group exists and players were replaced, `false` otherwise.
Future<bool> replaceGroupPlayers({
/* Update */
/// Updates the name of the group with the given [id] to [name].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateGroupName({
required String groupId,
required List<Player> newPlayers,
required String name,
}) async {
if (!await groupExists(groupId: groupId)) return false;
final rowsAffected =
await (update(groupTable)..where((g) => g.id.equals(groupId))).write(
GroupTableCompanion(name: Value(name)),
);
return rowsAffected > 0;
}
await db.transaction(() async {
// Remove all existing players from the group
final deleteQuery = delete(db.playerGroupTable)
..where((p) => p.groupId.equals(groupId));
await deleteQuery.go();
// Add new players to the player table if they don't exist
await Future.wait(
newPlayers.map((player) async {
if (!await db.playerDao.playerExists(playerId: player.id)) {
await db.playerDao.addPlayer(player: player);
}
}),
);
// Add the new players to the group
await db.batch(
(b) => b.insertAll(
db.playerGroupTable,
newPlayers
.map(
(player) => PlayerGroupTableCompanion.insert(
playerId: player.id,
groupId: groupId,
),
)
.toList(),
mode: InsertMode.insertOrReplace,
),
);
});
return true;
/// Updates the description of the group with the given [groupId] to [description].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateGroupDescription({
required String groupId,
required String description,
}) async {
final rowsAffected =
await (update(groupTable)..where((g) => g.id.equals(groupId))).write(
GroupTableCompanion(description: Value(description)),
);
return rowsAffected > 0;
}
}

View File

@@ -8,6 +8,7 @@ import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart';
import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/team.dart';
part 'match_dao.g.dart';
@@ -15,74 +16,13 @@ part 'match_dao.g.dart';
class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
MatchDao(super.db);
/// Retrieves all matches from the database.
Future<List<Match>> getAllMatches() async {
final query = select(matchTable);
final result = await query.get();
/* Create */
return Future.wait(
result.map((row) async {
final game = await db.gameDao.getGameById(gameId: row.gameId);
Group? group;
if (row.groupId != null) {
group = await db.groupDao.getGroupById(groupId: row.groupId!);
}
final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? [];
final scores = await db.scoreEntryDao.getAllMatchScores(
matchId: row.id,
);
return Match(
id: row.id,
name: row.name,
game: game,
group: group,
players: players,
notes: row.notes ?? '',
createdAt: row.createdAt,
endedAt: row.endedAt,
scores: scores,
);
}),
);
}
/// Retrieves a [Match] by its [matchId].
Future<Match> getMatchById({required String matchId}) async {
final query = select(matchTable)..where((g) => g.id.equals(matchId));
final result = await query.getSingle();
final game = await db.gameDao.getGameById(gameId: result.gameId);
Group? group;
if (result.groupId != null) {
group = await db.groupDao.getGroupById(groupId: result.groupId!);
}
final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? [];
final scores = await db.scoreEntryDao.getAllMatchScores(matchId: matchId);
return Match(
id: result.id,
name: result.name,
game: game,
group: group,
players: players,
notes: result.notes ?? '',
createdAt: result.createdAt,
endedAt: result.endedAt,
scores: scores,
);
}
/// Adds a new [Match] to the database. Also adds players associations.
/// Adds a new [Match] to the database. Also adds players associations and teams.
/// This method assumes that the game and group (if any) are already present
/// in the database.
Future<void> addMatch({required Match match}) async {
Future<bool> addMatch({required Match match}) async {
if (await matchExists(matchId: match.id)) return false;
await db.transaction(() async {
await into(matchTable).insert(
MatchTableCompanion.insert(
@@ -90,18 +30,36 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
gameId: match.game.id,
groupId: Value(match.group?.id),
name: match.name,
notes: Value(match.notes),
notes: match.notes,
createdAt: match.createdAt,
endedAt: Value(match.endedAt),
),
mode: InsertMode.insertOrReplace,
);
// Add teams
if (match.teams != null && match.teams!.isNotEmpty) {
await db.teamDao.addTeamsAsList(teams: match.teams!, matchId: match.id);
}
// Collect all player IDs that are already in teams
final playersInTeams = <String>{};
if (match.teams != null) {
for (final team in match.teams!) {
for (final member in team.members) {
playersInTeams.add(member.id);
}
}
}
// Add players that are not in teams
for (final p in match.players) {
await db.playerMatchDao.addPlayerToMatch(
matchId: match.id,
playerId: p.id,
);
if (!playersInTeams.contains(p.id)) {
await db.playerMatchDao.addPlayerToMatch(
matchId: match.id,
playerId: p.id,
);
}
}
for (final pid in match.scores.keys) {
@@ -115,14 +73,15 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
}
}
});
return true;
}
/// Adds multiple [Match]es to the database in a batch operation.
/// Also adds associated players and groups if they exist.
/// If the [matches] list is empty, the method returns immediately.
/// This method should only be used to import matches from a different device.
Future<void> addMatchAsList({required List<Match> matches}) async {
if (matches.isEmpty) return;
Future<bool> addMatchesAsList({required List<Match> matches}) async {
if (matches.isEmpty) return false;
await db.transaction(() async {
// Add all games first (deduplicated)
final uniqueGames = <String, Game>{};
@@ -183,7 +142,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
gameId: match.game.id,
groupId: Value(match.group?.id),
name: match.name,
notes: Value(match.notes),
notes: match.notes,
createdAt: match.createdAt,
endedAt: Value(match.endedAt),
),
@@ -279,15 +238,28 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
}
}
});
// Add teams for matches
for (final match in matches) {
if (match.teams != null && match.teams!.isNotEmpty) {
await db.teamDao.addTeamsAsList(
teams: match.teams!,
matchId: match.id,
);
}
}
});
return true;
}
/// Deletes the match with the given [matchId] from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteMatch({required String matchId}) async {
final query = delete(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.go();
return rowsAffected > 0;
/* Read */
/// Checks if a match with the given [matchId] exists in the database.
/// Returns `true` if the match exists, otherwise `false`.
Future<bool> matchExists({required String matchId}) async {
final query = select(matchTable)..where((g) => g.id.equals(matchId));
final result = await query.getSingleOrNull();
return result != null;
}
/// Retrieves the number of matches in the database.
@@ -299,6 +271,76 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return count ?? 0;
}
/// Retrieves all matches from the database.
Future<List<Match>> getAllMatches() async {
final query = select(matchTable);
final result = await query.get();
return Future.wait(
result.map((row) async {
final game = await db.gameDao.getGameById(gameId: row.gameId);
Group? group;
if (row.groupId != null) {
group = await db.groupDao.getGroupById(groupId: row.groupId!);
}
final players = await db.playerMatchDao.getPlayersOfMatch(
matchId: row.id,
);
final scores = await db.scoreEntryDao.getAllMatchScores(
matchId: row.id,
);
final teams = await _getMatchTeams(matchId: row.id);
return Match(
id: row.id,
name: row.name,
game: game,
group: group,
players: players,
teams: teams.isEmpty ? null : teams,
notes: row.notes,
createdAt: row.createdAt,
endedAt: row.endedAt,
scores: scores,
);
}),
);
}
/// Retrieves a [Match] by its [matchId].
Future<Match> getMatchById({required String matchId}) async {
final query = select(matchTable)..where((g) => g.id.equals(matchId));
final result = await query.getSingle();
final game = await db.gameDao.getGameById(gameId: result.gameId);
Group? group;
if (result.groupId != null) {
group = await db.groupDao.getGroupById(groupId: result.groupId!);
}
final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId);
final scores = await db.scoreEntryDao.getAllMatchScores(matchId: matchId);
final teams = await _getMatchTeams(matchId: matchId);
return Match(
id: result.id,
name: result.name,
game: game,
group: group,
players: players,
teams: teams.isEmpty ? null : teams,
notes: result.notes,
createdAt: result.createdAt,
endedAt: result.endedAt,
scores: scores,
);
}
/// Retrieves the number of matches associated with a specific game.
Future<int> getMatchCountByGame({required String gameId}) async {
final count =
@@ -310,14 +352,6 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return count ?? 0;
}
sneeex marked this conversation as resolved
Review

würde man das nicht eher in game dao packen? weil es geht doch um games und die damit assozierten matches und nicht andersrum, das steht auch im string da falsch

würde man das nicht eher in game dao packen? weil es geht doch um games und die damit assozierten matches und nicht andersrum, das steht auch im string da falsch
Review

Nein, ich will die Anzahl an Matches mit einer spezifischen Game-ID. Ich frage ja auch den Match Table an, deswegen ists in der matchDao

Nein, ich will die Anzahl an Matches mit einer spezifischen Game-ID. Ich frage ja auch den Match Table an, deswegen ists in der matchDao
/// Deletes all matches associated with a specific game.
/// Returns the number of matches deleted.
Future<int> deleteMatchesByGame({required String gameId}) async {
final query = delete(matchTable)..where((m) => m.gameId.equals(gameId));
final rowsAffected = await query.go();
return rowsAffected;
}
/// Retrieves all matches associated with the given [groupId].
/// Queries the database directly, filtering by [groupId].
Future<List<Match>> getGroupMatches({required String groupId}) async {
@@ -328,15 +362,18 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
rows.map((row) async {
final game = await db.gameDao.getGameById(gameId: row.gameId);
final group = await db.groupDao.getGroupById(groupId: groupId);
final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? [];
final players = await db.playerMatchDao.getPlayersOfMatch(
matchId: row.id,
);
final teams = await _getMatchTeams(matchId: row.id);
return Match(
id: row.id,
name: row.name,
game: game,
group: group,
players: players,
notes: row.notes ?? '',
teams: teams.isEmpty ? null : teams,
notes: row.notes,
createdAt: row.createdAt,
endedAt: row.endedAt,
);
@@ -344,19 +381,56 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
);
}
/// Checks if a match with the given [matchId] exists in the database.
/// Returns `true` if the match exists, otherwise `false`.
Future<bool> matchExists({required String matchId}) async {
final query = select(matchTable)..where((g) => g.id.equals(matchId));
final result = await query.getSingleOrNull();
return result != null;
/// Helper method to retrieve teams for a specific match
Future<List<Team>> _getMatchTeams({required String matchId}) async {
// Get all unique team IDs from PlayerMatchTable for this match
final playerMatchQuery = select(db.playerMatchTable)
..where((pm) => pm.matchId.equals(matchId) & pm.teamId.isNotNull());
final playerMatches = await playerMatchQuery.get();
if (playerMatches.isEmpty) return [];
final teamIds = playerMatches
.map((pm) => pm.teamId)
.whereType<String>()
.toSet()
.toList();
// Fetch all teams
final teams = await Future.wait(
teamIds.map((teamId) => db.teamDao.getTeamById(teamId: teamId)),
);
return teams;
}
/// Deletes all matches from the database.
/* Update */
/// Changes the name of the match with the given [matchId] to [name].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteAllMatches() async {
final query = delete(matchTable);
final rowsAffected = await query.go();
Future<bool> updateMatchName({
required String matchId,
required String name,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(name: Value(name)),
);
return rowsAffected > 0;
}
/// Updates the group of the match with the given [matchId].
/// Replaces the existing group association with the new group specified by [groupId].
/// Pass null to remove the group association.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchGroup({
required String matchId,
required String? groupId,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(groupId: Value(groupId)),
);
return rowsAffected > 0;
}
@@ -364,7 +438,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchNotes({
required String matchId,
required String? notes,
required String notes,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
@@ -373,47 +447,6 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return rowsAffected > 0;
}
/// Changes the name of the match with the given [matchId] to [newName].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchName({
required String matchId,
required String newName,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(name: Value(newName)),
);
return rowsAffected > 0;
}
/// Updates the game of the match with the given [matchId].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchGame({
required String matchId,
required String gameId,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(gameId: Value(gameId)),
);
return rowsAffected > 0;
}
/// Updates the group of the match with the given [matchId].
/// Replaces the existing group association with the new group specified by [newGroupId].
/// Pass null to remove the group association.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchGroup({
required String matchId,
required String? newGroupId,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(groupId: Value(newGroupId)),
);
return rowsAffected > 0;
}
/// Removes the group association of the match with the given [matchId].
/// Sets the groupId to null.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
@@ -425,25 +458,12 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return rowsAffected > 0;
}
/// Updates the createdAt timestamp of the match with the given [matchId].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchCreatedAt({
required String matchId,
required DateTime createdAt,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
MatchTableCompanion(createdAt: Value(createdAt)),
);
return rowsAffected > 0;
}
/// Updates the endedAt timestamp of the match with the given [matchId].
/// Pass null to remove the ended time (mark match as ongoing).
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updateMatchEndedAt({
required String matchId,
required DateTime? endedAt,
required DateTime endedAt,
}) async {
final query = update(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.write(
@@ -452,37 +472,29 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
return rowsAffected > 0;
}
/// Replaces all players in a match with the provided list of players.
/// Removes all existing players from the match and adds the new players.
/// Also adds any new players to the player table if they don't exist.
Future<void> replaceMatchPlayers({
required String matchId,
required List<Player> newPlayers,
}) async {
await db.transaction(() async {
// Remove all existing players from the match
final deleteQuery = delete(db.playerMatchTable)
..where((p) => p.matchId.equals(matchId));
await deleteQuery.go();
/* Delete */
// Add new players to the player table if they don't exist
await Future.wait(
newPlayers.map((player) async {
if (!await db.playerDao.playerExists(playerId: player.id)) {
await db.playerDao.addPlayer(player: player);
}
}),
);
/// Deletes the match with the given [matchId] from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteMatch({required String matchId}) async {
final query = delete(matchTable)..where((g) => g.id.equals(matchId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
// Add the new players to the match
await Future.wait(
newPlayers.map(
(player) => db.playerMatchDao.addPlayerToMatch(
matchId: matchId,
playerId: player.id,
),
),
);
});
/// Deletes all matches from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteAllMatches() async {
final query = delete(matchTable);
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Deletes all matches associated with a specific game.
/// Returns the number of matches deleted.
Future<int> deleteMatchesByGame({required String gameId}) async {
final query = delete(matchTable)..where((m) => m.gameId.equals(gameId));
final rowsAffected = await query.go();
return rowsAffected;
}
sneeex marked this conversation as resolved
Review

hier genauso?

hier genauso?
Review

Ich arbeite auf der Match-Table, deswegen ists in der matchDao

Ich arbeite auf der Match-Table, deswegen ists in der matchDao
}

View File

@@ -10,35 +10,7 @@ part 'player_dao.g.dart';
class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
PlayerDao(super.db);
/// Retrieves all players from the database.
Future<List<Player>> getAllPlayers() async {
final query = select(playerTable);
final result = await query.get();
return result
.map(
(row) => Player(
id: row.id,
name: row.name,
description: row.description,
createdAt: row.createdAt,
nameCount: row.nameCount,
),
)
.toList();
}
/// Retrieves a [Player] by their [id].
Future<Player> getPlayerById({required String playerId}) async {
final query = select(playerTable)..where((p) => p.id.equals(playerId));
final result = await query.getSingle();
return Player(
id: result.id,
name: result.name,
description: result.description,
createdAt: result.createdAt,
nameCount: result.nameCount,
);
}
/* Create */
/// Adds a new [player] to the database.
/// If a player with the same ID already exists, updates their name to
@@ -135,12 +107,15 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
return true;
}
/// Deletes the player with the given [id] from the database.
/// Returns `true` if the player was deleted, `false` if the player did not exist.
Future<bool> deletePlayer({required String playerId}) async {
final query = delete(playerTable)..where((p) => p.id.equals(playerId));
final rowsAffected = await query.go();
return rowsAffected > 0;
/* Read */
/// Retrieves the total count of players in the database.
Future<int> getPlayerCount() async {
final count =
await (selectOnly(playerTable)..addColumns([playerTable.id.count()]))
.map((row) => row.read(playerTable.id.count()))
.getSingle();
return count ?? 0;
}
/// Checks if a player with the given [playerId] exists in the database.
@@ -151,10 +126,42 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
return result != null;
}
/// Updates the name of the player with the given [playerId] to [newName].
Future<void> updatePlayerName({
/// Retrieves all players from the database.
Future<List<Player>> getAllPlayers() async {
final query = select(playerTable);
final result = await query.get();
return result
.map(
(row) => Player(
id: row.id,
name: row.name,
description: row.description,
createdAt: row.createdAt,
nameCount: row.nameCount,
),
)
.toList();
}
/// Retrieves a [Player] by their [id].
Future<Player> getPlayerById({required String playerId}) async {
final query = select(playerTable)..where((p) => p.id.equals(playerId));
final result = await query.getSingle();
return Player(
id: result.id,
name: result.name,
description: result.description,
createdAt: result.createdAt,
nameCount: result.nameCount,
);
}
/* Update */
/// Updates the name of the player with the given [playerId] to [name].
Future<bool> updatePlayerName({
required String playerId,
required String newName,
required String name,
}) async {
// Get previous name and name count for the player before updating
final previousPlayerName =
@@ -164,14 +171,15 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
'';
final previousNameCount = await getNameCount(name: previousPlayerName);
await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
PlayerTableCompanion(name: Value(newName)),
);
final rowsAffected =
await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
PlayerTableCompanion(name: Value(name)),
);
// Update name count for the new name
final count = await calculateNameCount(name: newName);
final count = await calculateNameCount(name: name);
if (count > 0) {
await (update(playerTable)..where((p) => p.name.equals(newName))).write(
await (update(playerTable)..where((p) => p.name.equals(name))).write(
PlayerTableCompanion(nameCount: Value(count)),
);
}
@@ -188,17 +196,35 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
);
}
}
return rowsAffected > 0;
}
/// Retrieves the total count of players in the database.
Future<int> getPlayerCount() async {
final count =
await (selectOnly(playerTable)..addColumns([playerTable.id.count()]))
.map((row) => row.read(playerTable.id.count()))
.getSingle();
return count ?? 0;
/// Updates the description of the player with the given [playerId] to
/// [description].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> updatePlayerDescription({
required String playerId,
required String description,
}) async {
final rowsAffected =
await (update(playerTable)..where((g) => g.id.equals(playerId))).write(
PlayerTableCompanion(description: Value(description)),
);
return rowsAffected > 0;
}
/* Delete */
/// Deletes the player with the given [id] from the database.
/// Returns `true` if the player was deleted, `false` if the player did not exist.
Future<bool> deletePlayer({required String playerId}) async {
final query = delete(playerTable)..where((p) => p.id.equals(playerId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/* Name count management */
/// Retrieves the count of players with the given [name].
Future<int> getNameCount({required String name}) async {
final query = select(playerTable)..where((p) => p.name.equals(name));

View File

@@ -11,8 +11,7 @@ class PlayerGroupDao extends DatabaseAccessor<AppDatabase>
with _$PlayerGroupDaoMixin {
PlayerGroupDao(super.db);
/// No need for a groupHasPlayers method since the members attribute is
/// not nullable
/* Create */
/// Adds a [player] to a group with the given [groupId].
/// If the player is already in the group, no action is taken.
@@ -33,10 +32,11 @@ class PlayerGroupDao extends DatabaseAccessor<AppDatabase>
await into(playerGroupTable).insert(
PlayerGroupTableCompanion.insert(playerId: player.id, groupId: groupId),
);
return true;
}
/* Read */
/// Retrieves all players belonging to a specific group by [groupId].
Future<List<Player>> getPlayersOfGroup({required String groupId}) async {
final query = select(playerGroupTable)
@@ -53,18 +53,6 @@ class PlayerGroupDao extends DatabaseAccessor<AppDatabase>
return groupMembers;
}
/// Removes a player from a group based on [playerId] and [groupId].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> removePlayerFromGroup({
required String playerId,
required String groupId,
}) async {
final query = delete(playerGroupTable)
..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Checks if a player with [playerId] is in the group with [groupId].
/// Returns `true` if the player is in the group, otherwise `false`.
Future<bool> isPlayerInGroup({
@@ -76,4 +64,65 @@ class PlayerGroupDao extends DatabaseAccessor<AppDatabase>
final result = await query.getSingleOrNull();
return result != null;
}
/* Update */
/// Replaces all players in a group with the provided list of players.
/// Removes all existing players from the group and adds the new players.
/// Also adds any new players to the player table if they don't exist.
/// Returns `true` if the group exists and players were replaced, `false` otherwise.
Future<bool> replaceGroupPlayers({
required String groupId,
required List<Player> newPlayers,
}) async {
if (!await db.groupDao.groupExists(groupId: groupId)) return false;
if (newPlayers.isEmpty) return false;
await db.transaction(() async {
// Remove all existing players from the group
final deleteQuery = delete(db.playerGroupTable)
..where((p) => p.groupId.equals(groupId));
await deleteQuery.go();
// Add new players to the player table if they don't exist
await Future.wait(
newPlayers.map((player) async {
if (!await db.playerDao.playerExists(playerId: player.id)) {
await db.playerDao.addPlayer(player: player);
}
}),
);
// Add the new players to the group
await db.batch(
(b) => b.insertAll(
db.playerGroupTable,
newPlayers
.map(
(player) => PlayerGroupTableCompanion.insert(
playerId: player.id,
groupId: groupId,
),
)
.toList(),
mode: InsertMode.insertOrReplace,
),
);
});
return true;
}
/* Delete */
/// Removes a player from a group based on [playerId] and [groupId].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> removePlayerFromGroup({
required String playerId,
required String groupId,
}) async {
final query = delete(playerGroupTable)
..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
}

View File

@@ -11,14 +11,16 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
with _$PlayerMatchDaoMixin {
PlayerMatchDao(super.db);
/* Create */
/// Associates a player with a match by inserting a record into the
/// [PlayerMatchTable]. Optionally associates with a team and sets initial score.
Future<void> addPlayerToMatch({
Future<bool> addPlayerToMatch({
required String matchId,
required String playerId,
String? teamId,
}) async {
await into(playerMatchTable).insert(
final rowsAffected = await into(playerMatchTable).insert(
PlayerMatchTableCompanion.insert(
playerId: playerId,
matchId: matchId,
@@ -26,42 +28,14 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
),
mode: InsertMode.insertOrReplace,
);
}
/// Retrieves a list of [Player]s associated with the given [matchId].
/// Returns null if no players are found.
Future<List<Player>?> getPlayersOfMatch({required String matchId}) async {
final result = await (select(
playerMatchTable,
)..where((p) => p.matchId.equals(matchId))).get();
if (result.isEmpty) return null;
final futures = result.map(
(row) => db.playerDao.getPlayerById(playerId: row.playerId),
);
final players = await Future.wait(futures);
return players;
}
/// Updates the team for a player in a match.
/// Returns `true` if the update was successful, otherwise `false`.
Future<bool> updatePlayerTeam({
required String matchId,
required String playerId,
required String? teamId,
}) async {
final rowsAffected =
await (update(playerMatchTable)..where(
(p) => p.matchId.equals(matchId) & p.playerId.equals(playerId),
))
.write(PlayerMatchTableCompanion(teamId: Value(teamId)));
return rowsAffected > 0;
}
/* Read */
/// Checks if there are any players associated with the given [matchId].
/// Returns `true` if there are players, otherwise `false`.
Future<bool> matchHasPlayers({required String matchId}) async {
Future<bool> hasMatchPlayers({required String matchId}) async {
final count =
await (selectOnly(playerMatchTable)
..where(playerMatchTable.matchId.equals(matchId))
@@ -87,31 +61,79 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
return (count ?? 0) > 0;
}
/// Removes the association of a player with a match by deleting the record
/// from the [PlayerMatchTable].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> removePlayerFromMatch({
/// Retrieves a list of [Player]s associated with the given [matchId].
/// Returns empty list if no players are found.
Future<List<Player>> getPlayersOfMatch({required String matchId}) async {
final result = await (select(
playerMatchTable,
)..where((p) => p.matchId.equals(matchId))).get();
if (result.isEmpty) return [];
final futures = result.map(
(row) => db.playerDao.getPlayerById(playerId: row.playerId),
);
final players = await Future.wait(futures);
return players;
}
/// Retrieves a list of [Player]s associated with a specific team in a match.
/// Returns empty list if no players are found for the team in the match.
Future<List<Player>> getPlayersOfTeamInMatch({
required String matchId,
required String teamId,
}) async {
final result =
await (select(playerMatchTable)
..where((p) => p.matchId.equals(matchId))
..where((p) => p.teamId.equals(teamId)))
.get();
if (result.isEmpty) return [];
final futures = result.map(
(row) => db.playerDao.getPlayerById(playerId: row.playerId),
);
final players = await Future.wait(futures);
return players;
}
/* Updated */
/// Updates the team for a player in a match.
/// Returns `true` if the update was successful, otherwise `false`.
Future<bool> updatePlayersTeam({
required String matchId,
required String playerId,
required String? teamId,
}) async {
final query = delete(playerMatchTable)
..where((pg) => pg.matchId.equals(matchId))
..where((pg) => pg.playerId.equals(playerId));
final rowsAffected = await query.go();
final rowsAffected =
await (update(playerMatchTable)..where(
(p) => p.matchId.equals(matchId) & p.playerId.equals(playerId),
))
.write(PlayerMatchTableCompanion(teamId: Value(teamId)));
return rowsAffected > 0;
}
/// Updates the players associated with a match based on the provided
/// [newPlayer] list. It adds new players and removes players that are no
/// [player] list. It adds new players and removes players that are no
/// longer associated with the match.
Future<void> updatePlayersFromMatch({
Future<bool> updateMatchPlayers({
required String matchId,
required List<Player> newPlayer,
required List<Player> player,
}) async {
if (player.isEmpty) return false;
final currentPlayers = await getPlayersOfMatch(matchId: matchId);
// Create sets of player IDs for easy comparison
final currentPlayerIds = currentPlayers?.map((p) => p.id).toSet() ?? {};
final newPlayerIdsSet = newPlayer.map((p) => p.id).toSet();
final currentPlayerIds = currentPlayers.map((p) => p.id).toSet();
final newPlayerIdsSet = player.map((p) => p.id).toSet();
// Are the current and new player identical?
if (currentPlayerIds.containsAll(newPlayerIdsSet) &&
newPlayerIdsSet.containsAll(currentPlayerIds)) {
return false;
}
// Determine players to add and remove
final playersToAdd = newPlayerIdsSet.difference(currentPlayerIds);
@@ -147,22 +169,22 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
);
}
});
return true;
}
/// Retrieves all players in a specific team for a match.
Future<List<Player>> getPlayersInTeam({
/* Delete */
/// Removes the association of a player with a match by deleting the record
/// from the [PlayerMatchTable].
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> removePlayerFromMatch({
required String matchId,
required String teamId,
required String playerId,
}) async {
final result = await (select(
playerMatchTable,
)..where((p) => p.matchId.equals(matchId) & p.teamId.equals(teamId))).get();
if (result.isEmpty) return [];
final futures = result.map(
(row) => db.playerDao.getPlayerById(playerId: row.playerId),
);
return Future.wait(futures);
final query = delete(playerMatchTable)
..where((pg) => pg.matchId.equals(matchId))
..where((pg) => pg.playerId.equals(playerId));
final rowsAffected = await query.go();
return rowsAffected > 0;
}
}

View File

@@ -13,6 +13,8 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
with _$ScoreEntryDaoMixin {
ScoreEntryDao(super.db);
/* Create */
/// Adds a score entry to the database.
Future<void> addScore({
required String playerId,
@@ -58,6 +60,8 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
});
}
/* Read */
/// Retrieves the score for a specific round.
Future<ScoreEntry?> getScore({
required String playerId,
@@ -126,28 +130,58 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
);
}
/// Gets the highest (latest) round number for a match.
/// Returns `null` if there are no scores for the match.
Future<int?> getLatestRoundNumber({required String matchId}) async {
final query = selectOnly(scoreEntryTable)
..where(scoreEntryTable.matchId.equals(matchId))
..addColumns([scoreEntryTable.roundNumber.max()]);
final result = await query.getSingle();
return result.read(scoreEntryTable.roundNumber.max());
}
/// Aggregates the total score for a player in a match by summing all their
/// score entry changes. Returns `0` if there are no scores for the player
/// in the match.
Future<int> getTotalScoreForPlayer({
required String playerId,
required String matchId,
}) async {
final scores = await getAllPlayerScoresInMatch(
playerId: playerId,
matchId: matchId,
);
if (scores.isEmpty) return 0;
// Return the sum of all score changes
return scores.fold<int>(0, (sum, element) => sum + element.change);
}
/* Update */
/// Updates a score entry.
Future<bool> updateScore({
required String playerId,
required String matchId,
required ScoreEntry newEntry,
required ScoreEntry entry,
}) async {
final rowsAffected =
await (update(scoreEntryTable)..where(
(s) =>
s.playerId.equals(playerId) &
s.matchId.equals(matchId) &
s.roundNumber.equals(newEntry.roundNumber),
s.roundNumber.equals(entry.roundNumber),
))
.write(
ScoreEntryTableCompanion(
score: Value(newEntry.score),
change: Value(newEntry.change),
score: Value(entry.score),
change: Value(entry.change),
),
);
return rowsAffected > 0;
}
/* Delete */
/// Deletes a score entry.
Future<bool> deleteScore({
required String playerId,
@@ -182,31 +216,7 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
return rowsAffected > 0;
}
/// Gets the highest (latest) round number for a match.
/// Returns `null` if there are no scores for the match.
Future<int?> getLatestRoundNumber({required String matchId}) async {
final query = selectOnly(scoreEntryTable)
..where(scoreEntryTable.matchId.equals(matchId))
..addColumns([scoreEntryTable.roundNumber.max()]);
final result = await query.getSingle();
return result.read(scoreEntryTable.roundNumber.max());
}
/// Aggregates the total score for a player in a match by summing all their
/// score entry changes. Returns `0` if there are no scores for the player
/// in the match.
Future<int> getTotalScoreForPlayer({
required String playerId,
required String matchId,
}) async {
final scores = await getAllPlayerScoresInMatch(
playerId: playerId,
matchId: matchId,
);
if (scores.isEmpty) return 0;
// Return the sum of all score changes
return scores.fold<int>(0, (sum, element) => sum + element.change);
}
/* Winner handling */
Future<bool> hasWinner({required String matchId}) async {
return await getWinner(matchId: matchId) != null;
@@ -275,12 +285,14 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
}
}
Future<bool> hasLooser({required String matchId}) async {
return await getLooser(matchId: matchId) != null;
/* Loser handling */
Future<bool> hasLoser({required String matchId}) async {
return await getLoser(matchId: matchId) != null;
}
// Setting the looser for a game and clearing previous looser if exists.
Future<bool> setLooser({
Future<bool> setLoser({
required String matchId,
required String playerId,
}) async {
@@ -304,7 +316,7 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
/// Retrieves the looser of a match by looking for a score entry where score
/// is 0. Returns `null` if no player found, else the first with the score.
Future<Player?> getLooser({required String matchId}) async {
Future<Player?> getLoser({required String matchId}) async {
final query =
select(scoreEntryTable).join([
innerJoin(
@@ -332,7 +344,7 @@ class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
///
/// Returns `true` if the looser was removed, `false` if there are multiple
/// scores or if the looser cannot be removed.
Future<bool> removeLooser({required String matchId}) async {
Future<bool> removeLoser({required String matchId}) async {
final scores = await getAllMatchScores(matchId: matchId);
if (scores.length > 1) {

View File

@@ -1,17 +1,105 @@
import 'package:drift/drift.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/db/tables/player_match_table.dart';
import 'package:tallee/data/db/tables/team_table.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/team.dart';
part 'team_dao.g.dart';
@DriftAccessor(tables: [TeamTable])
@DriftAccessor(tables: [TeamTable, PlayerMatchTable])
class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
TeamDao(super.db);
/* Create */
/// Adds a new [team] to the database.
/// Returns `true` if the team was added, `false` otherwise.
Future<bool> addTeam({required Team team, required String matchId}) async {
if (await teamExists(teamId: team.id)) return false;
await into(teamTable).insert(
TeamTableCompanion.insert(
id: team.id,
name: team.name,
createdAt: team.createdAt,
),
mode: InsertMode.insertOrReplace,
);
await db.batch((batch) async {
for (final player in team.members) {
await into(playerMatchTable).insert(
PlayerMatchTableCompanion.insert(
playerId: player.id,
matchId: matchId,
teamId: Value(team.id),
),
mode: InsertMode.insertOrReplace,
);
}
});
return true;
}
/// Adds multiple [teams] to the database in a batch operation.
Future<bool> addTeamsAsList({
required List<Team> teams,
required String matchId,
}) async {
if (teams.isEmpty) return false;
await db.batch(
(b) => b.insertAll(
teamTable,
teams
.map(
(team) => TeamTableCompanion.insert(
id: team.id,
name: team.name,
createdAt: team.createdAt,
),
)
.toList(),
mode: InsertMode.insertOrIgnore,
),
);
for (final team in teams) {
await db.batch((batch) async {
for (final player in team.members) {
await into(db.playerMatchTable).insert(
PlayerMatchTableCompanion.insert(
playerId: player.id,
matchId: matchId,
teamId: Value(team.id),
),
mode: InsertMode.insertOrReplace,
);
}
});
}
return true;
}
/* Read */
/// Retrieves the total count of teams in the database.
Future<int> getTeamCount() async {
final count =
await (selectOnly(teamTable)..addColumns([teamTable.id.count()]))
.map((row) => row.read(teamTable.id.count()))
.getSingle();
return count ?? 0;
}
/// Checks if a team with the given [teamId] exists in the database.
/// Returns `true` if the team exists, `false` otherwise.
Future<bool> teamExists({required String teamId}) async {
final query = select(teamTable)..where((t) => t.id.equals(teamId));
final result = await query.getSingleOrNull();
return result != null;
}
/// Retrieves all teams from the database.
/// Note: This returns teams without their members. Use getTeamById for full team data.
Future<List<Team>> getAllTeams() async {
final query = select(teamTable);
final result = await query.get();
@@ -41,8 +129,7 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
);
}
/// Helper method to get team members from player_match_table.
/// This assumes team members are tracked via the player_match_table.
/// Helper method to get team members from PlayerMatchTable.
Future<List<Player>> _getTeamMembers({required String teamId}) async {
// Get all player_match entries with this teamId
final playerMatchQuery = select(db.playerMatchTable)
@@ -61,44 +148,28 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
return players;
}
/// Adds a new [team] to the database.
/// Returns `true` if the team was added, `false` otherwise.
Future<bool> addTeam({required Team team}) async {
if (!await teamExists(teamId: team.id)) {
await into(teamTable).insert(
TeamTableCompanion.insert(
id: team.id,
name: team.name,
createdAt: team.createdAt,
),
mode: InsertMode.insertOrReplace,
);
return true;
}
return false;
/* Update */
/// Updates the name of the team with the given [teamId].
Future<bool> updateTeamName({
required String teamId,
required String name,
}) async {
final rowsAffected =
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
TeamTableCompanion(name: Value(name)),
);
return rowsAffected > 0;
}
/// Adds multiple [teams] to the database in a batch operation.
Future<bool> addTeamsAsList({required List<Team> teams}) async {
if (teams.isEmpty) return false;
/* Delete */
await db.batch(
(b) => b.insertAll(
teamTable,
teams
.map(
(team) => TeamTableCompanion.insert(
id: team.id,
name: team.name,
createdAt: team.createdAt,
),
)
.toList(),
mode: InsertMode.insertOrIgnore,
),
);
return true;
/// Deletes all teams from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteAllTeams() async {
final query = delete(teamTable);
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Deletes the team with the given [teamId] from the database.
@@ -108,39 +179,4 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
final rowsAffected = await query.go();
return rowsAffected > 0;
}
/// Checks if a team with the given [teamId] exists in the database.
/// Returns `true` if the team exists, `false` otherwise.
Future<bool> teamExists({required String teamId}) async {
final query = select(teamTable)..where((t) => t.id.equals(teamId));
final result = await query.getSingleOrNull();
return result != null;
}
/// Updates the name of the team with the given [teamId].
Future<void> updateTeamName({
required String teamId,
required String newName,
}) async {
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
TeamTableCompanion(name: Value(newName)),
);
}
/// Retrieves the total count of teams in the database.
Future<int> getTeamCount() async {
final count =
await (selectOnly(teamTable)..addColumns([teamTable.id.count()]))
.map((row) => row.read(teamTable.id.count()))
.getSingle();
return count ?? 0;
}
/// Deletes all teams from the database.
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
Future<bool> deleteAllTeams() async {
final query = delete(teamTable);
final rowsAffected = await query.go();
return rowsAffected > 0;
}
}

View File

@@ -5,6 +5,12 @@ part of 'team_dao.dart';
// ignore_for_file: type=lint
mixin _$TeamDaoMixin on DatabaseAccessor<AppDatabase> {
$TeamTableTable get teamTable => attachedDatabase.teamTable;
$PlayerTableTable get playerTable => attachedDatabase.playerTable;
$GameTableTable get gameTable => attachedDatabase.gameTable;
$GroupTableTable get groupTable => attachedDatabase.groupTable;
$MatchTableTable get matchTable => attachedDatabase.matchTable;
$PlayerMatchTableTable get playerMatchTable =>
attachedDatabase.playerMatchTable;
TeamDaoManager get managers => TeamDaoManager(this);
}
@@ -13,4 +19,17 @@ class TeamDaoManager {
TeamDaoManager(this._db);
$$TeamTableTableTableManager get teamTable =>
$$TeamTableTableTableManager(_db.attachedDatabase, _db.teamTable);
$$PlayerTableTableTableManager get playerTable =>
$$PlayerTableTableTableManager(_db.attachedDatabase, _db.playerTable);
$$GameTableTableTableManager get gameTable =>
$$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable);
$$GroupTableTableTableManager get groupTable =>
$$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable);
$$MatchTableTableTableManager get matchTable =>
$$MatchTableTableTableManager(_db.attachedDatabase, _db.matchTable);
$$PlayerMatchTableTableTableManager get playerMatchTable =>
$$PlayerMatchTableTableTableManager(
_db.attachedDatabase,
_db.playerMatchTable,
);
}

View File

@@ -1190,9 +1190,9 @@ class $MatchTableTable extends MatchTable
late final GeneratedColumn<String> notes = GeneratedColumn<String>(
'notes',
aliasedName,
true,
false,
type: DriftSqlType.string,
requiredDuringInsert: false,
requiredDuringInsert: true,
);
static const VerificationMeta _createdAtMeta = const VerificationMeta(
'createdAt',
@@ -1270,6 +1270,8 @@ class $MatchTableTable extends MatchTable
_notesMeta,
notes.isAcceptableOrUnknown(data['notes']!, _notesMeta),
);
} else if (isInserting) {
context.missing(_notesMeta);
}
if (data.containsKey('created_at')) {
context.handle(
@@ -1313,7 +1315,7 @@ class $MatchTableTable extends MatchTable
notes: attachedDatabase.typeMapping.read(
DriftSqlType.string,
data['${effectivePrefix}notes'],
),
)!,
createdAt: attachedDatabase.typeMapping.read(
DriftSqlType.dateTime,
data['${effectivePrefix}created_at'],
@@ -1336,7 +1338,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
final String gameId;
final String? groupId;
final String name;
final String? notes;
final String notes;
final DateTime createdAt;
final DateTime? endedAt;
const MatchTableData({
@@ -1344,7 +1346,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
required this.gameId,
this.groupId,
required this.name,
this.notes,
required this.notes,
required this.createdAt,
this.endedAt,
});
@@ -1357,9 +1359,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
map['group_id'] = Variable<String>(groupId);
}
map['name'] = Variable<String>(name);
if (!nullToAbsent || notes != null) {
map['notes'] = Variable<String>(notes);
}
map['notes'] = Variable<String>(notes);
map['created_at'] = Variable<DateTime>(createdAt);
if (!nullToAbsent || endedAt != null) {
map['ended_at'] = Variable<DateTime>(endedAt);
@@ -1375,9 +1375,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
? const Value.absent()
: Value(groupId),
name: Value(name),
notes: notes == null && nullToAbsent
? const Value.absent()
: Value(notes),
notes: Value(notes),
createdAt: Value(createdAt),
endedAt: endedAt == null && nullToAbsent
? const Value.absent()
@@ -1395,7 +1393,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
gameId: serializer.fromJson<String>(json['gameId']),
groupId: serializer.fromJson<String?>(json['groupId']),
name: serializer.fromJson<String>(json['name']),
notes: serializer.fromJson<String?>(json['notes']),
notes: serializer.fromJson<String>(json['notes']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
endedAt: serializer.fromJson<DateTime?>(json['endedAt']),
);
@@ -1408,7 +1406,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
'gameId': serializer.toJson<String>(gameId),
'groupId': serializer.toJson<String?>(groupId),
'name': serializer.toJson<String>(name),
'notes': serializer.toJson<String?>(notes),
'notes': serializer.toJson<String>(notes),
'createdAt': serializer.toJson<DateTime>(createdAt),
'endedAt': serializer.toJson<DateTime?>(endedAt),
};
@@ -1419,7 +1417,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
String? gameId,
Value<String?> groupId = const Value.absent(),
String? name,
Value<String?> notes = const Value.absent(),
String? notes,
DateTime? createdAt,
Value<DateTime?> endedAt = const Value.absent(),
}) => MatchTableData(
@@ -1427,7 +1425,7 @@ class MatchTableData extends DataClass implements Insertable<MatchTableData> {
gameId: gameId ?? this.gameId,
groupId: groupId.present ? groupId.value : this.groupId,
name: name ?? this.name,
notes: notes.present ? notes.value : this.notes,
notes: notes ?? this.notes,
createdAt: createdAt ?? this.createdAt,
endedAt: endedAt.present ? endedAt.value : this.endedAt,
);
@@ -1478,7 +1476,7 @@ class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
final Value<String> gameId;
final Value<String?> groupId;
final Value<String> name;
final Value<String?> notes;
final Value<String> notes;
final Value<DateTime> createdAt;
final Value<DateTime?> endedAt;
final Value<int> rowid;
@@ -1497,13 +1495,14 @@ class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
required String gameId,
this.groupId = const Value.absent(),
required String name,
this.notes = const Value.absent(),
required String notes,
required DateTime createdAt,
this.endedAt = const Value.absent(),
this.rowid = const Value.absent(),
}) : id = Value(id),
gameId = Value(gameId),
name = Value(name),
notes = Value(notes),
createdAt = Value(createdAt);
static Insertable<MatchTableData> custom({
Expression<String>? id,
@@ -1532,7 +1531,7 @@ class MatchTableCompanion extends UpdateCompanion<MatchTableData> {
Value<String>? gameId,
Value<String?>? groupId,
Value<String>? name,
Value<String?>? notes,
Value<String>? notes,
Value<DateTime>? createdAt,
Value<DateTime?>? endedAt,
Value<int>? rowid,
@@ -2122,7 +2121,7 @@ class $PlayerMatchTableTable extends PlayerMatchTable
type: DriftSqlType.string,
requiredDuringInsert: false,
defaultConstraints: GeneratedColumn.constraintIsAlways(
'REFERENCES team_table (id)',
'REFERENCES team_table (id) ON DELETE SET NULL',
),
);
@override
@@ -2820,6 +2819,13 @@ abstract class _$AppDatabase extends GeneratedDatabase {
),
result: [TableUpdate('player_match_table', kind: UpdateKind.delete)],
),
WritePropagation(
on: TableUpdateQuery.onTableName(
'team_table',
limitUpdateKind: UpdateKind.delete,
),
result: [TableUpdate('player_match_table', kind: UpdateKind.update)],
),
WritePropagation(
on: TableUpdateQuery.onTableName(
'player_table',
@@ -4086,7 +4092,7 @@ typedef $$MatchTableTableCreateCompanionBuilder =
required String gameId,
Value<String?> groupId,
required String name,
Value<String?> notes,
required String notes,
required DateTime createdAt,
Value<DateTime?> endedAt,
Value<int> rowid,
@@ -4097,7 +4103,7 @@ typedef $$MatchTableTableUpdateCompanionBuilder =
Value<String> gameId,
Value<String?> groupId,
Value<String> name,
Value<String?> notes,
Value<String> notes,
Value<DateTime> createdAt,
Value<DateTime?> endedAt,
Value<int> rowid,
@@ -4560,7 +4566,7 @@ class $$MatchTableTableTableManager
Value<String> gameId = const Value.absent(),
Value<String?> groupId = const Value.absent(),
Value<String> name = const Value.absent(),
Value<String?> notes = const Value.absent(),
Value<String> notes = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(),
Value<DateTime?> endedAt = const Value.absent(),
Value<int> rowid = const Value.absent(),
@@ -4580,7 +4586,7 @@ class $$MatchTableTableTableManager
required String gameId,
Value<String?> groupId = const Value.absent(),
required String name,
Value<String?> notes = const Value.absent(),
required String notes,
required DateTime createdAt,
Value<DateTime?> endedAt = const Value.absent(),
Value<int> rowid = const Value.absent(),

View File

@@ -12,7 +12,7 @@ class MatchTable extends Table {
.references(GroupTable, #id, onDelete: KeyAction.setNull)
.nullable()();
TextColumn get name => text()();
TextColumn get notes => text().nullable()();
TextColumn get notes => text()();
DateTimeColumn get createdAt => dateTime()();
DateTimeColumn get endedAt => dateTime().nullable()();

View File

@@ -8,7 +8,9 @@ class PlayerMatchTable extends Table {
text().references(PlayerTable, #id, onDelete: KeyAction.cascade)();
TextColumn get matchId =>
text().references(MatchTable, #id, onDelete: KeyAction.cascade)();
TextColumn get teamId => text().references(TeamTable, #id).nullable()();
TextColumn get teamId => text()
.references(TeamTable, #id, onDelete: KeyAction.setNull)
.nullable()();
@override
Set<Column<Object>> get primaryKey => {playerId, matchId};

View File

@@ -27,7 +27,43 @@ class Game {
return 'Game{id: $id, name: $name, ruleset: $ruleset, description: $description, color: $color, icon: $icon}';
}
/// Creates a Game instance from a JSON object.
Game copyWith({
String? id,
DateTime? createdAt,
String? name,
Ruleset? ruleset,
String? description,
GameColor? color,
String? icon,
}) {
return Game(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
name: name ?? this.name,
ruleset: ruleset ?? this.ruleset,
description: description ?? this.description,
color: color ?? this.color,
icon: icon ?? this.icon,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Game &&
runtimeType == other.runtimeType &&
id == other.id &&
createdAt == other.createdAt &&
name == other.name &&
ruleset == other.ruleset &&
description == other.description &&
color == other.color &&
icon == other.icon;
@override
int get hashCode =>
Object.hash(id, createdAt, name, ruleset, description, color, icon);
Game.fromJson(Map<String, dynamic> json)
: id = json['id'],
createdAt = DateTime.parse(json['createdAt']),
@@ -40,7 +76,6 @@ class Game {
color = GameColor.values.firstWhere((e) => e.name == json['color']),
icon = json['icon'];
/// Converts the Game instance to a JSON object.
Map<String, dynamic> toJson() => {
'id': id,
'createdAt': createdAt.toIso8601String(),

View File

@@ -1,4 +1,5 @@
import 'package:clock/clock.dart';
import 'package:collection/collection.dart';
import 'package:tallee/data/models/player.dart';
import 'package:uuid/uuid.dart';
@@ -24,6 +25,42 @@ class Group {
return 'Group{id: $id, name: $name, description: $description, members: $members}';
}
Group copyWith({
String? id,
String? name,
String? description,
DateTime? createdAt,
List<Player>? members,
}) {
return Group(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
createdAt: createdAt ?? this.createdAt,
members: members ?? this.members,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Group &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name &&
description == other.description &&
createdAt == other.createdAt &&
const DeepCollectionEquality().equals(members, other.members);
@override
int get hashCode => Object.hash(
id,
name,
description,
createdAt,
const DeepCollectionEquality().hash(members),
);
/// Creates a Group instance from a JSON object where the related [Player]
/// objects are represented by their IDs.
Group.fromJson(Map<String, dynamic> json)

View File

@@ -1,9 +1,11 @@
import 'package:clock/clock.dart';
import 'package:collection/collection.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.dart';
import 'package:uuid/uuid.dart';
class Match {
@@ -14,6 +16,7 @@ class Match {
final Game game;
final Group? group;
final List<Player> players;
final List<Team>? teams;
final String notes;
Map<String, ScoreEntry?> scores;
@@ -23,6 +26,7 @@ class Match {
required this.players,
this.endedAt,
this.group,
this.teams,
this.notes = '',
String? id,
DateTime? createdAt,
@@ -36,9 +40,62 @@ class Match {
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, mvp: $mvp}';
}
/// Creates a Match instance from a JSON object where related objects are
/// represented by their IDs. Therefore, the game, group, and players are not
/// fully constructed here.
Match copyWith({
String? id,
DateTime? createdAt,
DateTime? endedAt,
String? name,
Game? game,
Group? group,
List<Player>? players,
List<Team>? teams,
String? notes,
Map<String, ScoreEntry?>? scores,
}) {
return Match(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
endedAt: endedAt ?? this.endedAt,
name: name ?? this.name,
game: game ?? this.game,
group: group ?? this.group,
players: players ?? this.players,
teams: teams ?? this.teams,
notes: notes ?? this.notes,
scores: scores ?? this.scores,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Match &&
runtimeType == other.runtimeType &&
id == other.id &&
createdAt == other.createdAt &&
endedAt == other.endedAt &&
name == other.name &&
game == other.game &&
group == other.group &&
const DeepCollectionEquality().equals(players, other.players) &&
const DeepCollectionEquality().equals(teams, other.teams) &&
notes == other.notes &&
const DeepCollectionEquality().equals(scores, other.scores);
@override
int get hashCode => Object.hash(
id,
createdAt,
endedAt,
name,
game,
group,
const DeepCollectionEquality().hash(players),
const DeepCollectionEquality().hash(teams),
notes,
const DeepCollectionEquality().hash(scores),
);
Match.fromJson(Map<String, dynamic> json)
: id = json['id'],
createdAt = DateTime.parse(json['createdAt']),
@@ -55,6 +112,7 @@ class Match {
),
group = null,
players = [],
teams = [],
scores = json['scores'] != null
? (json['scores'] as Map<String, dynamic>).map(
(key, value) => MapEntry(
@@ -67,9 +125,6 @@ class Match {
: {},
notes = json['notes'] ?? '';
/// Converts the Match instance to a JSON object. Related objects are
/// represented by their IDs, so the game, group, and players are not fully
/// serialized here.
Map<String, dynamic> toJson() => {
'id': id,
'createdAt': createdAt.toIso8601String(),
@@ -78,6 +133,7 @@ class Match {
'gameId': game.id,
'groupId': group?.id,
'playerIds': players.map((player) => player.id).toList(),
'teams': teams?.map((team) => team.toJson()).toList(),
'scores': scores.map((key, value) => MapEntry(key, value?.toJson())),
'notes': notes,
};

View File

@@ -23,7 +23,36 @@ class Player {
return 'Player{id: $id, createdAt: $createdAt, name: $name, nameCount: $nameCount, description: $description}';
}
/// Creates a Player instance from a JSON object.
Player copyWith({
String? id,
DateTime? createdAt,
String? name,
int? nameCount,
String? description,
}) {
return Player(
id: id ?? this.id,
createdAt: createdAt ?? this.createdAt,
name: name ?? this.name,
nameCount: nameCount ?? this.nameCount,
description: description ?? this.description,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Player &&
runtimeType == other.runtimeType &&
id == other.id &&
createdAt == other.createdAt &&
name == other.name &&
nameCount == other.nameCount &&
description == other.description;
@override
int get hashCode => Object.hash(id, createdAt, name, nameCount, description);
Player.fromJson(Map<String, dynamic> json)
: id = json['id'],
createdAt = DateTime.parse(json['createdAt']),
@@ -31,7 +60,6 @@ class Player {
nameCount = 0,
description = json['description'];
/// Converts the Player instance to a JSON object.
Map<String, dynamic> toJson() => {
'id': id,
'createdAt': createdAt.toIso8601String(),

View File

@@ -10,6 +10,26 @@ class ScoreEntry {
return 'ScoreEntry{roundNumber: $roundNumber, score: $score, change: $change}';
}
ScoreEntry copyWith({int? roundNumber, int? score, int? change}) {
return ScoreEntry(
roundNumber: roundNumber ?? this.roundNumber,
score: score ?? this.score,
change: change ?? this.change,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is ScoreEntry &&
runtimeType == other.runtimeType &&
roundNumber == other.roundNumber &&
score == other.score &&
change == other.change;
@override
int get hashCode => Object.hash(roundNumber, score, change);
ScoreEntry.fromJson(Map<String, dynamic> json)
: roundNumber = json['roundNumber'],
score = json['score'],

View File

@@ -1,4 +1,5 @@
import 'package:clock/clock.dart';
import 'package:collection/collection.dart';
import 'package:tallee/data/models/player.dart';
import 'package:uuid/uuid.dart';
@@ -21,16 +22,44 @@ class Team {
return 'Team{id: $id, name: $name, members: $members}';
}
/// Creates a Team instance from a JSON object (memberIds format).
/// Player objects are reconstructed from memberIds by the DataTransferService.
Team copyWith({
String? id,
String? name,
DateTime? createdAt,
List<Player>? members,
}) {
return Team(
id: id ?? this.id,
name: name ?? this.name,
createdAt: createdAt ?? this.createdAt,
members: members ?? this.members,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Team &&
runtimeType == other.runtimeType &&
id == other.id &&
name == other.name &&
createdAt == other.createdAt &&
const DeepCollectionEquality().equals(members, other.members);
@override
int get hashCode => Object.hash(
id,
name,
createdAt,
const DeepCollectionEquality().hash(members),
);
Team.fromJson(Map<String, dynamic> json)
: id = json['id'],
name = json['name'],
createdAt = DateTime.parse(json['createdAt']),
members = []; // Populated during import via DataTransferService
/// Converts the Team instance to a JSON object. Related objects are
/// represented by their IDs.
Map<String, dynamic> toJson() => {
'id': id,
'name': name,

View File

@@ -172,12 +172,12 @@ class _CreateGroupViewState extends State<CreateGroupView> {
if (widget.groupToEdit!.name != groupName) {
successfullNameChange = await db.groupDao.updateGroupName(
groupId: widget.groupToEdit!.id,
newName: groupName,
name: groupName,
);
}
if (widget.groupToEdit!.members != selectedPlayers) {
successfullMemberChange = await db.groupDao.replaceGroupPlayers(
successfullMemberChange = await db.playerGroupDao.replaceGroupPlayers(
groupId: widget.groupToEdit!.id,
newPlayers: selectedPlayers,
);

View File

@@ -284,14 +284,14 @@ class _CreateMatchViewState extends State<CreateMatchView> {
if (widget.matchToEdit!.name != updatedMatch.name) {
await db.matchDao.updateMatchName(
matchId: widget.matchToEdit!.id,
newName: updatedMatch.name,
name: updatedMatch.name,
);
}
if (widget.matchToEdit!.group?.id != updatedMatch.group?.id) {
await db.matchDao.updateMatchGroup(
matchId: widget.matchToEdit!.id,
newGroupId: updatedMatch.group?.id,
groupId: updatedMatch.group?.id,
);
}

View File

@@ -242,9 +242,9 @@ class _MatchResultViewState extends State<MatchResultView> {
/// Handles saving or removing the loser in the database.
Future<bool> _handleLoser() async {
if (_selectedPlayer == null) {
return await db.scoreEntryDao.removeLooser(matchId: widget.match.id);
return await db.scoreEntryDao.removeLoser(matchId: widget.match.id);
} else {
return await db.scoreEntryDao.setLooser(
return await db.scoreEntryDao.setLoser(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);

View File

@@ -160,6 +160,7 @@ const allDependencies = <Package>[
/// Direct `dependencies`.
const dependencies = <Package>[
_clock,
_collection,
_cupertino_icons,
_drift,
_drift_flutter,
@@ -444,13 +445,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
);
/// build 4.0.5
/// build 4.0.6
const _build = Package(
name: 'build',
description: 'A package for authoring build_runner compatible code generators.',
repository: 'https://github.com/dart-lang/build/tree/master/build',
authors: [],
version: '4.0.5',
version: '4.0.6',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
@@ -567,13 +568,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
);
/// build_runner 2.14.0
/// build_runner 2.15.0
const _build_runner = Package(
name: 'build_runner',
description: 'A build system for Dart code generation and modular compilation.',
repository: 'https://github.com/dart-lang/build/tree/master/build_runner',
authors: [],
version: '2.14.0',
version: '2.15.0',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
@@ -651,14 +652,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
);
/// built_value 8.12.5
/// built_value 8.12.6
const _built_value = Package(
name: 'built_value',
description: '''Value types with builders, Dart classes as enums, and serialization. This library is the runtime dependency.
''',
repository: 'https://github.com/google/built_value.dart/tree/master/built_value',
authors: [],
version: '8.12.5',
version: '8.12.6',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
@@ -36204,13 +36205,13 @@ Copyright (C) 2009-2017, International Business Machines Corporation,
Google, and others. All Rights Reserved.''',
);
/// source_gen 4.2.2
/// source_gen 4.2.3
const _source_gen = Package(
name: 'source_gen',
description: 'Source code generation builders and utilities for the Dart build system',
repository: 'https://github.com/dart-lang/source_gen/tree/master/source_gen',
authors: [],
version: '4.2.2',
version: '4.2.3',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
@@ -37481,13 +37482,13 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution.''',
);
/// vm_service 15.1.0
/// vm_service 15.2.0
const _vm_service = Package(
name: 'vm_service',
description: 'A library to communicate with a service implementing the Dart VM service protocol.',
repository: 'https://github.com/dart-lang/sdk/tree/main/pkg/vm_service',
authors: [],
version: '15.1.0',
version: '15.2.0',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
@@ -37881,16 +37882,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''',
);
/// tallee 0.0.23+257
/// tallee 0.0.24+258
const _tallee = Package(
name: 'tallee',
description: 'Tracking App for Card Games',
authors: [],
version: '0.0.23+257',
version: '0.0.24+258',
spdxIdentifiers: ['LGPL-3.0'],
isMarkdown: false,
isSdk: false,
dependencies: [PackageRef('clock'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
dependencies: [PackageRef('clock'), PackageRef('collection'), PackageRef('cupertino_icons'), PackageRef('drift'), PackageRef('drift_flutter'), PackageRef('file_picker'), PackageRef('file_saver'), PackageRef('flutter'), PackageRef('flutter_localizations'), PackageRef('fluttericon'), PackageRef('font_awesome_flutter'), PackageRef('intl'), PackageRef('json_schema'), PackageRef('package_info_plus'), PackageRef('path_provider'), PackageRef('provider'), PackageRef('skeletonizer'), PackageRef('url_launcher'), PackageRef('uuid')],
devDependencies: [PackageRef('flutter_test'), PackageRef('build_runner'), PackageRef('dart_pubspec_licenses'), PackageRef('drift_dev'), PackageRef('flutter_lints')],
license: '''GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007

View File

@@ -35,13 +35,11 @@ class DataTransferService {
final groups = await db.groupDao.getAllGroups();
final players = await db.playerDao.getAllPlayers();
final games = await db.gameDao.getAllGames();
final teams = await db.teamDao.getAllTeams();
final Map<String, dynamic> jsonMap = {
'players': players.map((player) => player.toJson()).toList(),
'games': games.map((game) => game.toJson()).toList(),
'groups': groups.map((group) => group.toJson()).toList(),
'teams': teams.map((team) => team.toJson()).toList(),
'matches': matches.map((match) => match.toJson()).toList(),
};
@@ -130,8 +128,6 @@ class DataTransferService {
final importedGroups = parseGroupsFromJson(decodedJson, playerById);
final groupById = {for (final g in importedGroups) g.id: g};
final importedTeams = parseTeamsFromJson(decodedJson, playerById);
final importedMatches = parseMatchesFromJson(
decodedJson,
gameById,
@@ -142,8 +138,7 @@ class DataTransferService {
await db.playerDao.addPlayersAsList(players: importedPlayers);
await db.gameDao.addGamesAsList(games: importedGames);
await db.groupDao.addGroupsAsList(groups: importedGroups);
await db.teamDao.addTeamsAsList(teams: importedTeams);
await db.matchDao.addMatchAsList(matches: importedMatches);
await db.matchDao.addMatchesAsList(matches: importedMatches);
}
/* Parsing Methods */
@@ -190,13 +185,12 @@ class DataTransferService {
}).toList();
}
/// Parses teams from JSON data.
/// Parses teams from a list of JSON objects.
@visibleForTesting
static List<Team> parseTeamsFromJson(
Map<String, dynamic> decodedJson,
List<dynamic> teamsJson,
Map<String, Player> playerById,
) {
final teamsJson = (decodedJson['teams'] as List<dynamic>?) ?? [];
return teamsJson.map((t) {
final map = t as Map<String, dynamic>;
final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
@@ -259,12 +253,16 @@ class DataTransferService {
.whereType<Player>()
.toList();
final teamsJson = (map['teams'] as List<dynamic>?) ?? [];
final teams = parseTeamsFromJson(teamsJson, playersMap);
return Match(
id: id,
name: name,
game: game,
group: group,
players: players,
teams: teams.isEmpty ? null : teams,
createdAt: createdAt,
endedAt: endedAt,
notes: notes,

View File

@@ -1,13 +1,14 @@
name: tallee
description: "Tracking App for Card Games"
publish_to: 'none'
version: 0.0.23+257
version: 0.0.24+258
environment:
sdk: ^3.8.1
dependencies:
clock: ^1.1.2
collection: ^1.19.1
cupertino_icons: ^1.0.6
drift: ^2.27.0
drift_flutter: ^0.2.4

View File

@@ -35,25 +35,23 @@ void main() {
testPlayer4 = Player(name: 'Diana');
testGroup1 = Group(
name: 'Test Group',
description: '',
members: [testPlayer1, testPlayer2, testPlayer3],
description: 'description of the test group 1',
);
testGroup2 = Group(
id: 'gr2',
name: 'Second Group',
description: '',
members: [testPlayer2, testPlayer3, testPlayer4],
description: 'description of the test group 2',
);
testGroup3 = Group(
id: 'gr2',
name: 'Second Group',
description: '',
members: [testPlayer2, testPlayer4],
);
testGroup4 = Group(
id: 'gr2',
name: 'Second Group',
description: '',
members: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
});
@@ -62,312 +60,355 @@ void main() {
await database.close();
});
group('Group Tests', () {
// Verifies that a single group can be added and retrieved with all fields and members intact.
test('Adding and fetching a single group works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
group('CREATE', () {
test('Adding and fetching a single group works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
final fetchedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(fetchedGroup.id, testGroup1.id);
expect(fetchedGroup.name, testGroup1.name);
expect(fetchedGroup.createdAt, testGroup1.createdAt);
expect(fetchedGroup.members.length, testGroup1.members.length);
for (int i = 0; i < testGroup1.members.length; i++) {
expect(fetchedGroup.members[i].id, testGroup1.members[i].id);
expect(fetchedGroup.members[i].name, testGroup1.members[i].name);
expect(
fetchedGroup.members[i].createdAt,
testGroup1.members[i].createdAt,
final fetchedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
}
});
// Verifies that multiple groups can be added and retrieved with correct members.
test('Adding and fetching multiple groups works correctly', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup2, testGroup3, testGroup4],
);
expect(fetchedGroup.id, testGroup1.id);
expect(fetchedGroup.name, testGroup1.name);
expect(fetchedGroup.createdAt, testGroup1.createdAt);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 2);
final testGroups = {testGroup1.id: testGroup1, testGroup2.id: testGroup2};
for (final group in allGroups) {
final testGroup = testGroups[group.id]!;
expect(group.id, testGroup.id);
expect(group.name, testGroup.name);
expect(group.createdAt, testGroup.createdAt);
expect(group.members.length, testGroup.members.length);
for (int i = 0; i < testGroup.members.length; i++) {
expect(group.members[i].id, testGroup.members[i].id);
expect(group.members[i].name, testGroup.members[i].name);
expect(group.members[i].createdAt, testGroup.members[i].createdAt);
expect(fetchedGroup.members.length, testGroup1.members.length);
for (int i = 0; i < testGroup1.members.length; i++) {
expect(fetchedGroup.members[i].id, testGroup1.members[i].id);
expect(fetchedGroup.members[i].name, testGroup1.members[i].name);
expect(
fetchedGroup.members[i].createdAt,
testGroup1.members[i].createdAt,
);
}
}
});
});
// Verifies that adding the same group twice does not create duplicates.
test('Adding the same group twice does not create duplicates', () async {
await database.groupDao.addGroup(group: testGroup1);
await database.groupDao.addGroup(group: testGroup1);
test('Adding the same group twice does not create duplicates', () async {
await database.groupDao.addGroup(group: testGroup1);
await database.groupDao.addGroup(group: testGroup1);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
});
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
// Verifies that groupExists returns correct boolean based on group presence.
test('Group existence check works correctly', () async {
var groupExists = await database.groupDao.groupExists(
groupId: testGroup1.id,
);
expect(groupExists, false);
await database.groupDao.addGroup(group: testGroup1);
groupExists = await database.groupDao.groupExists(groupId: testGroup1.id);
expect(groupExists, true);
});
// Verifies that deleteGroup removes the group and returns true.
test('Deleting a group works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
final groupDeleted = await database.groupDao.deleteGroup(
groupId: testGroup1.id,
);
expect(groupDeleted, true);
final groupExists = await database.groupDao.groupExists(
groupId: testGroup1.id,
);
expect(groupExists, false);
});
// Verifies that updateGroupName correctly updates only the name field.
test('Updating a group name works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
const newGroupName = 'new group name';
await database.groupDao.updateGroupName(
groupId: testGroup1.id,
newName: newGroupName,
);
final result = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(result.name, newGroupName);
});
// Verifies that getGroupCount returns correct count through add/delete operations.
test('Getting the group count works correctly', () async {
final initialCount = await database.groupDao.getGroupCount();
expect(initialCount, 0);
await database.groupDao.addGroup(group: testGroup1);
final groupAdded = await database.groupDao.getGroupCount();
expect(groupAdded, 1);
final groupRemoved = await database.groupDao.deleteGroup(
groupId: testGroup1.id,
);
expect(groupRemoved, true);
final finalCount = await database.groupDao.getGroupCount();
expect(finalCount, 0);
});
// Verifies that getAllGroups returns an empty list when no groups exist.
test('getAllGroups returns empty list when no groups exist', () async {
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups, isEmpty);
});
// Verifies that getGroupById throws StateError for non-existent group ID.
test('getGroupById throws exception for non-existent group', () async {
expect(
() => database.groupDao.getGroupById(groupId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
// Verifies that addGroup returns false when trying to add a duplicate group.
test('addGroup returns false when group already exists', () async {
final firstAdd = await database.groupDao.addGroup(group: testGroup1);
expect(firstAdd, true);
final secondAdd = await database.groupDao.addGroup(group: testGroup1);
expect(secondAdd, false);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
});
// Verifies that addGroupsAsList handles an empty list without errors.
test('addGroupsAsList handles empty list correctly', () async {
await database.groupDao.addGroupsAsList(groups: []);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 0);
});
// Verifies that deleteGroup returns false for a non-existent group ID.
test('deleteGroup returns false for non-existent group', () async {
final deleted = await database.groupDao.deleteGroup(
groupId: 'non-existent-id',
);
expect(deleted, false);
});
// Verifies that updateGroupName returns false for a non-existent group ID.
test('updateGroupName returns false for non-existent group', () async {
final updated = await database.groupDao.updateGroupName(
groupId: 'non-existent-id',
newName: 'New Name',
);
expect(updated, false);
});
// Verifies that updateGroupDescription correctly updates the description field.
test('Updating a group description works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
const newDescription = 'This is a new description';
final updated = await database.groupDao.updateGroupDescription(
groupId: testGroup1.id,
newDescription: newDescription,
);
expect(updated, true);
final result = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(result.description, newDescription);
});
// Verifies that updateGroupDescription can set the description to null.
test('updateGroupDescription can set description to null', () async {
final groupWithDescription = Group(
name: 'Group with description',
description: 'Initial description',
members: [testPlayer1],
);
await database.groupDao.addGroup(group: groupWithDescription);
final updated = await database.groupDao.updateGroupDescription(
groupId: groupWithDescription.id,
newDescription: 'Updated description',
);
expect(updated, true);
final result = await database.groupDao.getGroupById(
groupId: groupWithDescription.id,
);
expect(result.description, 'Updated description');
});
// Verifies that updateGroupDescription returns false for a non-existent group.
test(
'updateGroupDescription returns false for non-existent group',
() async {
final updated = await database.groupDao.updateGroupDescription(
groupId: 'non-existent-id',
newDescription: 'New Description',
final fetchedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(updated, false);
},
);
expect(fetchedGroup.id, testGroup1.id);
expect(fetchedGroup.members.length, testGroup1.members.length);
});
// Verifies that deleteAllGroups removes all groups from the database.
test('deleteAllGroups removes all groups', () async {
await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]);
test('addGroup() returns false when group already exists', () async {
final firstAdd = await database.groupDao.addGroup(group: testGroup1);
expect(firstAdd, isTrue);
final countBefore = await database.groupDao.getGroupCount();
expect(countBefore, 2);
final secondAdd = await database.groupDao.addGroup(group: testGroup1);
expect(secondAdd, isFalse);
final deleted = await database.groupDao.deleteAllGroups();
expect(deleted, true);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
});
final countAfter = await database.groupDao.getGroupCount();
expect(countAfter, 0);
test('Adding and fetching multiple groups works correctly', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup2, testGroup3, testGroup4],
);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 2);
final testGroups = {
testGroup1.id: testGroup1,
testGroup2.id: testGroup2,
};
for (final group in allGroups) {
final testGroup = testGroups[group.id]!;
expect(group.id, testGroup.id);
expect(group.name, testGroup.name);
expect(group.createdAt, testGroup.createdAt);
expect(group.members.length, testGroup.members.length);
for (int i = 0; i < testGroup.members.length; i++) {
expect(group.members[i].id, testGroup.members[i].id);
expect(group.members[i].name, testGroup.members[i].name);
expect(group.members[i].createdAt, testGroup.members[i].createdAt);
}
}
});
test('addGroupsAsList() handles empty list correctly', () async {
await database.groupDao.addGroupsAsList(groups: []);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 0);
});
});
// Verifies that deleteAllGroups returns false when no groups exist.
test('deleteAllGroups returns false when no groups exist', () async {
final deleted = await database.groupDao.deleteAllGroups();
expect(deleted, false);
group('READ', () {
test('groupExists() works correctly', () async {
var groupExists = await database.groupDao.groupExists(
groupId: testGroup1.id,
);
expect(groupExists, isFalse);
await database.groupDao.addGroup(group: testGroup1);
groupExists = await database.groupDao.groupExists(
groupId: testGroup1.id,
);
expect(groupExists, isTrue);
});
test('getGroupCount() works correctly', () async {
var count = await database.groupDao.getGroupCount();
expect(count, 0);
var added = await database.groupDao.addGroup(group: testGroup1);
expect(added, isTrue);
count = await database.groupDao.getGroupCount();
expect(count, 1);
added = await database.groupDao.addGroup(group: testGroup2);
expect(added, isTrue);
count = await database.groupDao.getGroupCount();
expect(count, 2);
final removed = await database.groupDao.deleteGroup(
groupId: testGroup1.id,
);
expect(removed, isTrue);
count = await database.groupDao.getGroupCount();
expect(count, 1);
});
test('getGroupById() throws exception for non-existent group', () async {
expect(
() => database.groupDao.getGroupById(groupId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
test('getAllGroups() returns empty list when no groups exist', () async {
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups, isEmpty);
});
test('addGroupsAsList() with duplicate groups only adds once', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup1, testGroup1],
);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
});
});
// Verifies that groups with special characters (quotes, emojis) are stored correctly.
test('Group with special characters in name is stored correctly', () async {
final specialGroup = Group(
name: 'Group\'s & "Special" <Name>',
description: 'Description with émojis 🎮🎲',
members: [testPlayer1],
);
await database.groupDao.addGroup(group: specialGroup);
group('UPDATE', () {
test('updateGroupName() works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
final fetchedGroup = await database.groupDao.getGroupById(
groupId: specialGroup.id,
const newName = 'New name';
await database.groupDao.updateGroupName(
groupId: testGroup1.id,
name: newName,
);
final result = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(result.name, newName);
});
test('updateGroupName() returns false for non-existent group', () async {
final updated = await database.groupDao.updateGroupName(
groupId: 'non-existent-id',
name: 'New name',
);
expect(updated, isFalse);
});
test('updateGroupDescription() works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
const newDescription = 'New description';
final updated = await database.groupDao.updateGroupDescription(
groupId: testGroup1.id,
description: newDescription,
);
expect(updated, isTrue);
final group = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(group.description, newDescription);
});
test(
'updateGroupDescription() returns false for non-existent group',
() async {
final updated = await database.groupDao.updateGroupDescription(
groupId: 'non-existent-id',
description: 'New description',
);
expect(updated, isFalse);
},
);
test('Multiple updates to the same group work correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
const newName = 'New name';
const newDescription = 'New description';
await database.groupDao.updateGroupName(
groupId: testGroup1.id,
name: newName,
);
await database.groupDao.updateGroupDescription(
groupId: testGroup1.id,
description: newDescription,
);
final updatedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(updatedGroup.name, newName);
expect(updatedGroup.description, newDescription);
});
test('replaceGroupPlayers() works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
final initialGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(initialGroup.members.length, 3);
expect(
initialGroup.members
.map((p) => p.id)
.toList()
.contains(testPlayer1.id),
isTrue,
);
expect(
initialGroup.members
.map((p) => p.id)
.toList()
.contains(testPlayer2.id),
isTrue,
);
expect(
initialGroup.members
.map((p) => p.id)
.toList()
.contains(testPlayer3.id),
isTrue,
);
final newPlayers = [testPlayer2, testPlayer4];
final replaced = await database.playerGroupDao.replaceGroupPlayers(
groupId: testGroup1.id,
newPlayers: newPlayers,
);
expect(replaced, isTrue);
final updatedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(updatedGroup.members.length, 2);
final memberIds = updatedGroup.members.map((p) => p.id).toList();
expect(memberIds.contains(testPlayer2.id), isTrue);
expect(memberIds.contains(testPlayer4.id), isTrue);
expect(memberIds.contains(testPlayer1.id), isFalse);
expect(memberIds.contains(testPlayer3.id), isFalse);
});
test('replaceGroupPlayers() ignores empty list ', () async {
await database.groupDao.addGroup(group: testGroup1);
final replaced = await database.playerGroupDao.replaceGroupPlayers(
groupId: testGroup1.id,
newPlayers: [],
);
expect(replaced, isFalse);
final updatedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(updatedGroup.members.length, testGroup1.members.length);
});
test(
'replaceGroupPlayers() returns false for non-existent group',
() async {
final replaced = await database.playerGroupDao.replaceGroupPlayers(
groupId: 'non-existent-id',
newPlayers: [testPlayer1],
);
expect(replaced, isFalse);
},
);
expect(fetchedGroup.name, 'Group\'s & "Special" <Name>');
expect(fetchedGroup.description, 'Description with émojis 🎮🎲');
});
// Verifies that a group with an empty members list can be stored and retrieved.
test('Group with empty members list is stored correctly', () async {
final emptyGroup = Group(
name: 'Empty Group',
description: '',
members: [],
);
await database.groupDao.addGroup(group: emptyGroup);
group('DELETE', () {
test('deleteGroup() works correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
final fetchedGroup = await database.groupDao.getGroupById(
groupId: emptyGroup.id,
);
expect(fetchedGroup.name, 'Empty Group');
expect(fetchedGroup.members, isEmpty);
final groupDeleted = await database.groupDao.deleteGroup(
groupId: testGroup1.id,
);
expect(groupDeleted, isTrue);
final groupExists = await database.groupDao.groupExists(
groupId: testGroup1.id,
);
expect(groupExists, isFalse);
});
test('deleteGroup() returns false for non-existent group', () async {
final deleted = await database.groupDao.deleteGroup(
groupId: 'non-existent-id',
);
expect(deleted, isFalse);
});
test('deleteAllGroups() works correctly', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup2],
);
var count = await database.groupDao.getGroupCount();
expect(count, 2);
final deleted = await database.groupDao.deleteAllGroups();
expect(deleted, isTrue);
count = await database.groupDao.getGroupCount();
expect(count, 0);
});
test('deleteAllGroups() returns false when no groups exist', () async {
final deleted = await database.groupDao.deleteAllGroups();
expect(deleted, isFalse);
});
});
// Verifies that multiple sequential updates to the same group work correctly.
test('Multiple updates to the same group work correctly', () async {
await database.groupDao.addGroup(group: testGroup1);
group('Edge Cases', () {
test('Group with special characters is stored correctly', () async {
final specialGroup = Group(
name: 'Group\'s & "Special" <Name>',
description: 'Description with émojis 🎮🎲',
members: [testPlayer1],
);
await database.groupDao.addGroup(group: specialGroup);
await database.groupDao.updateGroupName(
groupId: testGroup1.id,
newName: 'Updated Name',
);
await database.groupDao.updateGroupDescription(
groupId: testGroup1.id,
newDescription: 'Updated Description',
);
final updatedGroup = await database.groupDao.getGroupById(
groupId: testGroup1.id,
);
expect(updatedGroup.name, 'Updated Name');
expect(updatedGroup.description, 'Updated Description');
expect(updatedGroup.members.length, testGroup1.members.length);
});
// Verifies that addGroupsAsList with duplicate groups only adds unique ones.
test('addGroupsAsList with duplicate groups only adds once', () async {
await database.groupDao.addGroupsAsList(
groups: [testGroup1, testGroup1, testGroup1],
);
final allGroups = await database.groupDao.getAllGroups();
expect(allGroups.length, 1);
final fetchedGroup = await database.groupDao.getGroupById(
groupId: specialGroup.id,
);
expect(fetchedGroup.name, 'Group\'s & "Special" <Name>');
expect(fetchedGroup.description, 'Description with émojis 🎮🎲');
});
});
});
}

View File

@@ -101,212 +101,221 @@ void main() {
});
group('Match Tests', () {
// Verifies that a single match can be added and retrieved with all fields, group, and players intact.
test('Adding and fetching single match works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
group('CREATE', () {
test('Adding and fetching single match works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
final result = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
final result = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(result.id, testMatch1.id);
expect(result.name, testMatch1.name);
expect(result.createdAt, testMatch1.createdAt);
expect(result.id, testMatch1.id);
expect(result.name, testMatch1.name);
expect(result.createdAt, testMatch1.createdAt);
if (result.group != null) {
expect(result.group!.members.length, testGroup1.members.length);
if (result.group != null) {
expect(result.group!.members.length, testGroup1.members.length);
for (int i = 0; i < testGroup1.members.length; i++) {
expect(result.group!.members[i].id, testGroup1.members[i].id);
expect(result.group!.members[i].name, testGroup1.members[i].name);
}
} else {
fail('Group is null');
}
expect(result.players.length, testMatch1.players.length);
for (int i = 0; i < testMatch1.players.length; i++) {
expect(result.players[i].id, testMatch1.players[i].id);
expect(result.players[i].name, testMatch1.players[i].name);
expect(result.players[i].createdAt, testMatch1.players[i].createdAt);
}
});
// Verifies that multiple matches can be added and retrieved with correct groups and players.
test('Adding and fetching multiple matches works correctly', () async {
await database.matchDao.addMatchAsList(
matches: [
testMatch1,
testMatch2,
testMatchOnlyGroup,
testMatchOnlyPlayers,
],
);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches.length, 4);
final testMatches = {
testMatch1.id: testMatch1,
testMatch2.id: testMatch2,
testMatchOnlyGroup.id: testMatchOnlyGroup,
testMatchOnlyPlayers.id: testMatchOnlyPlayers,
};
for (final match in allMatches) {
final testMatch = testMatches[match.id]!;
// Match-Checks
expect(match.id, testMatch.id);
expect(match.name, testMatch.name);
expect(match.createdAt, testMatch.createdAt);
// Group-Checks
if (testMatch.group != null) {
expect(match.group!.id, testMatch.group!.id);
expect(match.group!.name, testMatch.group!.name);
expect(match.group!.createdAt, testMatch.group!.createdAt);
// Group Members-Checks
expect(match.group!.members.length, testMatch.group!.members.length);
for (int i = 0; i < testMatch.group!.members.length; i++) {
expect(match.group!.members[i].id, testMatch.group!.members[i].id);
expect(
match.group!.members[i].name,
testMatch.group!.members[i].name,
);
expect(
match.group!.members[i].createdAt,
testMatch.group!.members[i].createdAt,
);
for (int i = 0; i < testGroup1.members.length; i++) {
expect(result.group!.members[i].id, testGroup1.members[i].id);
expect(result.group!.members[i].name, testGroup1.members[i].name);
}
} else {
expect(match.group, null);
fail('Group is null');
}
expect(result.players.length, testMatch1.players.length);
// Players-Checks
expect(match.players.length, testMatch.players.length);
for (int i = 0; i < testMatch.players.length; i++) {
expect(match.players[i].id, testMatch.players[i].id);
expect(match.players[i].name, testMatch.players[i].name);
expect(match.players[i].createdAt, testMatch.players[i].createdAt);
for (int i = 0; i < testMatch1.players.length; i++) {
expect(result.players[i].id, testMatch1.players[i].id);
expect(result.players[i].name, testMatch1.players[i].name);
expect(result.players[i].createdAt, testMatch1.players[i].createdAt);
}
}
});
test('Adding and fetching multiple matches works correctly', () async {
await database.matchDao.addMatchesAsList(
matches: [
testMatch1,
testMatch2,
testMatchOnlyGroup,
testMatchOnlyPlayers,
],
);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches.length, 4);
final testMatches = {
testMatch1.id: testMatch1,
testMatch2.id: testMatch2,
testMatchOnlyGroup.id: testMatchOnlyGroup,
testMatchOnlyPlayers.id: testMatchOnlyPlayers,
};
for (final match in allMatches) {
final testMatch = testMatches[match.id]!;
// Match-Checks
expect(match.id, testMatch.id);
expect(match.name, testMatch.name);
expect(match.createdAt, testMatch.createdAt);
// Group-Checks
if (testMatch.group != null) {
expect(match.group!.id, testMatch.group!.id);
expect(match.group!.name, testMatch.group!.name);
expect(match.group!.createdAt, testMatch.group!.createdAt);
// Group Members-Checks
expect(
match.group!.members.length,
testMatch.group!.members.length,
);
for (int i = 0; i < testMatch.group!.members.length; i++) {
expect(
match.group!.members[i].id,
testMatch.group!.members[i].id,
);
expect(
match.group!.members[i].name,
testMatch.group!.members[i].name,
);
expect(
match.group!.members[i].createdAt,
testMatch.group!.members[i].createdAt,
);
}
} else {
expect(match.group, null);
}
// Players-Checks
expect(match.players.length, testMatch.players.length);
for (int i = 0; i < testMatch.players.length; i++) {
expect(match.players[i].id, testMatch.players[i].id);
expect(match.players[i].name, testMatch.players[i].name);
expect(match.players[i].createdAt, testMatch.players[i].createdAt);
}
}
});
test('addMatch() ignores duplicate games', () async {
var added = await database.matchDao.addMatch(match: testMatch1);
expect(added, isTrue);
added = await database.matchDao.addMatch(match: testMatch1);
expect(added, isFalse);
final matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
});
test('addMatchesAsList() returns isFalse for empty list', () async {
var added = await database.matchDao.addMatchesAsList(matches: []);
expect(added, isFalse);
});
test('addMatchesAsList() ignores duplicate games', () async {
final added = await database.matchDao.addMatchesAsList(
matches: [testMatch1, testMatch2, testMatch1],
);
expect(added, isTrue);
final matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 2);
});
});
// Verifies that adding the same match twice does not create duplicates.
test('Adding the same match twice does not create duplicates', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.matchDao.addMatch(match: testMatch1);
group('READ', () {
test('matchExists() works correctly', () async {
var matchExists = await database.matchDao.matchExists(
matchId: testMatch1.id,
);
expect(matchExists, isFalse);
final matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
await database.matchDao.addMatch(match: testMatch1);
matchExists = await database.matchDao.matchExists(
matchId: testMatch1.id,
);
expect(matchExists, isTrue);
});
test('getGroupMatches() works correctly', () async {
var matches = await database.matchDao.getGroupMatches(
groupId: 'non-existing-id',
);
expect(matches, isEmpty);
await database.matchDao.addMatch(match: testMatch1);
matches = await database.matchDao.getGroupMatches(
groupId: testGroup1.id,
);
expect(matches, isNotEmpty);
final match = matches.first;
expect(match.id, testMatch1.id);
expect(match.group, isNotNull);
expect(match.group!.id, testGroup1.id);
});
});
// Verifies that matchExists returns correct boolean based on match presence.
test('Match existence check works correctly', () async {
var matchExists = await database.matchDao.matchExists(
matchId: testMatch1.id,
);
expect(matchExists, false);
group('UPDATE', () {
test('updateMatchName() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.matchDao.addMatch(match: testMatch1);
const newName = 'New name';
await database.matchDao.updateMatchName(
matchId: testMatch1.id,
name: newName,
);
matchExists = await database.matchDao.matchExists(matchId: testMatch1.id);
expect(matchExists, true);
});
final fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.name, newName);
});
// Verifies that deleteMatch removes the match and returns true.
test('Deleting a match works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
test('updateMatchName() does nothing for non-existent match', () async {
final updated = await database.matchDao.updateMatchName(
matchId: 'non-existing-id',
name: 'New Name',
);
expect(updated, isFalse);
final matchDeleted = await database.matchDao.deleteMatch(
matchId: testMatch1.id,
);
expect(matchDeleted, true);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches, isEmpty);
});
final matchExists = await database.matchDao.matchExists(
matchId: testMatch1.id,
);
expect(matchExists, false);
});
test('updateMatchGroup() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.groupDao.addGroup(group: testGroup2);
// Verifies that getMatchCount returns correct count through add/delete operations.
test('Getting the match count works correctly', () async {
var matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 0);
await database.matchDao.updateMatchGroup(
matchId: testMatch1.id,
groupId: testGroup2.id,
);
await database.matchDao.addMatch(match: testMatch1);
final fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.group?.id, testGroup2.id);
});
sneeex marked this conversation as resolved Outdated

oben testest du auch noch löschen hier nicht? wieso?

oben testest du auch noch löschen hier nicht? wieso?

fixed

fixed
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
test('updateMatchGroup() does nothing for non-existent match', () async {
final updated = await database.matchDao.updateMatchGroup(
matchId: 'non-existing-id',
groupId: 'group-id',
);
expect(updated, isFalse);
await database.matchDao.addMatch(match: testMatch2);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches, isEmpty);
});
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 2);
await database.matchDao.deleteMatch(matchId: testMatch1.id);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
await database.matchDao.deleteMatch(matchId: testMatch2.id);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 0);
});
// Verifies that updateMatchName correctly updates only the name field.
test('Renaming a match works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
var fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.name, testMatch1.name);
const newName = 'Updated Match Name';
await database.matchDao.updateMatchName(
matchId: testMatch1.id,
newName: newName,
);
fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.name, newName);
});
test('Fetching a winner works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
var fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.mvp, isNotNull);
expect(fetchedMatch.mvp.first.id, testPlayer4.id);
});
test('Setting a winner works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.scoreEntryDao.setWinner(
matchId: testMatch1.id,
playerId: testPlayer5.id,
);
final fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.mvp, isNotNull);
expect(fetchedMatch.mvp.first.id, testPlayer5.id);
});
test(
'removeMatchGroup removes group from match with existing group',
() async {
test('removeMatchGroup() works correctly', () async {
expect(testMatch1.group, isNotNull);
await database.matchDao.addMatch(match: testMatch1);
final removed = await database.matchDao.removeMatchGroup(
@@ -318,53 +327,149 @@ void main() {
matchId: testMatch1.id,
);
expect(updatedMatch.group, null);
expect(updatedMatch.game.id, testMatch1.game.id);
expect(updatedMatch.name, testMatch1.name);
expect(updatedMatch.notes, testMatch1.notes);
},
);
});
test(
'removeMatchGroup on match that already has no group still succeeds',
() async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
test(
'removeMatchGroup() on match that already has no group still succeeds',
() async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
final removed = await database.matchDao.removeMatchGroup(
matchId: testMatchOnlyPlayers.id,
);
expect(removed, isTrue);
final removed = await database.matchDao.removeMatchGroup(
matchId: testMatchOnlyPlayers.id,
);
expect(removed, isTrue);
final updatedMatch = await database.matchDao.getMatchById(
matchId: testMatchOnlyPlayers.id,
);
expect(updatedMatch.group, null);
},
);
test('removeMatchGroup on non-existing match returns false', () async {
final removed = await database.matchDao.removeMatchGroup(
matchId: 'non-existing-id',
final updatedMatch = await database.matchDao.getMatchById(
matchId: testMatchOnlyPlayers.id,
);
expect(updatedMatch.group, null);
},
);
expect(removed, isFalse);
test(
'removeMatchGroup() on non-existing match returns isFalse',
() async {
final removed = await database.matchDao.removeMatchGroup(
matchId: 'non-existing-id',
);
expect(removed, isFalse);
},
);
test('updateMatchNotes() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
const newName = 'New name';
await database.matchDao.updateMatchName(
matchId: testMatch1.id,
name: newName,
);
final fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.name, newName);
});
test('updateMatchNotes() does nothing for non-existent game', () async {
final updated = await database.matchDao.updateMatchNotes(
matchId: 'non-existing-id',
notes: 'New notes',
);
expect(updated, isFalse);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches, isEmpty);
});
test('updateMatchEndedAt() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
DateTime newEndedAt = DateTime(2030, 1, 1, 12, 0, 0);
print(newEndedAt);
await database.matchDao.updateMatchEndedAt(
matchId: testMatch1.id,
endedAt: newEndedAt,
);
final fetchedMatch = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(fetchedMatch.endedAt, newEndedAt);
});
test('updateMatchEndedAt() does nothing for non-existent game', () async {
final updated = await database.matchDao.updateMatchEndedAt(
matchId: 'non-existing-id',
endedAt: DateTime.now(),
);
expect(updated, isFalse);
final allMatches = await database.matchDao.getAllMatches();
expect(allMatches, isEmpty);
});
test('Getting the match count works correctly', () async {
var matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 0);
await database.matchDao.addMatch(match: testMatch1);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
await database.matchDao.addMatch(match: testMatch2);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 2);
await database.matchDao.deleteMatch(matchId: testMatch1.id);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 1);
await database.matchDao.deleteMatch(matchId: testMatch2.id);
matchCount = await database.matchDao.getMatchCount();
expect(matchCount, 0);
});
});
test('Fetching all matches related to a group', () async {
var matches = await database.matchDao.getGroupMatches(
groupId: 'non-existing-id',
);
group('DELETE', () {
test('deleteMatch() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
expect(matches, isEmpty);
var deleted = await database.matchDao.deleteMatch(
matchId: testMatch1.id,
);
expect(deleted, isTrue);
await database.matchDao.addMatch(match: testMatch1);
final matchExists = await database.matchDao.matchExists(
matchId: testMatch1.id,
);
expect(matchExists, isFalse);
matches = await database.matchDao.getGroupMatches(groupId: testGroup1.id);
deleted = await database.matchDao.deleteMatch(matchId: testMatch1.id);
expect(deleted, isFalse);
});
expect(matches, isNotEmpty);
test('deleteAllMatches() works correctly', () async {
await database.matchDao.addMatchesAsList(
matches: [testMatch1, testMatch2, testMatchOnlyPlayers],
);
final match = matches.first;
expect(match.id, testMatch1.id);
expect(match.group, isNotNull);
expect(match.group!.id, testGroup1.id);
var count = await database.matchDao.getMatchCount();
expect(count, 3);
var deleted = await database.matchDao.deleteAllMatches();
expect(deleted, isTrue);
count = await database.matchDao.getMatchCount();
expect(count, 0);
deleted = await database.matchDao.deleteAllMatches();
expect(deleted, isFalse);
});
});
test('getMatchCountByGame() works correctly', () async {

View File

@@ -1,3 +1,5 @@
import 'dart:core' hide Match;
import 'package:clock/clock.dart';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
@@ -18,8 +20,11 @@ void main() {
late Team testTeam1;
late Team testTeam2;
late Team testTeam3;
late Game testGame1;
late Game testGame2;
late Team testTeam4;
late Game testGame;
late Match testMatch1;
late Match testMatch2;
late Match matchWithNoTeams;
final fixedDate = DateTime(2025, 11, 19, 00, 11, 23);
final fakeClock = Clock(() => fixedDate);
@@ -40,27 +45,35 @@ void main() {
testTeam1 = Team(name: 'Team Alpha', members: [testPlayer1, testPlayer2]);
testTeam2 = Team(name: 'Team Beta', members: [testPlayer3, testPlayer4]);
testTeam3 = Team(name: 'Team Gamma', members: [testPlayer1, testPlayer3]);
testGame1 = Game(
name: 'Game 1',
ruleset: Ruleset.singleWinner,
description: 'Test game 1',
testTeam4 = Team(name: 'Team Omega', members: [testPlayer2, testPlayer4]);
testGame = Game(
name: 'Test Game',
ruleset: Ruleset.highestScore,
color: GameColor.blue,
icon: '',
);
testGame2 = Game(
name: 'Game 2',
ruleset: Ruleset.highestScore,
description: 'Test game 2',
color: GameColor.red,
icon: '',
testMatch1 = Match(
name: 'Match 1',
game: testGame,
players: [],
teams: [testTeam1, testTeam2],
);
testMatch2 = Match(
name: 'Match 2',
game: testGame,
players: [],
teams: [testTeam3, testTeam4],
);
matchWithNoTeams = Match(
name: 'Match with no teams',
game: testGame,
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
});
await database.gameDao.addGame(game: testGame);
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
await database.gameDao.addGame(game: testGame1);
await database.gameDao.addGame(game: testGame2);
});
tearDown(() async {
@@ -68,460 +81,251 @@ void main() {
});
group('Team Tests', () {
// Verifies that a single team can be added and retrieved with all fields intact.
test('Adding and fetching a single team works correctly', () async {
final added = await database.teamDao.addTeam(team: testTeam1);
expect(added, true);
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.id, testTeam1.id);
expect(fetchedTeam.name, testTeam1.name);
expect(fetchedTeam.createdAt, testTeam1.createdAt);
});
// Verifies that multiple teams can be added at once and retrieved correctly.
test('Adding and fetching multiple teams works correctly', () async {
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 3);
final testTeams = {
testTeam1.id: testTeam1,
testTeam2.id: testTeam2,
testTeam3.id: testTeam3,
};
for (final team in allTeams) {
final testTeam = testTeams[team.id]!;
expect(team.id, testTeam.id);
expect(team.name, testTeam.name);
expect(team.createdAt, testTeam.createdAt);
}
});
// Verifies that adding the same team twice does not create duplicates and returns false.
test('Adding the same team twice does not create duplicates', () async {
await database.teamDao.addTeam(team: testTeam1);
final addedAgain = await database.teamDao.addTeam(team: testTeam1);
expect(addedAgain, false);
final teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 1);
});
// Verifies that teamExists returns correct boolean based on team presence.
test('Team existence check works correctly', () async {
var teamExists = await database.teamDao.teamExists(teamId: testTeam1.id);
expect(teamExists, false);
await database.teamDao.addTeam(team: testTeam1);
teamExists = await database.teamDao.teamExists(teamId: testTeam1.id);
expect(teamExists, true);
});
// Verifies that deleteTeam removes the team and returns true.
test('Deleting a team works correctly', () async {
await database.teamDao.addTeam(team: testTeam1);
final teamDeleted = await database.teamDao.deleteTeam(
teamId: testTeam1.id,
);
expect(teamDeleted, true);
final teamExists = await database.teamDao.teamExists(
teamId: testTeam1.id,
);
expect(teamExists, false);
});
// Verifies that deleteTeam returns false for a non-existent team ID.
test('Deleting a non-existent team returns false', () async {
final teamDeleted = await database.teamDao.deleteTeam(
teamId: 'non-existent-id',
);
expect(teamDeleted, false);
});
// Verifies that getTeamCount returns correct count through add/delete operations.
test('Getting the team count works correctly', () async {
var teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 0);
await database.teamDao.addTeam(team: testTeam1);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 1);
await database.teamDao.addTeam(team: testTeam2);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 2);
await database.teamDao.deleteTeam(teamId: testTeam1.id);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 1);
await database.teamDao.deleteTeam(teamId: testTeam2.id);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 0);
});
// Verifies that updateTeamName correctly updates only the name field.
test('Updating team name works correctly', () async {
await database.teamDao.addTeam(team: testTeam1);
var fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.name, testTeam1.name);
const newName = 'Updated Team Name';
await database.teamDao.updateTeamName(
teamId: testTeam1.id,
newName: newName,
);
fetchedTeam = await database.teamDao.getTeamById(teamId: testTeam1.id);
expect(fetchedTeam.name, newName);
});
// Verifies that deleteAllTeams removes all teams from the database.
test('Deleting all teams works correctly', () async {
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
);
var teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 3);
final deleted = await database.teamDao.deleteAllTeams();
expect(deleted, true);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 0);
});
// Verifies that deleteAllTeams returns false when no teams exist.
test('Deleting all teams when empty returns false', () async {
final deleted = await database.teamDao.deleteAllTeams();
expect(deleted, false);
});
// Verifies that addTeamsAsList returns false when given an empty list.
test('Adding teams as list with empty list returns false', () async {
final added = await database.teamDao.addTeamsAsList(teams: []);
expect(added, false);
});
// Verifies that addTeamsAsList with duplicate IDs ignores duplicates and keeps the first.
test('Adding teams with duplicate IDs ignores duplicates', () async {
final duplicateTeam = Team(
id: testTeam1.id,
name: 'Duplicate Team',
members: [testPlayer4],
);
await database.teamDao.addTeamsAsList(
teams: [testTeam1, duplicateTeam, testTeam2],
);
final teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 2);
// The first one should be kept (insertOrIgnore)
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.name, testTeam1.name);
});
// Verifies that getAllTeams returns empty list when no teams exist.
test('Getting all teams when empty returns empty list', () async {
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.isEmpty, true);
});
// Verifies that getTeamById throws exception for non-existent team.
test('Getting non-existent team throws exception', () async {
expect(
() => database.teamDao.getTeamById(teamId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
// Verifies that updating team name preserves other fields.
test('Updating team name preserves other team fields', () async {
await database.teamDao.addTeam(team: testTeam1);
final originalTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
final originalCreatedAt = originalTeam.createdAt;
const newName = 'Brand New Team Name';
await database.teamDao.updateTeamName(
teamId: testTeam1.id,
newName: newName,
);
final updatedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(updatedTeam.name, newName);
expect(updatedTeam.id, testTeam1.id);
expect(updatedTeam.createdAt, originalCreatedAt);
});
// Verifies that team name can be updated to an empty string.
test('Updating team name to empty string works', () async {
await database.teamDao.addTeam(team: testTeam1);
await database.teamDao.updateTeamName(teamId: testTeam1.id, newName: '');
final updatedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(updatedTeam.name, '');
});
// Verifies that team name can be updated to a very long string.
test('Updating team name to long string works', () async {
await database.teamDao.addTeam(team: testTeam1);
final longName = 'A' * 500; // 500 character name
await database.teamDao.updateTeamName(
teamId: testTeam1.id,
newName: longName,
);
final updatedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(updatedTeam.name, longName);
expect(updatedTeam.name.length, 500);
});
// Verifies that updating non-existent team name doesn't throw error.
test('Updating non-existent team name completes without error', () async {
expect(
() => database.teamDao.updateTeamName(
teamId: 'non-existent-id',
newName: 'New Name',
),
returnsNormally,
);
});
// Verifies that deleteTeam only affects the specified team.
test('Deleting one team does not affect other teams', () async {
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
);
await database.teamDao.deleteTeam(teamId: testTeam2.id);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 2);
expect(allTeams.any((t) => t.id == testTeam1.id), true);
expect(allTeams.any((t) => t.id == testTeam2.id), false);
expect(allTeams.any((t) => t.id == testTeam3.id), true);
});
// Verifies that teams with overlapping members are independent.
test('Teams with overlapping members are independent', () async {
// Create two matches since player_match has primary key {playerId, matchId}
final match1 = Match(
name: 'Match 1',
game: testGame1,
players: [testPlayer1, testPlayer2],
);
final match2 = Match(
name: 'Match 2',
game: testGame2,
players: [testPlayer1, testPlayer2],
);
await database.matchDao.addMatch(match: match1);
await database.matchDao.addMatch(match: match2);
// Add teams to database
await database.teamDao.addTeamsAsList(teams: [testTeam1, testTeam3]);
// Associate players with teams through match1
// testTeam1: player1, player2
await database.playerMatchDao.addPlayerToMatch(
playerId: testPlayer1.id,
matchId: match1.id,
teamId: testTeam1.id,
);
await database.playerMatchDao.addPlayerToMatch(
playerId: testPlayer2.id,
matchId: match1.id,
teamId: testTeam1.id,
);
// Associate players with teams through match2
// testTeam3: player1, player3 (overlapping player1)
await database.playerMatchDao.addPlayerToMatch(
playerId: testPlayer1.id,
matchId: match2.id,
teamId: testTeam3.id,
);
await database.playerMatchDao.addPlayerToMatch(
playerId: testPlayer3.id,
matchId: match2.id,
teamId: testTeam3.id,
);
final team1 = await database.teamDao.getTeamById(teamId: testTeam1.id);
final team3 = await database.teamDao.getTeamById(teamId: testTeam3.id);
expect(team1.members.length, 2);
expect(team3.members.length, 2);
expect(team1.members.any((p) => p.id == testPlayer1.id), true);
expect(team3.members.any((p) => p.id == testPlayer1.id), true);
});
// Verifies that adding teams sequentially works correctly.
test('Adding teams sequentially maintains correct count', () async {
var count = await database.teamDao.getTeamCount();
expect(count, 0);
await database.teamDao.addTeam(team: testTeam1);
count = await database.teamDao.getTeamCount();
expect(count, 1);
await database.teamDao.addTeam(team: testTeam2);
count = await database.teamDao.getTeamCount();
expect(count, 2);
await database.teamDao.addTeam(team: testTeam3);
count = await database.teamDao.getTeamCount();
expect(count, 3);
});
// Verifies that getAllTeams returns all teams with correct data.
test('Getting all teams returns all teams with correct data', () async {
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 3);
expect(allTeams.map((t) => t.id).toSet(), {
testTeam1.id,
testTeam2.id,
testTeam3.id,
group('CREATE', () {
test('Adding and fetching a single team works correctly', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
final added = await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
expect(added, isTrue);
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.id, testTeam1.id);
expect(fetchedTeam.name, testTeam1.name);
expect(fetchedTeam.createdAt, testTeam1.createdAt);
expect(fetchedTeam.members.length, testTeam1.members.length);
for (int i = 0; i < fetchedTeam.members.length; i++) {
expect(fetchedTeam.members[i].id, testTeam1.members[i].id);
expect(fetchedTeam.members[i].name, testTeam1.members[i].name);
}
});
test('Adding and fetching multiple teams works correctly', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
matchId: matchWithNoTeams.id,
);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 3);
final testTeams = {
testTeam1.id: testTeam1,
testTeam2.id: testTeam2,
testTeam3.id: testTeam3,
};
for (final team in allTeams) {
final testTeam = testTeams[team.id]!;
expect(team.id, testTeam.id);
expect(team.name, testTeam.name);
expect(team.createdAt, testTeam.createdAt);
}
});
test('addTeam() ignores duplicates', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
var added = await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
expect(added, isTrue);
added = await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
expect(added, isFalse);
final teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 1);
});
test('addTeamsAsList() with empty list returns isFalse', () async {
final added = await database.teamDao.addTeamsAsList(
teams: [],
matchId: matchWithNoTeams.id,
);
expect(added, isFalse);
});
test('addTeamsAsList() ignores duplicates', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
final added = await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam1],
matchId: matchWithNoTeams.id,
);
expect(added, isTrue);
final teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 2);
});
});
// Verifies that teamExists returns false for deleted teams.
test('Team existence returns false after deletion', () async {
await database.teamDao.addTeam(team: testTeam1);
expect(await database.teamDao.teamExists(teamId: testTeam1.id), true);
group('READ', () {
test('getTeamCount works correctly', () async {
var count = await database.teamDao.getTeamCount();
expect(count, 0);
await database.teamDao.deleteTeam(teamId: testTeam1.id);
expect(await database.teamDao.teamExists(teamId: testTeam1.id), false);
await database.matchDao.addMatch(match: testMatch1);
count = await database.teamDao.getTeamCount();
expect(count, 2);
await database.teamDao.addTeam(
team: testTeam2,
matchId: matchWithNoTeams.id,
);
count = await database.teamDao.getTeamCount();
expect(count, 2);
await database.teamDao.deleteTeam(teamId: testTeam1.id);
count = await database.teamDao.getTeamCount();
expect(count, 1);
await database.teamDao.deleteTeam(teamId: testTeam2.id);
count = await database.teamDao.getTeamCount();
expect(count, 0);
});
test('teamExists() works correctly', () async {
var teamExists = await database.teamDao.teamExists(
teamId: testTeam1.id,
);
expect(teamExists, isFalse);
await database.matchDao.addMatch(match: matchWithNoTeams);
await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
teamExists = await database.teamDao.teamExists(teamId: testTeam1.id);
expect(teamExists, isTrue);
});
test('getAllTeams() with no teams returns empty list', () async {
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams, isA<List<Team>>());
expect(allTeams.isEmpty, isTrue);
});
test('getAllTeams() works correctly', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
matchId: matchWithNoTeams.id,
);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 3);
expect(allTeams.map((t) => t.id).toSet(), {
testTeam1.id,
testTeam2.id,
testTeam3.id,
});
});
test('Getting non-existent team throws exception', () async {
expect(
() => database.teamDao.getTeamById(teamId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
});
// Verifies that adding multiple teams in batch then deleting returns correct count.
test('Batch add then partial delete maintains correct count', () async {
await database.teamDao.addTeamsAsList(
teams: [testTeam1, testTeam2, testTeam3],
);
group('UPDATED', () {
test('updateTeamName() works correctly', () async {
await database.matchDao.addMatch(match: matchWithNoTeams);
expect(await database.teamDao.getTeamCount(), 3);
await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
await database.teamDao.deleteTeam(teamId: testTeam1.id);
expect(await database.teamDao.getTeamCount(), 2);
var fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.name, testTeam1.name);
await database.teamDao.deleteTeam(teamId: testTeam3.id);
expect(await database.teamDao.getTeamCount(), 1);
const newName = 'New name';
await database.teamDao.updateTeamName(
teamId: testTeam1.id,
name: newName,
);
fetchedTeam = await database.teamDao.getTeamById(teamId: testTeam1.id);
expect(fetchedTeam.name, newName);
});
test('updateTeamName() does nothing for non-existent team', () async {
final updated = await database.teamDao.updateTeamName(
teamId: 'non-existing-id',
name: 'New Name',
);
expect(updated, isFalse);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams, isEmpty);
});
});
// Verifies that deleteAllTeams with single team works.
test('Deleting all teams with single team returns true', () async {
await database.teamDao.addTeam(team: testTeam1);
expect(await database.teamDao.getTeamCount(), 1);
group('DELETE', () {
test('deleteTeam() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.matchDao.addMatch(match: matchWithNoTeams);
final deleted = await database.teamDao.deleteAllTeams();
expect(deleted, true);
expect(await database.teamDao.getTeamCount(), 0);
});
await database.teamDao.addTeam(
team: testTeam1,
matchId: matchWithNoTeams.id,
);
// Verifies that addTeam after deleteAllTeams works correctly.
test('Adding team after deleteAllTeams works correctly', () async {
await database.teamDao.addTeamsAsList(teams: [testTeam1, testTeam2]);
expect(await database.teamDao.getTeamCount(), 2);
final deleted = await database.teamDao.deleteTeam(teamId: testTeam1.id);
expect(deleted, isTrue);
await database.teamDao.deleteAllTeams();
expect(await database.teamDao.getTeamCount(), 0);
final teamExists = await database.teamDao.teamExists(
teamId: testTeam1.id,
);
expect(teamExists, isFalse);
});
final added = await database.teamDao.addTeam(team: testTeam3);
expect(added, true);
expect(await database.teamDao.getTeamCount(), 1);
test('Deleting a non-existent team returns isFalse', () async {
final deleted = await database.teamDao.deleteTeam(
teamId: 'non-existent-id',
);
expect(deleted, isFalse);
});
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam3.id,
);
expect(fetchedTeam.name, testTeam3.name);
});
test('deleteAllTeams() works correctly', () async {
await database.matchDao.addMatchesAsList(
matches: [testMatch1, testMatch2],
);
var teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 4);
// Verifies that addTeamsAsList with partial duplicates ignores duplicates.
test('Adding teams with some duplicates ignores only duplicates', () async {
await database.teamDao.addTeam(team: testTeam1);
final deleted = await database.teamDao.deleteAllTeams();
expect(deleted, isTrue);
final duplicateTeam1 = Team(
id: testTeam1.id,
name: 'Different Name',
members: [testPlayer3],
);
teamCount = await database.teamDao.getTeamCount();
expect(teamCount, 0);
});
await database.teamDao.addTeamsAsList(
teams: [duplicateTeam1, testTeam2, testTeam3],
);
final allTeams = await database.teamDao.getAllTeams();
expect(allTeams.length, 3);
// Verify testTeam1 retained original name (was inserted first)
final team1 = await database.teamDao.getTeamById(teamId: testTeam1.id);
expect(team1.name, testTeam1.name);
});
// Verifies that team IDs are preserved correctly.
test('Team IDs are preserved through add and retrieve', () async {
await database.teamDao.addTeam(team: testTeam1);
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.id, testTeam1.id);
});
// Verifies that createdAt timestamps are preserved.
test('Team createdAt timestamps are preserved', () async {
await database.teamDao.addTeam(team: testTeam1);
final fetchedTeam = await database.teamDao.getTeamById(
teamId: testTeam1.id,
);
expect(fetchedTeam.createdAt, testTeam1.createdAt);
test('deleteAllTeams() with empty list returns false', () async {
final deleted = await database.teamDao.deleteAllTeams();
expect(deleted, isFalse);
});
});
});
}

View File

@@ -24,6 +24,7 @@ void main() {
withClock(fakeClock, () {
testGame1 = Game(
id: 'game1',
name: 'Chess',
ruleset: Ruleset.singleWinner,
description: 'A classic strategy game',
@@ -54,492 +55,350 @@ void main() {
});
group('Game Tests', () {
// Verifies that getAllGames returns an empty list when the database has no games.
test('getAllGames returns empty list when no games exist', () async {
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
group('CREATE', () {
test('Adding and fetching a single game works correctly', () async {
final added = await database.gameDao.addGame(game: testGame1);
expect(added, isTrue);
// Verifies that a single game can be added and retrieved with all fields intact.
test('Adding and fetching a single game works correctly', () async {
await database.gameDao.addGame(game: testGame1);
final game = await database.gameDao.getGameById(gameId: testGame1.id);
expect(game.id, testGame1.id);
expect(game.name, testGame1.name);
expect(game.ruleset, testGame1.ruleset);
expect(game.description, testGame1.description);
expect(game.color, testGame1.color);
expect(game.icon, testGame1.icon);
expect(game.createdAt, testGame1.createdAt);
});
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 1);
expect(allGames.first.id, testGame1.id);
expect(allGames.first.name, testGame1.name);
expect(allGames.first.ruleset, testGame1.ruleset);
expect(allGames.first.description, testGame1.description);
expect(allGames.first.color, testGame1.color);
expect(allGames.first.icon, testGame1.icon);
expect(allGames.first.createdAt, testGame1.createdAt);
});
test('Adding and fetching multiple games works correctly', () async {
final added = await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
expect(added, isTrue);
// Verifies that multiple games can be added and retrieved correctly.
test('Adding and fetching multiple games works correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.addGame(game: testGame2);
await database.gameDao.addGame(game: testGame3);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 3);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 3);
// Map for connecting fetched games with expected games
final testGames = {
testGame1.id: testGame1,
testGame2.id: testGame2,
testGame3.id: testGame3,
};
final names = allGames.map((g) => g.name).toList();
expect(names, containsAll(['Chess', 'Poker', 'Monopoly']));
});
for (final game in allGames) {
final testGame = testGames[game.id]!;
// Verifies that getGameById returns the correct game with all properties.
test('getGameById returns correct game', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.addGame(game: testGame2);
expect(game.id, testGame.id);
expect(game.name, testGame.name);
expect(game.createdAt, testGame.createdAt);
expect(game.description, testGame.description);
expect(game.ruleset, testGame.ruleset);
expect(game.color, testGame.color);
expect(game.icon, testGame.icon);
}
});
final game = await database.gameDao.getGameById(gameId: testGame2.id);
expect(game.id, testGame2.id);
expect(game.name, testGame2.name);
expect(game.ruleset, testGame2.ruleset);
expect(game.description, testGame2.description);
expect(game.color, testGame2.color);
expect(game.icon, testGame2.icon);
});
test('addGamesAsList() returns false for empty list', () async {
final result = await database.gameDao.addGamesAsList(games: []);
expect(result, isFalse);
// Verifies that getGameById throws a StateError when the game doesn't exist.
test('getGameById throws exception for non-existent game', () async {
expect(
() => database.gameDao.getGameById(gameId: 'non-existent-id'),
throwsA(isA<StateError>()),
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 0);
});
test('addGamesAsList() ignores duplicate games', () async {
final added = await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame1],
);
expect(added, isTrue);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 2);
});
test(
'Game with special characters in name is stored correctly',
() async {
final specialGame = Game(
name: 'Game\'s & "Special" <Name>',
ruleset: Ruleset.multipleWinners,
description: 'Description with émojis 🎮🎲',
color: GameColor.purple,
icon: '',
);
await database.gameDao.addGame(game: specialGame);
final fetchedGame = await database.gameDao.getGameById(
gameId: specialGame.id,
);
expect(fetchedGame.name, 'Game\'s & "Special" <Name>');
expect(fetchedGame.description, 'Description with émojis 🎮🎲');
},
);
});
// Verifies that addGame returns true when a game is successfully added.
test('addGame returns true when game is added successfully', () async {
final result = await database.gameDao.addGame(game: testGame1);
expect(result, true);
group('READ', () {
test('getGameById() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 1);
final game = await database.gameDao.getGameById(gameId: testGame1.id);
expect(game.id, testGame1.id);
expect(game.name, testGame1.name);
expect(game.ruleset, testGame1.ruleset);
expect(game.description, testGame1.description);
expect(game.color, testGame1.color);
expect(game.icon, testGame1.icon);
});
test('getGameById() throws exception for non-existent game', () async {
expect(
() => database.gameDao.getGameById(gameId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
test('gameExists() works correctly', () async {
var exists = await database.gameDao.gameExists(gameId: testGame1.id);
expect(exists, isFalse);
await database.gameDao.addGame(game: testGame1);
exists = await database.gameDao.gameExists(gameId: testGame1.id);
expect(exists, isTrue);
});
test('getAllGames() returns empty list when no games exist', () async {
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('getGameCount() works correctly', () async {
var count = await database.gameDao.getGameCount();
expect(count, 0);
await database.gameDao.addGame(game: testGame1);
count = await database.gameDao.getGameCount();
expect(count, 1);
await database.gameDao.addGame(game: testGame2);
count = await database.gameDao.getGameCount();
expect(count, 2);
await database.gameDao.deleteGame(gameId: testGame1.id);
count = await database.gameDao.getGameCount();
expect(count, 1);
});
});
// Verifies that addGame returns false when trying to add a duplicate game.
test('addGame returns false when game already exists', () async {
final firstAdd = await database.gameDao.addGame(game: testGame1);
expect(firstAdd, true);
group('UPDATE', () {
test('updateGameName() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
const newName = 'New name';
final secondAdd = await database.gameDao.addGame(game: testGame1);
expect(secondAdd, false);
final updated = await database.gameDao.updateGameName(
gameId: testGame1.id,
name: newName,
);
expect(updated, isTrue);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 1);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.name, newName);
});
test('updateGameName() does nothing for non-existent game', () async {
final updated = await database.gameDao.updateGameName(
gameId: 'non-existent-id',
name: 'New name',
);
expect(updated, isFalse);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('updateGameRuleset() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
const ruleset = Ruleset.highestScore;
final updated = await database.gameDao.updateGameRuleset(
gameId: testGame1.id,
ruleset: ruleset,
);
expect(updated, isTrue);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.ruleset, ruleset);
});
test('updateGameRuleset() does nothing for non-existent game', () async {
final updated = await database.gameDao.updateGameRuleset(
gameId: 'non-existent-id',
ruleset: Ruleset.lowestScore,
);
expect(updated, isFalse);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('updateGameDescription() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
const newDescription = 'New description';
final updated = await database.gameDao.updateGameDescription(
gameId: testGame1.id,
description: newDescription,
);
expect(updated, isTrue);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.description, newDescription);
});
test(
'updateGameDescription() does nothing for non-existent game',
() async {
final updated = await database.gameDao.updateGameDescription(
gameId: 'non-existent-id',
description: 'New description',
);
expect(updated, isFalse);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
},
);
test('updateGameColor() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameColor(
gameId: testGame1.id,
color: GameColor.green,
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.color, GameColor.green);
});
test('updateGameColor() does nothing for non-existent game', () async {
final updated = await database.gameDao.updateGameColor(
gameId: 'non-existent-id',
color: GameColor.green,
);
expect(updated, isFalse);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('updateGameIcon() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
const newIcon = 'new_chess_icon';
final updated = await database.gameDao.updateGameIcon(
gameId: testGame1.id,
icon: newIcon,
);
expect(updated, isTrue);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.icon, newIcon);
});
test('updateGameIcon() does nothing for non-existent game', () async {
final updated = await database.gameDao.updateGameIcon(
gameId: 'non-existent-id',
icon: 'New icon',
);
expect(updated, isFalse);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('Multiple updates to the same game work correctly', () async {
await database.gameDao.addGame(game: testGame1);
const newName = 'New name';
await database.gameDao.updateGameName(
gameId: testGame1.id,
name: newName,
);
const newGameColor = GameColor.teal;
await database.gameDao.updateGameColor(
gameId: testGame1.id,
color: newGameColor,
);
const newDescription = 'New description';
await database.gameDao.updateGameDescription(
gameId: testGame1.id,
description: newDescription,
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
// Changed values
expect(updatedGame.name, newName);
expect(updatedGame.color, newGameColor);
expect(updatedGame.description, newDescription);
// Staying the same
expect(updatedGame.ruleset, testGame1.ruleset);
expect(updatedGame.icon, testGame1.icon);
});
});
// Verifies that a game with empty optional fields can be added and retrieved.
test('addGame handles game with null optional fields', () async {
final gameWithNulls = Game(
name: 'Simple Game',
ruleset: Ruleset.lowestScore,
description: 'A simple game',
color: GameColor.green,
icon: '',
);
final result = await database.gameDao.addGame(game: gameWithNulls);
expect(result, true);
final fetchedGame = await database.gameDao.getGameById(
gameId: gameWithNulls.id,
);
expect(fetchedGame.name, 'Simple Game');
expect(fetchedGame.description, 'A simple game');
expect(fetchedGame.color, GameColor.green);
expect(fetchedGame.icon, '');
});
// Verifies that multiple games can be added at once using addGamesAsList.
test('addGamesAsList adds multiple games correctly', () async {
final result = await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
expect(result, true);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 3);
});
// Verifies that addGamesAsList returns false when given an empty list.
test('addGamesAsList returns false for empty list', () async {
final result = await database.gameDao.addGamesAsList(games: []);
expect(result, false);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 0);
});
// Verifies that addGamesAsList ignores duplicate games when adding.
test('addGamesAsList ignores duplicate games', () async {
await database.gameDao.addGame(game: testGame1);
final result = await database.gameDao.addGamesAsList(
games: [testGame1, testGame2],
);
expect(result, true);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 2);
});
// Verifies that deleteGame returns true and removes the game from database.
test('deleteGame returns true when game is deleted', () async {
await database.gameDao.addGame(game: testGame1);
final result = await database.gameDao.deleteGame(gameId: testGame1.id);
expect(result, true);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that deleteGame returns false for a non-existent game ID.
test('deleteGame returns false for non-existent game', () async {
final result = await database.gameDao.deleteGame(
gameId: 'non-existent-id',
);
expect(result, false);
});
// Verifies that deleteGame only removes the specified game, leaving others intact.
test('deleteGame only deletes the specified game', () async {
await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
await database.gameDao.deleteGame(gameId: testGame2.id);
final allGames = await database.gameDao.getAllGames();
expect(allGames.length, 2);
expect(allGames.any((g) => g.id == testGame2.id), false);
expect(allGames.any((g) => g.id == testGame1.id), true);
expect(allGames.any((g) => g.id == testGame3.id), true);
});
// Verifies that gameExists returns true when the game exists in database.
test('gameExists returns true for existing game', () async {
await database.gameDao.addGame(game: testGame1);
final exists = await database.gameDao.gameExists(gameId: testGame1.id);
expect(exists, true);
});
// Verifies that gameExists returns false for a non-existent game ID.
test('gameExists returns false for non-existent game', () async {
final exists = await database.gameDao.gameExists(
gameId: 'non-existent-id',
);
expect(exists, false);
});
// Verifies that gameExists returns false after a game has been deleted.
test('gameExists returns false after game is deleted', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.deleteGame(gameId: testGame1.id);
final exists = await database.gameDao.gameExists(gameId: testGame1.id);
expect(exists, false);
});
// Verifies that updateGameName correctly updates only the name field.
test('updateGameName updates the name correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameName(
gameId: testGame1.id,
newName: 'Updated Chess',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.name, 'Updated Chess');
expect(updatedGame.ruleset, testGame1.ruleset);
});
// Verifies that updateGameName does nothing when game doesn't exist.
test('updateGameName does nothing for non-existent game', () async {
await database.gameDao.updateGameName(
gameId: 'non-existent-id',
newName: 'New Name',
);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that updateGameRuleset correctly updates only the ruleset field.
test('updateGameRuleset updates the ruleset correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameRuleset(
gameId: testGame1.id,
newRuleset: Ruleset.highestScore,
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.ruleset, Ruleset.highestScore);
expect(updatedGame.name, testGame1.name);
});
// Verifies that updateGameRuleset does nothing when game doesn't exist.
test('updateGameRuleset does nothing for non-existent game', () async {
await database.gameDao.updateGameRuleset(
gameId: 'non-existent-id',
newRuleset: Ruleset.lowestScore,
);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that updateGameDescription correctly updates the description.
test('updateGameDescription updates the description correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameDescription(
gameId: testGame1.id,
newDescription: 'An updated description',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.description, 'An updated description');
});
// Verifies that updateGameDescription can set the description to an empty string.
test('updateGameDescription can set description to empty string', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameDescription(
gameId: testGame1.id,
newDescription: '',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.description, '');
});
// Verifies that updateGameDescription does nothing when game doesn't exist.
test('updateGameDescription does nothing for non-existent game', () async {
await database.gameDao.updateGameDescription(
gameId: 'non-existent-id',
newDescription: 'New Description',
);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that updateGameColor correctly updates the color value.
test('updateGameColor updates the color correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameColor(
gameId: testGame1.id,
newColor: GameColor.green,
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.color, GameColor.green);
});
// Verifies that updateGameColor does nothing when game doesn't exist.
test('updateGameColor does nothing for non-existent game', () async {
await database.gameDao.updateGameColor(
gameId: 'non-existent-id',
newColor: GameColor.green,
);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that updateGameIcon correctly updates the icon value.
test('updateGameIcon updates the icon correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameIcon(
gameId: testGame1.id,
newIcon: 'new_chess_icon',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.icon, 'new_chess_icon');
});
// Verifies that updateGameIcon can update the icon.
test('updateGameIcon updates icon correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameIcon(
gameId: testGame1.id,
newIcon: 'new_icon',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.icon, 'new_icon');
});
// Verifies that updateGameIcon does nothing when game doesn't exist.
test('updateGameIcon does nothing for non-existent game', () async {
await database.gameDao.updateGameIcon(
gameId: 'non-existent-id',
newIcon: 'some_icon',
);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
// Verifies that getGameCount returns 0 when no games exist.
test('getGameCount returns 0 when no games exist', () async {
final count = await database.gameDao.getGameCount();
expect(count, 0);
});
// Verifies that getGameCount returns the correct count after adding games.
test('getGameCount returns correct count after adding games', () async {
await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
final count = await database.gameDao.getGameCount();
expect(count, 3);
});
// Verifies that getGameCount updates correctly after deleting a game.
test('getGameCount updates correctly after deletion', () async {
await database.gameDao.addGamesAsList(games: [testGame1, testGame2]);
final countBefore = await database.gameDao.getGameCount();
expect(countBefore, 2);
await database.gameDao.deleteGame(gameId: testGame1.id);
final countAfter = await database.gameDao.getGameCount();
expect(countAfter, 1);
});
// Verifies that deleteAllGames removes all games from the database.
test('deleteAllGames removes all games', () async {
await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
final countBefore = await database.gameDao.getGameCount();
expect(countBefore, 3);
final result = await database.gameDao.deleteAllGames();
expect(result, true);
final countAfter = await database.gameDao.getGameCount();
expect(countAfter, 0);
});
// Verifies that deleteAllGames returns false when no games exist.
test('deleteAllGames returns false when no games exist', () async {
final result = await database.gameDao.deleteAllGames();
expect(result, false);
});
// Verifies that games with special characters (quotes, emojis) are stored correctly.
test('Game with special characters in name is stored correctly', () async {
final specialGame = Game(
name: 'Game\'s & "Special" <Name>',
ruleset: Ruleset.multipleWinners,
description: 'Description with émojis 🎮🎲',
color: GameColor.purple,
icon: '',
);
await database.gameDao.addGame(game: specialGame);
final fetchedGame = await database.gameDao.getGameById(
gameId: specialGame.id,
);
expect(fetchedGame.name, 'Game\'s & "Special" <Name>');
expect(fetchedGame.description, 'Description with émojis 🎮🎲');
});
// Verifies that games with empty string fields are stored and retrieved correctly.
test('Game with empty string fields is stored correctly', () async {
final emptyGame = Game(
name: '',
ruleset: Ruleset.singleWinner,
description: '',
icon: '',
color: GameColor.red,
);
await database.gameDao.addGame(game: emptyGame);
final fetchedGame = await database.gameDao.getGameById(
gameId: emptyGame.id,
);
expect(fetchedGame.name, '');
expect(fetchedGame.ruleset, Ruleset.singleWinner);
expect(fetchedGame.description, '');
expect(fetchedGame.icon, '');
});
// Verifies that games with very long strings (10000 chars) are handled correctly.
test('Game with very long strings is stored correctly', () async {
final longString = 'A' * 10000;
final longGame = Game(
name: longString,
description: longString,
ruleset: Ruleset.multipleWinners,
color: GameColor.yellow,
icon: '',
);
await database.gameDao.addGame(game: longGame);
final fetchedGame = await database.gameDao.getGameById(
gameId: longGame.id,
);
expect(fetchedGame.name.length, 10000);
expect(fetchedGame.description.length, 10000);
expect(fetchedGame.ruleset, Ruleset.multipleWinners);
});
// Verifies that multiple sequential updates to the same game work correctly.
test('Multiple updates to the same game work correctly', () async {
await database.gameDao.addGame(game: testGame1);
await database.gameDao.updateGameName(
gameId: testGame1.id,
newName: 'Updated Name',
);
await database.gameDao.updateGameColor(
gameId: testGame1.id,
newColor: GameColor.teal,
);
await database.gameDao.updateGameDescription(
gameId: testGame1.id,
newDescription: 'Updated Description',
);
final updatedGame = await database.gameDao.getGameById(
gameId: testGame1.id,
);
expect(updatedGame.name, 'Updated Name');
expect(updatedGame.color, GameColor.teal);
expect(updatedGame.description, 'Updated Description');
expect(updatedGame.ruleset, testGame1.ruleset);
expect(updatedGame.icon, testGame1.icon);
group('DELETE', () {
test('deleteGame() works correctly', () async {
await database.gameDao.addGame(game: testGame1);
final deleted = await database.gameDao.deleteGame(gameId: testGame1.id);
expect(deleted, isTrue);
final allGames = await database.gameDao.getAllGames();
expect(allGames, isEmpty);
});
test('deleteGame() returns false for non-existent game', () async {
final deleted = await database.gameDao.deleteGame(
gameId: 'non-existent-id',
);
expect(deleted, isFalse);
});
test('deleteAllGames() removes all games', () async {
await database.gameDao.addGamesAsList(
games: [testGame1, testGame2, testGame3],
);
var count = await database.gameDao.getGameCount();
expect(count, 3);
final deleted = await database.gameDao.deleteAllGames();
expect(deleted, isTrue);
count = await database.gameDao.getGameCount();
expect(count, 0);
});
test('deleteAllGames() returns false when no games exist', () async {
final deleted = await database.gameDao.deleteAllGames();
expect(deleted, isFalse);
});
});
});
}

View File

@@ -24,8 +24,8 @@ void main() {
);
withClock(fakeClock, () {
testPlayer1 = Player(name: 'Test Player');
testPlayer2 = Player(name: 'Second Player');
testPlayer1 = Player(name: 'Anna', description: 'First test player');
testPlayer2 = Player(name: 'Bob', description: 'Second test player');
testPlayer3 = Player(name: 'Charlie');
testPlayer4 = Player(name: 'Diana');
});
@@ -35,355 +35,314 @@ void main() {
});
group('Player Tests', () {
// Verifies that players can be added and retrieved with all fields intact.
test('Adding and fetching single player works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer2);
group('CREATE', () {
test('Adding and fetching single player works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer2);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 2);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 2);
final fetchedPlayer1 = allPlayers.firstWhere(
(g) => g.id == testPlayer1.id,
);
expect(fetchedPlayer1.name, testPlayer1.name);
expect(fetchedPlayer1.createdAt, testPlayer1.createdAt);
final fetchedPlayer1 = allPlayers.firstWhere(
(g) => g.id == testPlayer1.id,
);
expect(fetchedPlayer1.name, testPlayer1.name);
expect(fetchedPlayer1.createdAt, testPlayer1.createdAt);
expect(fetchedPlayer1.description, testPlayer1.description);
final fetchedPlayer2 = allPlayers.firstWhere(
(g) => g.id == testPlayer2.id,
);
expect(fetchedPlayer2.name, testPlayer2.name);
expect(fetchedPlayer2.createdAt, testPlayer2.createdAt);
});
final fetchedPlayer2 = allPlayers.firstWhere(
(g) => g.id == testPlayer2.id,
);
expect(fetchedPlayer2.name, testPlayer2.name);
expect(fetchedPlayer2.createdAt, testPlayer2.createdAt);
expect(fetchedPlayer2.description, testPlayer2.description);
});
// Verifies that multiple players can be added at once and retrieved correctly.
test('Adding and fetching multiple players works correctly', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 4);
// Map for connecting fetched players with expected players
final testPlayers = {
testPlayer1.id: testPlayer1,
testPlayer2.id: testPlayer2,
testPlayer3.id: testPlayer3,
testPlayer4.id: testPlayer4,
};
for (final player in allPlayers) {
final testPlayer = testPlayers[player.id]!;
expect(player.id, testPlayer.id);
expect(player.name, testPlayer.name);
expect(player.createdAt, testPlayer.createdAt);
}
});
// Verifies that adding the same player twice does not create duplicates.
test('Adding the same player twice does not create duplicates', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer1);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 1);
});
// Verifies that playerExists returns correct boolean based on player presence.
test('Player existence check works correctly', () async {
var playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, false);
await database.playerDao.addPlayer(player: testPlayer1);
playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, true);
});
// Verifies that deletePlayer removes the player and returns true.
test('Deleting a player works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
final playerDeleted = await database.playerDao.deletePlayer(
playerId: testPlayer1.id,
);
expect(playerDeleted, true);
final playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, false);
});
// Verifies that updatePlayerName correctly updates only the name field.
test('Updating a player name works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
const newPlayerName = 'new player name';
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
newName: newPlayerName,
);
final result = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(result.name, newPlayerName);
});
// Verifies that getPlayerCount returns correct count through add/delete operations.
test('Getting the player count works correctly', () async {
var playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
await database.playerDao.addPlayer(player: testPlayer1);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 1);
await database.playerDao.addPlayer(player: testPlayer2);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 2);
await database.playerDao.deletePlayer(playerId: testPlayer1.id);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 1);
await database.playerDao.deletePlayer(playerId: testPlayer2.id);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
});
// Verifies that getAllPlayers returns an empty list when no players exist.
test('getAllPlayers returns empty list when no players exist', () async {
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
});
// Verifies that getPlayerById returns the correct player.
test('getPlayerById returns correct player', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer2);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.id, testPlayer1.id);
expect(fetchedPlayer.name, testPlayer1.name);
expect(fetchedPlayer.createdAt, testPlayer1.createdAt);
expect(fetchedPlayer.description, testPlayer1.description);
});
// Verifies that getPlayerById throws StateError for non-existent player ID.
test('getPlayerById throws exception for non-existent player', () async {
expect(
() => database.playerDao.getPlayerById(playerId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
});
// Verifies that addPlayer returns false when trying to add a duplicate player.
test('addPlayer returns false when player already exists', () async {
final firstAdd = await database.playerDao.addPlayer(player: testPlayer1);
expect(firstAdd, true);
final secondAdd = await database.playerDao.addPlayer(player: testPlayer1);
expect(secondAdd, false);
});
// Verifies that addPlayersAsList handles empty list correctly.
test('addPlayersAsList handles empty list correctly', () async {
final result = await database.playerDao.addPlayersAsList(players: []);
expect(result, false);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
});
// Verifies that addPlayersAsList ignores duplicate player IDs.
test('addPlayersAsList with duplicate IDs ignores duplicates', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer1, testPlayer2],
);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 2);
});
// Verifies that deletePlayer returns false for non-existent player.
test('deletePlayer returns false for non-existent player', () async {
final result = await database.playerDao.deletePlayer(
playerId: 'non-existent-id',
);
expect(result, false);
});
// Verifies that updatePlayerName does nothing for non-existent player (no exception).
test('updatePlayerName does nothing for non-existent player', () async {
// Should not throw, just do nothing
await database.playerDao.updatePlayerName(
playerId: 'non-existent-id',
newName: 'New Name',
);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
});
// Verifies that deleteAllPlayers removes all players.
test('deleteAllPlayers removes all players', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3],
);
var playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 3);
final result = await database.playerDao.deleteAllPlayers();
expect(result, true);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
});
// Verifies that deleteAllPlayers returns false when no players exist.
test('deleteAllPlayers returns false when no players exist', () async {
final result = await database.playerDao.deleteAllPlayers();
expect(result, false);
});
// Verifies that a player with special characters in name is stored correctly.
test(
'Player with special characters in name is stored correctly',
() async {
final specialPlayer = Player(
name: 'Test!@#\$%^&*()_+-=[]{}|;\':",.<>?/`~',
description: '',
test('Adding and fetching multiple players works correctly', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
await database.playerDao.addPlayer(player: specialPlayer);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 4);
// Map for connecting fetched players with expected players
final testPlayers = {
testPlayer1.id: testPlayer1,
testPlayer2.id: testPlayer2,
testPlayer3.id: testPlayer3,
testPlayer4.id: testPlayer4,
};
for (final player in allPlayers) {
final testPlayer = testPlayers[player.id]!;
expect(player.id, testPlayer.id);
expect(player.name, testPlayer.name);
expect(player.createdAt, testPlayer.createdAt);
expect(player.description, testPlayer.description);
}
});
test('Adding the same player twice does not create duplicates', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer1);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 1);
});
test('addPlayer() returns false when player already exists', () async {
var added = await database.playerDao.addPlayer(player: testPlayer1);
expect(added, isTrue);
added = await database.playerDao.addPlayer(player: testPlayer1);
expect(added, isFalse);
});
test('addPlayersAsList() handles empty list correctly', () async {
final added = await database.playerDao.addPlayersAsList(players: []);
expect(added, isFalse);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
});
test(
'addPlayersAsList() with duplicate IDs ignores duplicates',
() async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer1, testPlayer2],
);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers.length, 2);
},
);
test(
'Player with special characters in name is stored correctly',
() async {
final specialPlayer = Player(
name: 'Test!@#\$%^&*()_+-=[]{}|;\':"😎,.<>?/`~',
);
await database.playerDao.addPlayer(player: specialPlayer);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: specialPlayer.id,
);
expect(fetchedPlayer.name, specialPlayer.name);
},
);
});
group('READ', () {
test('getPlayerCount() works correctly', () async {
var playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
await database.playerDao.addPlayer(player: testPlayer1);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 1);
await database.playerDao.addPlayer(player: testPlayer2);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 2);
await database.playerDao.deletePlayer(playerId: testPlayer1.id);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 1);
await database.playerDao.deletePlayer(playerId: testPlayer2.id);
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
});
test('playerExists() works correctly', () async {
var playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, isFalse);
await database.playerDao.addPlayer(player: testPlayer1);
playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, isTrue);
});
test(
'getAllPlayers() returns empty list when no players exist',
() async {
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
},
);
test('getPlayerById() returns correct player', () async {
await database.playerDao.addPlayer(player: testPlayer1);
await database.playerDao.addPlayer(player: testPlayer2);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: specialPlayer.id,
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, specialPlayer.name);
},
);
// Verifies that a player with description is stored correctly.
test('Player with description is stored correctly', () async {
final playerWithDescription = Player(
name: 'Described Player',
description: 'This is a test description',
expect(fetchedPlayer.id, testPlayer1.id);
expect(fetchedPlayer.name, testPlayer1.name);
expect(fetchedPlayer.createdAt, testPlayer1.createdAt);
expect(fetchedPlayer.description, testPlayer1.description);
});
test(
'getPlayerById() throws exception for non-existent player',
() async {
expect(
() => database.playerDao.getPlayerById(playerId: 'non-existent-id'),
throwsA(isA<StateError>()),
);
},
);
await database.playerDao.addPlayer(player: playerWithDescription);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: playerWithDescription.id,
);
expect(fetchedPlayer.name, playerWithDescription.name);
expect(fetchedPlayer.description, playerWithDescription.description);
});
// Verifies that a player with null description is stored correctly.
test('Player with null description is stored correctly', () async {
final playerWithoutDescription = Player(
name: 'No Description Player',
description: '',
group('UPDATE', () {
test('updatePlayerName() works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
const newName = 'New name';
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
name: newName,
);
final player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.name, newName);
});
test('updatePlayerName() does nothing for non-existent player', () async {
final updated = await database.playerDao.updatePlayerName(
playerId: 'non-existent-id',
name: 'New name',
);
expect(updated, isFalse);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
});
test('updatePlayerDescription() works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
const newDescription = 'New description';
final updated = await database.playerDao.updatePlayerDescription(
playerId: testPlayer1.id,
description: newDescription,
);
expect(updated, isTrue);
final player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(player.description, newDescription);
});
test(
'updatePlayerDescription() does nothing for non-existent player',
() async {
final updated = await database.playerDao.updatePlayerDescription(
playerId: 'non-existent-id',
description: 'New description',
);
expect(updated, isFalse);
final allPlayers = await database.playerDao.getAllPlayers();
expect(allPlayers, isEmpty);
},
);
await database.playerDao.addPlayer(player: playerWithoutDescription);
test('Multiple updates to the same player work correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: playerWithoutDescription.id,
);
expect(fetchedPlayer.description, '');
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
name: 'First Update',
);
var fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, 'First Update');
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
name: 'Second Update',
);
fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, 'Second Update');
await database.playerDao.updatePlayerDescription(
playerId: testPlayer1.id,
description: 'Third Update',
);
fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.description, 'Third Update');
});
});
// Verifies that multiple updates to the same player work correctly.
test('Multiple updates to the same player work correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
group('DELETE', () {
test('deletePlayer() works correctly', () async {
await database.playerDao.addPlayer(player: testPlayer1);
final playerDeleted = await database.playerDao.deletePlayer(
playerId: testPlayer1.id,
);
expect(playerDeleted, isTrue);
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
newName: 'First Update',
);
final playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, isFalse);
});
var fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, 'First Update');
test('deletePlayer() returns false for non-existent player', () async {
final deleted = await database.playerDao.deletePlayer(
playerId: 'non-existent-id',
);
expect(deleted, isFalse);
});
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
newName: 'Second Update',
);
test('deleteAllPlayers() removes all players', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2, testPlayer3],
);
fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, 'Second Update');
var playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 3);
await database.playerDao.updatePlayerName(
playerId: testPlayer1.id,
newName: 'Third Update',
);
final deleted = await database.playerDao.deleteAllPlayers();
expect(deleted, isTrue);
fetchedPlayer = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,
);
expect(fetchedPlayer.name, 'Third Update');
playerCount = await database.playerDao.getPlayerCount();
expect(playerCount, 0);
});
test('deleteAllPlayers() returns false when no players exist', () async {
final deleted = await database.playerDao.deleteAllPlayers();
expect(deleted, isFalse);
});
});
// Verifies that a player with empty string name is stored correctly.
test('Player with empty string name is stored correctly', () async {
final emptyNamePlayer = Player(name: '');
await database.playerDao.addPlayer(player: emptyNamePlayer);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: emptyNamePlayer.id,
);
expect(fetchedPlayer.name, '');
});
// Verifies that a player with very long name is stored correctly.
test('Player with very long name is stored correctly', () async {
final longName = 'A' * 1000;
final longNamePlayer = Player(name: longName);
await database.playerDao.addPlayer(player: longNamePlayer);
final fetchedPlayer = await database.playerDao.getPlayerById(
playerId: longNamePlayer.id,
);
expect(fetchedPlayer.name, longName);
});
// Verifies that addPlayer returns true on first add.
test('addPlayer returns true when player is added successfully', () async {
final result = await database.playerDao.addPlayer(player: testPlayer1);
expect(result, true);
final playerExists = await database.playerDao.playerExists(
playerId: testPlayer1.id,
);
expect(playerExists, true);
});
group('Name Count Tests', () {
test('Single player gets initialized wih name count 0', () async {
group('NAME COUNT', () {
test('Single player gets initialized wih name count 0', () async {
await database.playerDao.addPlayer(player: testPlayer1);
final player = await database.playerDao.getPlayerById(
@@ -392,7 +351,7 @@ void main() {
expect(player.nameCount, 0);
});
test('Multiple players get initialized wih name count 0', () async {
test('Multiple players get initialized wih name count 0', () async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2],
);
@@ -470,7 +429,7 @@ void main() {
playerId: testPlayer1.id,
nameCount: 2,
);
expect(success, true);
expect(success, isTrue);
final player = await database.playerDao.getPlayerById(
playerId: testPlayer1.id,

View File

@@ -42,189 +42,162 @@ void main() {
});
group('Player-Group Tests', () {
// Verifies that a player can be added to an existing group and isPlayerInGroup returns true.
test('Adding a player to a group works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
await database.playerDao.addPlayer(player: testPlayer4);
await database.playerGroupDao.addPlayerToGroup(
groupId: testGroup.id,
player: testPlayer4,
);
var playerAdded = await database.playerGroupDao.isPlayerInGroup(
groupId: testGroup.id,
playerId: testPlayer4.id,
);
expect(playerAdded, true);
playerAdded = await database.playerGroupDao.isPlayerInGroup(
groupId: testGroup.id,
playerId: '',
);
expect(playerAdded, false);
});
// Verifies that a player can be removed from a group and the group's member count decreases.
test('Removing player from group works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
final playerToRemove = testGroup.members[0];
final removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: playerToRemove.id,
groupId: testGroup.id,
);
expect(removed, true);
final result = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(result.members.length, testGroup.members.length - 1);
final playerExists = result.members.any((p) => p.id == playerToRemove.id);
expect(playerExists, false);
});
// Verifies that getPlayersOfGroup returns all members of a group with correct data.
test('Retrieving players of a group works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: testGroup.id,
);
for (int i = 0; i < players.length; i++) {
expect(players[i].id, testGroup.members[i].id);
expect(players[i].name, testGroup.members[i].name);
expect(players[i].createdAt, testGroup.members[i].createdAt);
}
});
// Verifies that isPlayerInGroup returns false for non-existent player.
test('isPlayerInGroup returns false for non-existent player', () async {
await database.groupDao.addGroup(group: testGroup);
final result = await database.playerGroupDao.isPlayerInGroup(
playerId: 'non-existent-player-id',
groupId: testGroup.id,
);
expect(result, false);
});
// Verifies that isPlayerInGroup returns false for non-existent group.
test('isPlayerInGroup returns false for non-existent group', () async {
await database.playerDao.addPlayer(player: testPlayer1);
final result = await database.playerGroupDao.isPlayerInGroup(
playerId: testPlayer1.id,
groupId: 'non-existent-group-id',
);
expect(result, false);
});
// Verifies that addPlayerToGroup returns false when player already in group.
test(
'addPlayerToGroup returns false when player already in group',
() async {
group('CREATE', () {
test('addPlayerToGroup() works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
// testPlayer1 is already in testGroup via group creation
final result = await database.playerGroupDao.addPlayerToGroup(
player: testPlayer1,
groupId: testGroup.id,
);
expect(result, false);
},
);
// Verifies that addPlayerToGroup adds player to player table if not exists.
test(
'addPlayerToGroup adds player to player table if not exists',
() async {
await database.groupDao.addGroup(group: testGroup);
// testPlayer4 is not in the database yet
var playerExists = await database.playerDao.playerExists(
playerId: testPlayer4.id,
);
expect(playerExists, false);
await database.playerDao.addPlayer(player: testPlayer4);
await database.playerGroupDao.addPlayerToGroup(
player: testPlayer4,
groupId: testGroup.id,
player: testPlayer4,
);
// Now player should exist in player table
playerExists = await database.playerDao.playerExists(
var playerAdded = await database.playerGroupDao.isPlayerInGroup(
groupId: testGroup.id,
playerId: testPlayer4.id,
);
expect(playerExists, true);
},
);
// Verifies that removePlayerFromGroup returns false for non-existent player.
test(
'removePlayerFromGroup returns false for non-existent player',
() async {
expect(playerAdded, isTrue);
});
test(
'addPlayerToGroup() returns false when player already in group',
() async {
await database.groupDao.addGroup(group: testGroup);
final added = await database.playerGroupDao.addPlayerToGroup(
player: testPlayer1,
groupId: testGroup.id,
);
expect(added, isFalse);
},
);
test(
'addPlayerToGroup() adds player to player table if not exists',
() async {
await database.groupDao.addGroup(group: testGroup);
var playerExists = await database.playerDao.playerExists(
playerId: testPlayer4.id,
);
expect(playerExists, isFalse);
await database.playerGroupDao.addPlayerToGroup(
player: testPlayer4,
groupId: testGroup.id,
);
playerExists = await database.playerDao.playerExists(
playerId: testPlayer4.id,
);
expect(playerExists, isTrue);
},
);
});
group('READ', () {
test(
'isPlayerInGroup() returns false for non-existent player or group',
() async {
await database.groupDao.addGroup(group: testGroup);
var isInGroup = await database.playerGroupDao.isPlayerInGroup(
playerId: 'non-existent-player-id',
groupId: testGroup.id,
);
expect(isInGroup, isFalse);
isInGroup = await database.playerGroupDao.isPlayerInGroup(
playerId: testPlayer1.id,
groupId: 'non-existent-group-id',
);
expect(isInGroup, isFalse);
isInGroup = await database.playerGroupDao.isPlayerInGroup(
playerId: 'non-existent-player-id',
groupId: 'non-existent-group-id',
);
expect(isInGroup, isFalse);
},
);
test('getPlayersOfGroup() works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
final result = await database.playerGroupDao.removePlayerFromGroup(
playerId: 'non-existent-player-id',
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: testGroup.id,
);
expect(result, false);
},
);
for (int i = 0; i < players.length; i++) {
expect(players[i].id, testGroup.members[i].id);
expect(players[i].name, testGroup.members[i].name);
expect(players[i].createdAt, testGroup.members[i].createdAt);
}
});
// Verifies that removePlayerFromGroup returns false for non-existent group.
test(
'removePlayerFromGroup returns false for non-existent group',
() async {
await database.playerDao.addPlayer(player: testPlayer1);
test('getPlayersOfGroup() returns empty list for empty group', () async {
final emptyGroup = Group(name: 'Empty Group', members: []);
await database.groupDao.addGroup(group: emptyGroup);
final result = await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: 'non-existent-group-id',
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: emptyGroup.id,
);
expect(players, isEmpty);
});
expect(result, false);
},
);
// Verifies that getPlayersOfGroup returns empty list for group with no members.
test('getPlayersOfGroup returns empty list for empty group', () async {
final emptyGroup = Group(
name: 'Empty Group',
description: '',
members: [],
test(
'getPlayersOfGroup() returns empty list for non-existent group',
() async {
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: 'non-existent-group-id',
);
expect(players, isEmpty);
},
);
await database.groupDao.addGroup(group: emptyGroup);
});
group('UPDATE', () {
test('replaceGroupPlayers() works correctly ', () async {
await database.groupDao.addGroup(group: testGroup);
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: emptyGroup.id,
);
var groupMembers = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(groupMembers.members.length, testGroup.members.length);
expect(players, isEmpty);
final newPlayersList = [testPlayer3, testPlayer4];
final replaced = await database.playerGroupDao.replaceGroupPlayers(
groupId: testGroup.id,
newPlayers: newPlayersList,
);
expect(replaced, isTrue);
groupMembers = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(groupMembers.members.length, 2);
expect(groupMembers.members.any((p) => p.id == testPlayer3.id), isTrue);
expect(groupMembers.members.any((p) => p.id == testPlayer4.id), isTrue);
});
});
group('DELETE', () {
test('removePlayerFromGroup() works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
final removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
expect(removed, isTrue);
final result = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(result.members.length, testGroup.members.length - 1);
final playerExists = result.members.any((p) => p.id == testPlayer1.id);
expect(playerExists, isFalse);
});
});
// Verifies that getPlayersOfGroup returns empty list for non-existent group.
test(
'getPlayersOfGroup returns empty list for non-existent group',
() async {
final players = await database.playerGroupDao.getPlayersOfGroup(
groupId: 'non-existent-group-id',
);
expect(players, isEmpty);
},
);
// Verifies that removing all players from a group leaves the group empty.
test('Removing all players from a group leaves group empty', () async {
await database.groupDao.addGroup(group: testGroup);
@@ -240,137 +213,53 @@ void main() {
);
expect(players, isEmpty);
// Group should still exist
final groupExists = await database.groupDao.groupExists(
groupId: testGroup.id,
);
expect(groupExists, true);
expect(groupExists, isTrue);
});
// Verifies that a player can be in multiple groups.
test('Player can be in multiple groups', () async {
final secondGroup = Group(
name: 'Second Group',
description: '',
members: [],
);
test('removePlayerFromGroup() works correctly', () async {
await database.groupDao.addGroup(group: testGroup);
await database.groupDao.addGroup(group: secondGroup);
// Add testPlayer1 to second group (already in testGroup)
await database.playerGroupDao.addPlayerToGroup(
player: testPlayer1,
groupId: secondGroup.id,
);
final inFirstGroup = await database.playerGroupDao.isPlayerInGroup(
var removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
final inSecondGroup = await database.playerGroupDao.isPlayerInGroup(
expect(removed, isTrue);
removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: secondGroup.id,
);
expect(inFirstGroup, true);
expect(inSecondGroup, true);
});
// Verifies that removing player from one group doesn't affect other groups.
test(
'Removing player from one group does not affect other groups',
() async {
final secondGroup = Group(
name: 'Second Group',
description: '',
members: [testPlayer1],
);
await database.groupDao.addGroup(group: testGroup);
await database.groupDao.addGroup(group: secondGroup);
// Remove testPlayer1 from testGroup
await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
final inFirstGroup = await database.playerGroupDao.isPlayerInGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
final inSecondGroup = await database.playerGroupDao.isPlayerInGroup(
playerId: testPlayer1.id,
groupId: secondGroup.id,
);
expect(inFirstGroup, false);
expect(inSecondGroup, true);
},
);
// Verifies that addPlayerToGroup returns true on successful addition.
test('addPlayerToGroup returns true on successful addition', () async {
await database.groupDao.addGroup(group: testGroup);
await database.playerDao.addPlayer(player: testPlayer4);
final result = await database.playerGroupDao.addPlayerToGroup(
player: testPlayer4,
groupId: testGroup.id,
);
expect(result, true);
expect(removed, isFalse);
});
// Verifies that removing the same player twice returns false on second attempt.
test(
'Removing same player twice returns false on second attempt',
'removePlayerFromGroup() returns false for non-existent player or group',
() async {
await database.groupDao.addGroup(group: testGroup);
final firstRemoval = await database.playerGroupDao
.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
expect(firstRemoval, true);
await database.groupDao.addGroup(group: testGroup);
final secondRemoval = await database.playerGroupDao
.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: testGroup.id,
);
expect(secondRemoval, false);
var removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: 'non-existent-player-id',
groupId: testGroup.id,
);
expect(removed, isFalse);
removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: testPlayer1.id,
groupId: 'non-existent-group-id',
);
expect(removed, isFalse);
removed = await database.playerGroupDao.removePlayerFromGroup(
playerId: 'non-existent-player-id',
groupId: 'non-existent-group-id',
);
expect(removed, isFalse);
},
);
// Verifies that replaceGroupPlayers removes all existing players and replaces with new list.
test('replaceGroupPlayers replaces all group members correctly', () async {
// Create initial group with 3 players
await database.groupDao.addGroup(group: testGroup);
// Verify initial members
var groupMembers = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(groupMembers.members.length, 3);
// Replace with new list containing 2 different players
final newPlayersList = [testPlayer3, testPlayer4];
await database.groupDao.replaceGroupPlayers(
groupId: testGroup.id,
newPlayers: newPlayersList,
);
// Get updated group and verify members
groupMembers = await database.groupDao.getGroupById(
groupId: testGroup.id,
);
expect(groupMembers.members.length, 2);
expect(groupMembers.members.any((p) => p.id == testPlayer3.id), true);
expect(groupMembers.members.any((p) => p.id == testPlayer4.id), true);
expect(groupMembers.members.any((p) => p.id == testPlayer1.id), false);
expect(groupMembers.members.any((p) => p.id == testPlayer2.id), false);
});
});
}

View File

@@ -5,7 +5,6 @@ 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/group.dart';
import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/team.dart';
@@ -17,11 +16,8 @@ void main() {
late Player testPlayer3;
late Player testPlayer4;
late Player testPlayer5;
late Player testPlayer6;
late Group testGroup;
late Game testGame;
late Match testMatchOnlyGroup;
late Match testMatchOnlyPlayers;
late Match testMatch1;
late Team testTeam1;
late Team testTeam2;
final fixedDate = DateTime(2025, 11, 19, 00, 11, 23);
@@ -42,12 +38,6 @@ void main() {
testPlayer3 = Player(name: 'Charlie');
testPlayer4 = Player(name: 'Diana');
testPlayer5 = Player(name: 'Eve');
testPlayer6 = Player(name: 'Frank');
testGroup = Group(
name: 'Test Group',
description: '',
members: [testPlayer1, testPlayer2, testPlayer3],
);
testGame = Game(
name: 'Test Game',
ruleset: Ruleset.singleWinner,
@@ -55,31 +45,17 @@ void main() {
color: GameColor.blue,
icon: '',
);
testMatchOnlyGroup = Match(
name: 'Test Match with Group',
game: testGame,
players: testGroup.members,
group: testGroup,
);
testMatchOnlyPlayers = Match(
testMatch1 = Match(
name: 'Test Match with Players',
game: testGame,
players: [testPlayer4, testPlayer5, testPlayer6],
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
testTeam1 = Team(name: 'Team Alpha', members: [testPlayer1, testPlayer2]);
testTeam2 = Team(name: 'Team Beta', members: [testPlayer3, testPlayer4]);
});
await database.playerDao.addPlayersAsList(
players: [
testPlayer1,
testPlayer2,
testPlayer3,
testPlayer4,
testPlayer5,
testPlayer6,
],
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
);
await database.groupDao.addGroup(group: testGroup);
await database.gameDao.addGame(game: testGame);
});
tearDown(() async {
@@ -87,603 +63,361 @@ void main() {
});
group('Player-Match Tests', () {
test('Match has player works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.playerDao.addPlayer(player: testPlayer1);
group('CREATE', () {
test('addPlayerToMatch() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.playerDao.addPlayer(player: testPlayer1);
var matchHasPlayers = await database.playerMatchDao.matchHasPlayers(
matchId: testMatchOnlyGroup.id,
);
expect(matchHasPlayers, true);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
);
matchHasPlayers = await database.playerMatchDao.matchHasPlayers(
matchId: testMatchOnlyGroup.id,
);
expect(matchHasPlayers, true);
});
test('Adding a player to a match works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.playerDao.addPlayer(player: testPlayer5);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer5.id,
);
var playerAdded = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer5.id,
);
expect(playerAdded, true);
playerAdded = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatchOnlyGroup.id,
playerId: '',
);
expect(playerAdded, false);
});
test('Removing player from match works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
final playerToRemove = testMatchOnlyPlayers.players[0];
final removed = await database.playerMatchDao.removePlayerFromMatch(
playerId: playerToRemove.id,
matchId: testMatchOnlyPlayers.id,
);
expect(removed, true);
final result = await database.matchDao.getMatchById(
matchId: testMatchOnlyPlayers.id,
);
expect(result.players.length, testMatchOnlyPlayers.players.length - 1);
final playerExists = result.players.any((p) => p.id == playerToRemove.id);
expect(playerExists, false);
});
test('Retrieving players of a match works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
final players =
await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
) ??
[];
for (int i = 0; i < players.length; i++) {
expect(players[i].id, testMatchOnlyPlayers.players[i].id);
expect(players[i].name, testMatchOnlyPlayers.players[i].name);
expect(players[i].createdAt, testMatchOnlyPlayers.players[i].createdAt);
}
});
test('Updating the match players works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
final newPlayers = [testPlayer1, testPlayer2, testPlayer4];
await database.playerDao.addPlayersAsList(players: newPlayers);
// First, remove all existing players
final existingPlayers = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
);
if (existingPlayers == null || existingPlayers.isEmpty) {
fail('Existing players should not be null or empty');
}
await database.playerMatchDao.updatePlayersFromMatch(
matchId: testMatchOnlyPlayers.id,
newPlayer: newPlayers,
);
final updatedPlayers = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
);
if (updatedPlayers == null) {
fail('Updated players should not be null');
}
expect(updatedPlayers.length, newPlayers.length);
/// Create a map of new players for easy lookup
final testPlayers = {for (var p in newPlayers) p.id: p};
/// Verify each updated player matches the new players
for (final player in updatedPlayers) {
final testPlayer = testPlayers[player.id]!;
expect(player.id, testPlayer.id);
expect(player.name, testPlayer.name);
expect(player.createdAt, testPlayer.createdAt);
}
});
test(
'Adding the same player to separate matches works correctly',
() async {
final playersList = [testPlayer1, testPlayer2, testPlayer3];
final match1 = Match(
name: 'Match 1',
game: testGame,
players: playersList,
notes: '',
var added = await database.playerMatchDao.addPlayerToMatch(
matchId: testMatch1.id,
playerId: testPlayer1.id,
);
final match2 = Match(
name: 'Match 2',
game: testGame,
players: playersList,
notes: '',
expect(added, isTrue);
added = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatch1.id,
playerId: testPlayer1.id,
);
expect(added, isTrue);
});
await Future.wait([
database.matchDao.addMatch(match: match1),
database.matchDao.addMatch(match: match2),
]);
test('addPlayerToMatch() with team works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.teamDao.addTeam(team: testTeam1, matchId: testMatch1.id);
final players1 = await database.playerMatchDao.getPlayersOfMatch(
matchId: match1.id,
);
final players2 = await database.playerMatchDao.getPlayersOfMatch(
matchId: match2.id,
);
expect(players1, isNotNull);
expect(players2, isNotNull);
expect(
players1!.map((p) => p.id).toList(),
equals(players2!.map((p) => p.id).toList()),
);
expect(
players1.map((p) => p.name).toList(),
equals(players2.map((p) => p.name).toList()),
);
expect(
players1.map((p) => p.createdAt).toList(),
equals(players2.map((p) => p.createdAt).toList()),
);
},
);
// Verifies that getPlayersOfMatch returns null for a non-existent match.
test('getPlayersOfMatch returns null for non-existent match', () async {
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: 'non-existent-match-id',
);
expect(players, isNull);
});
test('Adding player with teamId works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
final playersInTeam = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
);
expect(playersInTeam.length, 1);
expect(playersInTeam[0].id, testPlayer1.id);
});
test('updatePlayerTeam updates team correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
await database.teamDao.addTeam(team: testTeam2);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
// Update player's team
final updated = await database.playerMatchDao.updatePlayerTeam(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam2.id,
);
expect(updated, true);
// Verify player is now in testTeam2
final playersInTeam2 = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam2.id,
);
expect(playersInTeam2.length, 1);
expect(playersInTeam2[0].id, testPlayer1.id);
// Verify player is no longer in testTeam1
final playersInTeam1 = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
);
expect(playersInTeam1.isEmpty, true);
});
test('updatePlayerTeam can remove player from team', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
// Remove player from team by setting teamId to null
final updated = await database.playerMatchDao.updatePlayerTeam(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: null,
);
expect(updated, true);
final playersInTeam = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
);
expect(playersInTeam.isEmpty, true);
});
test(
'updatePlayerTeam returns false for non-existent player-match',
() async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
final updated = await database.playerMatchDao.updatePlayerTeam(
matchId: testMatchOnlyGroup.id,
playerId: 'non-existent-player-id',
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatch1.id,
playerId: testPlayer3.id,
teamId: testTeam1.id,
);
expect(updated, false);
},
);
final playersInTeam = await database.playerMatchDao
.getPlayersOfTeamInMatch(
matchId: testMatch1.id,
teamId: testTeam1.id,
);
// Verifies that getPlayersInTeam returns empty list for non-existent team.
test('getPlayersInTeam returns empty list for non-existent team', () async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
expect(playersInTeam, isNotEmpty);
expect(playersInTeam.length, 3);
});
final players = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyPlayers.id,
teamId: 'non-existent-team-id',
);
test('addPlayerToMatch() ignores duplicates', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.playerDao.addPlayer(player: testPlayer5);
expect(players.isEmpty, true);
final isInMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatch1.id,
playerId: testPlayer5.id,
);
expect(isInMatch, isFalse);
var players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(players.length, testMatch1.players.length);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatch1.id,
playerId: testPlayer5.id,
);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatch1.id,
playerId: testPlayer5.id,
);
players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(players.length, testMatch1.players.length + 1);
});
});
test('getPlayersInTeam returns all players of a team', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
group('READ', () {
test('hasMatchPlayers() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
var matchHasPlayers = await database.playerMatchDao.hasMatchPlayers(
matchId: testMatch1.id,
);
expect(matchHasPlayers, isTrue);
});
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer2.id,
teamId: testTeam1.id,
test('hasMatchPlayers() returns false for non-existent match', () async {
final hasPlayers = await database.playerMatchDao.hasMatchPlayers(
matchId: 'non-existent-match-id',
);
expect(hasPlayers, isFalse);
});
test('isPlayerInMatch() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
final isInMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatch1.id,
playerId: testPlayer1.id,
);
expect(isInMatch, isTrue);
});
test('isPlayerInMatch() returns false for non-existent match', () async {
final isInMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: 'non-existent-match-id',
playerId: testPlayer1.id,
);
expect(isInMatch, isFalse);
});
test('getPlayersOfMatch() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(players, isNotEmpty);
for (int i = 0; i < players.length; i++) {
expect(players[i].id, testMatch1.players[i].id);
expect(players[i].name, testMatch1.players[i].name);
expect(players[i].createdAt, testMatch1.players[i].createdAt);
}
});
test(
'getPlayersOfMatch() returns empty list for non-existent match',
() async {
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: 'non-existent-match-id',
);
expect(players, isEmpty);
},
);
final playersInTeam = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
test('getPlayersInTeam() works correctly', () async {
// Create a match with teams
final matchWithTeams = Match(
name: 'Match with teams',
game: testGame,
players: [],
teams: [testTeam1, testTeam2],
);
await database.matchDao.addMatch(match: matchWithTeams);
var playersInTeam = await database.playerMatchDao
.getPlayersOfTeamInMatch(
matchId: matchWithTeams.id,
teamId: testTeam1.id,
);
expect(playersInTeam, isNotEmpty);
expect(playersInTeam.length, 2);
var playerIds = playersInTeam.map((p) => p.id).toSet();
expect(playerIds.contains(testPlayer1.id), isTrue);
expect(playerIds.contains(testPlayer2.id), isTrue);
playersInTeam = await database.playerMatchDao.getPlayersOfTeamInMatch(
matchId: matchWithTeams.id,
teamId: testTeam2.id,
);
expect(playersInTeam, isNotEmpty);
expect(playersInTeam.length, 2);
playerIds = playersInTeam.map((p) => p.id).toSet();
expect(playerIds.contains(testPlayer3.id), isTrue);
expect(playerIds.contains(testPlayer4.id), isTrue);
});
test(
'getPlayersInTeam() returns empty list for non-existent match',
() async {
final players = await database.playerMatchDao.getPlayersOfTeamInMatch(
matchId: 'non-existent-match-id',
teamId: testTeam1.id,
);
expect(players, isEmpty);
},
);
expect(playersInTeam.length, 2);
final playerIds = playersInTeam.map((p) => p.id).toSet();
expect(playerIds.contains(testPlayer1.id), true);
expect(playerIds.contains(testPlayer2.id), true);
});
test(
'removePlayerFromMatch returns false for non-existent player',
() async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
group('UPDATE', () {
test('updateMatchPlayers() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
final newPlayers = [testPlayer1, testPlayer2, testPlayer4];
await database.playerDao.addPlayersAsList(players: newPlayers);
final existingPlayers = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(existingPlayers, isNotEmpty);
await database.playerMatchDao.updateMatchPlayers(
matchId: testMatch1.id,
player: newPlayers,
);
final updatedPlayers = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(updatedPlayers, isNotEmpty);
expect(updatedPlayers.length, newPlayers.length);
/// Create a map of new players for easy lookup
final testPlayers = {for (var p in newPlayers) p.id: p};
for (final player in updatedPlayers) {
final testPlayer = testPlayers[player.id]!;
expect(player.id, testPlayer.id);
expect(player.name, testPlayer.name);
expect(player.createdAt, testPlayer.createdAt);
}
});
test('updatePlayersTeam() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
await database.teamDao.addTeam(team: testTeam1, matchId: testMatch1.id);
await database.teamDao.addTeam(team: testTeam2, matchId: testMatch1.id);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatch1.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
final updated = await database.playerMatchDao.updatePlayersTeam(
matchId: testMatch1.id,
playerId: testPlayer1.id,
teamId: testTeam2.id,
);
expect(updated, isTrue);
final playersInTeam2 = await database.playerMatchDao
.getPlayersOfTeamInMatch(
matchId: testMatch1.id,
teamId: testTeam2.id,
);
expect(playersInTeam2, isNotEmpty);
expect(playersInTeam2.length, 3);
final playersInTeam1 = await database.playerMatchDao
.getPlayersOfTeamInMatch(
matchId: testMatch1.id,
teamId: testTeam1.id,
);
expect(playersInTeam1, isNotEmpty);
expect(playersInTeam1.length, 1);
expect(playersInTeam1[0].id, testPlayer2.id);
});
test(
'updatePlayersTeam() returns false for non-existent player-match',
() async {
await database.matchDao.addMatch(match: testMatch1);
final updated = await database.playerMatchDao.updatePlayersTeam(
matchId: testMatch1.id,
playerId: 'non-existent-player-id',
teamId: testTeam1.id,
);
expect(updated, isFalse);
},
);
test('updateMatchPlayers() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
var matchPlayers = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(matchPlayers.players.length, testMatch1.players.length);
final newPlayersList = [testPlayer1, testPlayer2];
await database.playerMatchDao.updateMatchPlayers(
matchId: testMatch1.id,
player: newPlayersList,
);
matchPlayers = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(matchPlayers.players.length, 2);
expect(matchPlayers.players.any((p) => p.id == testPlayer1.id), isTrue);
expect(matchPlayers.players.any((p) => p.id == testPlayer2.id), isTrue);
});
test('updateMatchPlayers() with same players makes is ignored', () async {
await database.matchDao.addMatch(match: testMatch1);
final originalPlayers = testMatch1.players;
final updated = await database.playerMatchDao.updateMatchPlayers(
matchId: testMatch1.id,
player: originalPlayers,
);
expect(updated, isFalse);
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatch1.id,
);
expect(players.length, originalPlayers.length);
final playerIds = players.map((p) => p.id).toSet();
for (final originalPlayer in originalPlayers) {
expect(playerIds.contains(originalPlayer.id), isTrue);
}
});
test('updateMatchPlayers() with empty list returns false', () async {
await database.matchDao.addMatch(match: testMatch1);
final updated = await database.playerMatchDao.updateMatchPlayers(
matchId: testMatch1.id,
player: [],
);
expect(updated, isFalse);
});
});
group('DELETE', () {
test('removePlayerFromMatch() works correctly', () async {
await database.matchDao.addMatch(match: testMatch1);
final playerToRemove = testMatch1.players[0];
final removed = await database.playerMatchDao.removePlayerFromMatch(
playerId: 'non-existent-player-id',
matchId: testMatchOnlyPlayers.id,
playerId: playerToRemove.id,
matchId: testMatch1.id,
);
expect(removed, isTrue);
expect(removed, false);
},
);
test('Adding same player twice to same match is ignored', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
);
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyGroup.id,
);
expect(players?.length, 3);
});
test(
'updatePlayersFromMatch with empty list removes all players',
() async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
// Verify players exist initially
var players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
final result = await database.matchDao.getMatchById(
matchId: testMatch1.id,
);
expect(players?.length, 3);
expect(result.players.length, testMatch1.players.length - 1);
// Update with empty list
await database.playerMatchDao.updatePlayersFromMatch(
matchId: testMatchOnlyPlayers.id,
newPlayer: [],
final playerExists = result.players.any(
(p) => p.id == playerToRemove.id,
);
expect(playerExists, isFalse);
});
// Verify all players are removed
players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
);
expect(players, isNull);
},
);
test(
'removePlayerFromMatch() returns false for non-existent player',
() async {
await database.matchDao.addMatch(match: testMatch1);
test('updatePlayersFromMatch with same players makes no changes', () async {
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
final removed = await database.playerMatchDao.removePlayerFromMatch(
playerId: 'non-existent-player-id',
matchId: testMatch1.id,
);
final originalPlayers = [testPlayer4, testPlayer5, testPlayer6];
await database.playerMatchDao.updatePlayersFromMatch(
matchId: testMatchOnlyPlayers.id,
newPlayer: originalPlayers,
expect(removed, isFalse);
},
);
final players = await database.playerMatchDao.getPlayersOfMatch(
matchId: testMatchOnlyPlayers.id,
);
expect(players?.length, originalPlayers.length);
final playerIds = players!.map((p) => p.id).toSet();
for (final originalPlayer in originalPlayers) {
expect(playerIds.contains(originalPlayer.id), true);
}
});
test('matchHasPlayers returns false for non-existent match', () async {
final hasPlayers = await database.playerMatchDao.matchHasPlayers(
matchId: 'non-existent-match-id',
);
expect(hasPlayers, false);
});
test('isPlayerInMatch returns false for non-existent match', () async {
final isInMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: 'non-existent-match-id',
playerId: testPlayer1.id,
);
expect(isInMatch, false);
});
// Verifies that getPlayersInTeam returns empty list for non-existent match.
test(
'getPlayersInTeam returns empty list for non-existent match',
() async {
await database.teamDao.addTeam(team: testTeam1);
final players = await database.playerMatchDao.getPlayersInTeam(
matchId: 'non-existent-match-id',
teamId: testTeam1.id,
);
expect(players.isEmpty, true);
},
);
// Verifies that players in different teams within the same match are returned correctly.
test('Players in different teams within same match are separate', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
await database.teamDao.addTeam(team: testTeam2);
// Add players to different teams
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
teamId: testTeam1.id,
);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer2.id,
teamId: testTeam1.id,
);
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer3.id,
teamId: testTeam2.id,
);
// Verify team 1 players
final playersInTeam1 = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
);
expect(playersInTeam1.length, 2);
final team1Ids = playersInTeam1.map((p) => p.id).toSet();
expect(team1Ids.contains(testPlayer1.id), true);
expect(team1Ids.contains(testPlayer2.id), true);
expect(team1Ids.contains(testPlayer3.id), false);
// Verify team 2 players
final playersInTeam2 = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam2.id,
);
expect(playersInTeam2.length, 1);
expect(playersInTeam2[0].id, testPlayer3.id);
});
// Verifies that removePlayerFromMatch does not affect other matches.
test('removePlayerFromMatch does not affect other matches', () async {
final playersList = [testPlayer1, testPlayer2];
final match1 = Match(
name: 'Match 1',
game: testGame,
players: playersList,
);
final match2 = Match(
name: 'Match 2',
game: testGame,
players: playersList,
);
await Future.wait([
database.matchDao.addMatch(match: match1),
database.matchDao.addMatch(match: match2),
]);
// Remove player from match1
final removed = await database.playerMatchDao.removePlayerFromMatch(
playerId: testPlayer1.id,
matchId: match1.id,
);
expect(removed, true);
// Verify player is removed from match1
final isInMatch1 = await database.playerMatchDao.isPlayerInMatch(
matchId: match1.id,
playerId: testPlayer1.id,
);
expect(isInMatch1, false);
// Verify player still exists in match2
final isInMatch2 = await database.playerMatchDao.isPlayerInMatch(
matchId: match2.id,
playerId: testPlayer1.id,
);
expect(isInMatch2, true);
});
// Verifies that updatePlayersFromMatch on non-existent match fails with constraint error.
test(
'updatePlayersFromMatch on non-existent match fails with foreign key constraint',
() async {
// Should throw due to foreign key constraint - match doesn't exist
await expectLater(
database.playerMatchDao.updatePlayersFromMatch(
matchId: 'non-existent-match-id',
newPlayer: [testPlayer1, testPlayer2],
),
throwsA(anything),
);
},
);
// Verifies that a player can be in a match without being assigned to a team.
test('Player can exist in match without team assignment', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1);
// Add player to match without team
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
);
// Add another player to match with team
await database.playerMatchDao.addPlayerToMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer2.id,
teamId: testTeam1.id,
);
// Verify both players are in the match
final isPlayer1InMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer1.id,
);
final isPlayer2InMatch = await database.playerMatchDao.isPlayerInMatch(
matchId: testMatchOnlyGroup.id,
playerId: testPlayer2.id,
);
expect(isPlayer1InMatch, true);
expect(isPlayer2InMatch, true);
// Verify only player2 is in the team
final playersInTeam = await database.playerMatchDao.getPlayersInTeam(
matchId: testMatchOnlyGroup.id,
teamId: testTeam1.id,
);
expect(playersInTeam.length, 1);
expect(playersInTeam[0].id, testPlayer2.id);
});
// Verifies that replaceMatchPlayers removes all existing players and replaces with new list.
test('replaceMatchPlayers replaces all match players correctly', () async {
// Create initial match with 3 players
await database.matchDao.addMatch(match: testMatchOnlyPlayers);
// Verify initial players
var matchPlayers = await database.matchDao.getMatchById(
matchId: testMatchOnlyPlayers.id,
);
expect(matchPlayers.players.length, 3);
// Replace with new list containing 2 different players
final newPlayersList = [testPlayer1, testPlayer2];
await database.matchDao.replaceMatchPlayers(
matchId: testMatchOnlyPlayers.id,
newPlayers: newPlayersList,
);
// Get updated match and verify players
matchPlayers = await database.matchDao.getMatchById(
matchId: testMatchOnlyPlayers.id,
);
expect(matchPlayers.players.length, 2);
expect(matchPlayers.players.any((p) => p.id == testPlayer1.id), true);
expect(matchPlayers.players.any((p) => p.id == testPlayer2.id), true);
expect(matchPlayers.players.any((p) => p.id == testPlayer4.id), false);
expect(matchPlayers.players.any((p) => p.id == testPlayer5.id), false);
expect(matchPlayers.players.any((p) => p.id == testPlayer6.id), false);
});
});
}

View File

@@ -17,6 +17,9 @@ void main() {
late Game testGame;
late Match testMatch1;
late Match testMatch2;
ScoreEntry entryRound1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entryRound2 = ScoreEntry(roundNumber: 2, score: 25, change: 15);
ScoreEntry entryRound3 = ScoreEntry(roundNumber: 3, score: 30, change: 5);
final fixedDate = DateTime(2025, 11, 19, 00, 11, 23);
final fakeClock = Clock(() => fixedDate);
@@ -65,13 +68,12 @@ void main() {
});
group('Score Tests', () {
group('Adding and Fetching scores', () {
test('Single Score', () async {
ScoreEntry entry = ScoreEntry(roundNumber: 1, score: 10, change: 10);
group('CREATE', () {
test('Adding and fetching single score works correctly', () async {
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entry,
entry: entryRound1,
);
final score = await database.scoreEntryDao.getScore(
@@ -81,41 +83,37 @@ void main() {
);
expect(score, isNotNull);
expect(score!.roundNumber, 1);
expect(score.score, 10);
expect(score.change, 10);
expect(score!.roundNumber, entryRound1.roundNumber);
expect(score.score, entryRound1.score);
expect(score.change, entryRound1.change);
});
test('Multiple Scores', () async {
final entryList = [
ScoreEntry(roundNumber: 1, score: 5, change: 5),
ScoreEntry(roundNumber: 2, score: 12, change: 7),
ScoreEntry(roundNumber: 3, score: 18, change: 6),
];
test('Adding and fetching single score works correctly', () async {
await database.scoreEntryDao.addScoresAsList(
entrys: entryList,
entrys: [entryRound1, entryRound2, entryRound3],
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
final scores = await database.scoreEntryDao.getAllPlayerScoresInMatch(
final entrys = await database.scoreEntryDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(scores, isNotNull);
expect(entrys, isNotEmpty);
// 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);
// Map for connecting fetched entry with expected entrys
final testScores = {1: entryRound1, 2: entryRound2, 3: entryRound3};
for (final entry in entrys) {
final testEntry = testScores[entry.roundNumber]!;
expect(entry.roundNumber, testEntry.roundNumber);
expect(entry.score, testEntry.score);
expect(entry.change, testEntry.change);
}
});
});
group('Undesirable values', () {
test('Score & Round can have negative values', () async {
ScoreEntry entry = ScoreEntry(roundNumber: -2, score: -10, change: -10);
await database.scoreEntryDao.addScore(
@@ -155,6 +153,31 @@ void main() {
expect(score.change, 0);
});
test('Adding the same score twice replaces the existing one', () async {
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entryRound1,
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entryRound1,
);
final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(score, isNotNull);
expect(score!.score, entryRound1.score);
expect(score.change, entryRound1.change);
});
});
group('READ', () {
test('Getting score for a non-existent entities returns null', () async {
var score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id,
@@ -201,10 +224,8 @@ void main() {
expect(score, isNull);
});
});
group('Scores in matches', () {
test('getAllMatchScores()', () async {
test('getAllMatchScores() works correctly', () async {
ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 1, score: 20, change: 20);
ScoreEntry entry3 = ScoreEntry(roundNumber: 2, score: 25, change: 15);
@@ -238,10 +259,10 @@ void main() {
matchId: testMatch1.id,
);
expect(scores.isEmpty, true);
expect(scores.isEmpty, isTrue);
});
test('getAllPlayerScoresInMatch()', () async {
test('getAllPlayerScoresInMatch() works correctly', () async {
ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 2, score: 25, change: 15);
ScoreEntry entry3 = ScoreEntry(roundNumber: 1, score: 30, change: 30);
@@ -278,7 +299,7 @@ void main() {
matchId: testMatch1.id,
);
expect(playerScores.isEmpty, true);
expect(playerScores.isEmpty, isTrue);
});
test('Scores are isolated across different matches', () async {
@@ -315,31 +336,109 @@ void main() {
expect(match2Scores[0].score, 50);
expect(match2Scores[0].change, 50);
});
test('getLatestRoundNumber() works correctly', () async {
var latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, isNull);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entryRound1,
);
latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 1);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entryRound2,
);
latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 2);
});
test('getLatestRoundNumber() with non-consecutive rounds', () async {
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entrys: [entryRound1, entryRound3],
);
final latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 3);
});
test('getTotalScoreForPlayer() works correctly', () async {
var totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(totalScore, 0);
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entrys: [entryRound1, entryRound2, entryRound3],
);
totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
final expectedTotal =
entryRound1.change + entryRound2.change + entryRound3.change;
expect(totalScore, expectedTotal);
});
test('getTotalScoreForPlayer() ignores round score', () async {
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entrys: [
ScoreEntry(roundNumber: 2, score: 25, change: 25),
ScoreEntry(roundNumber: 1, score: 25, change: 10),
ScoreEntry(roundNumber: 3, score: 25, change: 25),
],
);
final totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
// Should return the sum of all changes
expect(totalScore, 60);
});
});
group('Updating scores', () {
test('updateScore()', () async {
ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 2, score: 15, change: 5);
await database.scoreEntryDao.addScore(
group('UPDATE', () {
test('updateScore() works correctly', () async {
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entry1,
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: entry2,
entrys: [entryRound1, entryRound2],
);
final newEntry = ScoreEntry(roundNumber: 2, score: 50, change: 40);
final updated = await database.scoreEntryDao.updateScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
newEntry: ScoreEntry(roundNumber: 2, score: 50, change: 40),
entry: newEntry,
);
expect(updated, true);
expect(updated, isTrue);
final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id,
@@ -348,23 +447,23 @@ void main() {
);
expect(score, isNotNull);
expect(score!.score, 50);
expect(score.change, 40);
expect(score!.score, newEntry.score);
expect(score.change, newEntry.change);
});
test('Updating a non-existent score returns false', () async {
final updated = await database.scoreEntryDao.updateScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
newEntry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
entry: entryRound1,
);
expect(updated, false);
expect(updated, isFalse);
});
});
group('Deleting scores', () {
test('deleteScore() ', () async {
group('DELETE', () {
test('deleteScore() works correctly', () async {
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
@@ -377,7 +476,7 @@ void main() {
roundNumber: 1,
);
expect(deleted, true);
expect(deleted, isTrue);
final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id,
@@ -395,31 +494,36 @@ void main() {
roundNumber: 1,
);
expect(deleted, false);
expect(deleted, isFalse);
});
test('deleteAllScoresForMatch() works correctly', () async {
final score1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
entry: score1,
);
final score2 = ScoreEntry(roundNumber: 1, score: 20, change: 20);
await database.scoreEntryDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
entry: score2,
);
final score3 = ScoreEntry(roundNumber: 1, score: 15, change: 15);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch2.id,
entry: ScoreEntry(roundNumber: 1, score: 15, change: 15),
entry: score3,
);
final deleted = await database.scoreEntryDao.deleteAllScoresForMatch(
matchId: testMatch1.id,
);
expect(deleted, true);
expect(deleted, isTrue);
final match1Scores = await database.scoreEntryDao.getAllMatchScores(
matchId: testMatch1.id,
@@ -433,22 +537,16 @@ void main() {
});
test('deleteAllScoresForPlayerInMatch() works correctly', () async {
await database.scoreEntryDao.addScore(
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 2, score: 15, change: 5),
entrys: [entryRound1, entryRound2],
);
await database.scoreEntryDao.addScore(
playerId: testPlayer2.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 6, change: 6),
entry: entryRound1,
);
final deleted = await database.scoreEntryDao
@@ -457,7 +555,7 @@ void main() {
matchId: testMatch1.id,
);
expect(deleted, true);
expect(deleted, isTrue);
final player1Scores = await database.scoreEntryDao
.getAllPlayerScoresInMatch(
@@ -475,146 +573,12 @@ void main() {
});
});
group('Score Aggregations & Edge Cases', () {
test('getLatestRoundNumber()', () async {
var latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, isNull);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
);
latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 1);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 5, score: 50, change: 40),
);
latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 5);
});
test('getLatestRoundNumber() with non-consecutive rounds', () async {
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 5, score: 50, change: 40),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 3, score: 30, change: 20),
);
final latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id,
);
expect(latestRound, 5);
});
test('getTotalScoreForPlayer()', () async {
var totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(totalScore, 0);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 2, score: 25, change: 15),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 3, score: 40, change: 15),
);
totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
expect(totalScore, 40);
});
test('getTotalScoreForPlayer() ignores round score', () async {
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 2, score: 25, change: 25),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 25, change: 10),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 3, score: 25, change: 25),
);
final totalScore = await database.scoreEntryDao.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.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
entry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
);
final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 1,
);
expect(score, isNotNull);
expect(score!.score, 20);
expect(score.change, 20);
});
});
group('Handling Winner', () {
group('WINNER', () {
test('hasWinner() works correctly', () async {
var hasWinner = await database.scoreEntryDao.hasWinner(
matchId: testMatch1.id,
);
expect(hasWinner, false);
expect(hasWinner, isFalse);
await database.scoreEntryDao.setWinner(
playerId: testPlayer1.id,
@@ -624,7 +588,7 @@ void main() {
hasWinner = await database.scoreEntryDao.hasWinner(
matchId: testMatch1.id,
);
expect(hasWinner, true);
expect(hasWinner, isTrue);
});
test('getWinnersForMatch() returns correct winner', () async {
@@ -648,7 +612,7 @@ void main() {
var removed = await database.scoreEntryDao.removeWinner(
matchId: testMatch1.id,
);
expect(removed, false);
expect(removed, isFalse);
await database.scoreEntryDao.setWinner(
playerId: testPlayer1.id,
@@ -658,7 +622,7 @@ void main() {
removed = await database.scoreEntryDao.removeWinner(
matchId: testMatch1.id,
);
expect(removed, true);
expect(removed, isTrue);
var winner = await database.scoreEntryDao.getWinner(
matchId: testMatch1.id,
@@ -667,58 +631,58 @@ void main() {
});
});
group('Handling Looser', () {
test('hasLooser() works correctly', () async {
var hasLooser = await database.scoreEntryDao.hasLooser(
group('LOSER', () {
test('hasLoser() works correctly', () async {
var hasLooser = await database.scoreEntryDao.hasLoser(
matchId: testMatch1.id,
);
expect(hasLooser, false);
expect(hasLooser, isFalse);
await database.scoreEntryDao.setLooser(
await database.scoreEntryDao.setLoser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
hasLooser = await database.scoreEntryDao.hasLooser(
hasLooser = await database.scoreEntryDao.hasLoser(
matchId: testMatch1.id,
);
expect(hasLooser, true);
expect(hasLooser, isTrue);
});
test('getLooser() returns correct winner', () async {
var looser = await database.scoreEntryDao.getLooser(
test('getLoser() returns correct winner', () async {
var looser = await database.scoreEntryDao.getLoser(
matchId: testMatch1.id,
);
expect(looser, isNull);
await database.scoreEntryDao.setLooser(
await database.scoreEntryDao.setLoser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
looser = await database.scoreEntryDao.getLooser(matchId: testMatch1.id);
looser = await database.scoreEntryDao.getLoser(matchId: testMatch1.id);
expect(looser, isNotNull);
expect(looser!.id, testPlayer1.id);
});
test('removeLooser() works correctly', () async {
var removed = await database.scoreEntryDao.removeLooser(
test('removeLoser() works correctly', () async {
var removed = await database.scoreEntryDao.removeLoser(
matchId: testMatch1.id,
);
expect(removed, false);
expect(removed, isFalse);
await database.scoreEntryDao.setLooser(
await database.scoreEntryDao.setLoser(
playerId: testPlayer1.id,
matchId: testMatch1.id,
);
removed = await database.scoreEntryDao.removeLooser(
removed = await database.scoreEntryDao.removeLoser(
matchId: testMatch1.id,
);
expect(removed, true);
expect(removed, isTrue);
var looser = await database.scoreEntryDao.getLooser(
var looser = await database.scoreEntryDao.getLoser(
matchId: testMatch1.id,
);
expect(looser, isNull);

View File

@@ -99,8 +99,8 @@ void main() {
await database.playerDao.addPlayer(player: testPlayer1);
await database.gameDao.addGame(game: testGame);
await database.groupDao.addGroup(group: testGroup);
await database.teamDao.addTeam(team: testTeam);
await database.matchDao.addMatch(match: testMatch);
await database.teamDao.addTeam(team: testTeam, matchId: testMatch.id);
var playerCount = await database.playerDao.getPlayerCount();
var gameCount = await database.gameDao.getGameCount();
@@ -137,7 +137,9 @@ void main() {
await database.playerDao.addPlayer(player: testPlayer2);
await database.gameDao.addGame(game: testGame);
await database.groupDao.addGroup(group: testGroup);
/*
await database.teamDao.addTeam(team: testTeam);
*/
await database.matchDao.addMatch(match: testMatch);
final ctx = await getContext(tester);
@@ -147,22 +149,19 @@ void main() {
final decoded = json.decode(jsonString) as Map<String, dynamic>;
expect(decoded.containsKey('players'), true);
expect(decoded.containsKey('games'), true);
expect(decoded.containsKey('groups'), true);
expect(decoded.containsKey('teams'), true);
expect(decoded.containsKey('matches'), true);
expect(decoded.containsKey('players'), isTrue);
expect(decoded.containsKey('games'), isTrue);
expect(decoded.containsKey('groups'), isTrue);
expect(decoded.containsKey('matches'), isTrue);
final players = decoded['players'] as List<dynamic>;
final games = decoded['games'] as List<dynamic>;
final groups = decoded['groups'] as List<dynamic>;
final teams = decoded['teams'] as List<dynamic>;
final matches = decoded['matches'] as List<dynamic>;
expect(players.length, 2);
expect(games.length, 1);
expect(groups.length, 1);
expect(teams.length, 1);
expect(matches.length, 1);
});
@@ -175,13 +174,11 @@ void main() {
final players = decoded['players'] as List<dynamic>;
final games = decoded['games'] as List<dynamic>;
final groups = decoded['groups'] as List<dynamic>;
final teams = decoded['teams'] as List<dynamic>;
final matches = decoded['matches'] as List<dynamic>;
expect(players, isEmpty);
expect(games, isEmpty);
expect(groups, isEmpty);
expect(teams, isEmpty);
expect(matches, isEmpty);
});
});
@@ -243,29 +240,6 @@ void main() {
expect(memberIds, containsAll([testPlayer1.id, testPlayer2.id]));
});
testWidgets('Team data is correct', (tester) async {
await database.teamDao.addTeam(team: testTeam);
final ctx = await getContext(tester);
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
final decoded = json.decode(jsonString) as Map<String, dynamic>;
final teams = decoded['teams'] as List<dynamic>;
expect(teams.length, 1);
final teamData = teams[0] as Map<String, dynamic>;
expect(teamData['id'], testTeam.id);
expect(teamData['name'], testTeam.name);
expect(teamData['memberIds'], isA<List>());
// Note: In this system, teams don't have independent members.
// Team members are only tracked through matches via PlayerMatchTable.
// Therefore, memberIds will be empty for standalone teams.
final memberIds = teamData['memberIds'] as List<dynamic>;
expect(memberIds, isEmpty);
});
testWidgets('Match data is correct', (tester) async {
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2],
@@ -317,6 +291,51 @@ void main() {
expect(player2Score.change, 15);
});
testWidgets('Match with teams is handled correctly', (tester) async {
final matchWithTeams = Match(
name: 'Match with Teams',
game: testGame,
players: [testPlayer1, testPlayer2],
teams: [testTeam],
notes: 'Team match',
);
await database.playerDao.addPlayersAsList(
players: [testPlayer1, testPlayer2],
);
await database.gameDao.addGame(game: testGame);
await database.matchDao.addMatch(match: matchWithTeams);
final ctx = await getContext(tester);
final jsonString = await DataTransferService.getAppDataAsJson(ctx);
final decoded = json.decode(jsonString) as Map<String, dynamic>;
final matches = decoded['matches'] as List<dynamic>;
expect(matches.length, 1);
final matchData = matches[0] as Map<String, dynamic>;
expect(matchData['id'], matchWithTeams.id);
expect(matchData['name'], matchWithTeams.name);
expect(
matchData['teams'],
isNotNull,
reason: 'teams should not be null',
);
expect(matchData['teams'], isA<List>());
final teamsInMatch = matchData['teams'] as List<dynamic>;
expect(teamsInMatch.length, 1);
final teamData = teamsInMatch[0] as Map<String, dynamic>;
expect(teamData['id'], testTeam.id);
expect(teamData['name'], testTeam.name);
expect(teamData['memberIds'], isA<List>());
final memberIds = teamData['memberIds'] as List<dynamic>;
expect(memberIds.length, 2);
expect(memberIds, containsAll([testPlayer1.id, testPlayer2.id]));
});
testWidgets('Match without group is handled correctly', (tester) async {
final matchWithoutGroup = Match(
name: 'No Group Match',
@@ -644,19 +663,17 @@ void main() {
test('parseTeamsFromJson()', () {
final playerById = {testPlayer1.id: testPlayer1};
final jsonMap = {
'teams': [
{
'id': testTeam.id,
'name': testTeam.name,
'memberIds': [testPlayer1.id],
'createdAt': testTeam.createdAt.toIso8601String(),
},
],
};
final teamsJson = [
{
'id': testTeam.id,
'name': testTeam.name,
'memberIds': [testPlayer1.id],
'createdAt': testTeam.createdAt.toIso8601String(),
},
];
final teams = DataTransferService.parseTeamsFromJson(
jsonMap,
teamsJson,
playerById,
);
@@ -668,15 +685,21 @@ void main() {
});
test('parseTeamsFromJson() empty list', () {
final jsonMap = {'teams': []};
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
final teams = DataTransferService.parseTeamsFromJson([], {});
expect(teams, isEmpty);
});
test('parseTeamsFromJson() missing key', () {
final jsonMap = <String, dynamic>{};
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
expect(teams, isEmpty);
test('parseTeamsFromJson() missing memberIds', () {
final teamsJson = [
{
'id': testTeam.id,
'name': testTeam.name,
'createdAt': testTeam.createdAt.toIso8601String(),
},
];
final teams = DataTransferService.parseTeamsFromJson(teamsJson, {});
expect(teams.length, 1);
expect(teams[0].members, isEmpty);
});
test('parseMatchesFromJson()', () {