From cbc8a1afcec9025b7b8c86886f4b578d13f86137 Mon Sep 17 00:00:00 2001 From: Felix Kirchner Date: Fri, 1 May 2026 16:49:33 +0200 Subject: [PATCH] Updated player-match dao and tests --- lib/data/dao/match_dao.dart | 47 +- lib/data/dao/player_match_dao.dart | 134 +-- .../relationships/player_match_test.dart | 928 +++++++----------- 3 files changed, 415 insertions(+), 694 deletions(-) diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index faa0227..11cd5a2 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -270,8 +270,9 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { if (row.groupId != null) { group = await db.groupDao.getGroupById(groupId: row.groupId!); } - final players = - await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? []; + final players = await db.playerMatchDao.getPlayersOfMatch( + matchId: row.id, + ); final scores = await db.scoreEntryDao.getAllMatchScores( matchId: row.id, @@ -307,8 +308,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { group = await db.groupDao.getGroupById(groupId: result.groupId!); } - final players = - await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; + final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); final scores = await db.scoreEntryDao.getAllMatchScores(matchId: matchId); @@ -338,8 +338,9 @@ class MatchDao extends DatabaseAccessor 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, @@ -447,40 +448,6 @@ class MatchDao extends DatabaseAccessor 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 updateMatchPlayers({ - required String matchId, - required List 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(); - - // 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 match - await Future.wait( - newPlayers.map( - (player) => db.playerMatchDao.addPlayerToMatch( - matchId: matchId, - playerId: player.id, - ), - ), - ); - }); - } - /* Delete */ /// Deletes the match with the given [matchId] from the database. diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index b467a1b..1c9d0dd 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -11,14 +11,16 @@ class PlayerMatchDao extends DatabaseAccessor 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 addPlayerToMatch({ + Future 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 ), mode: InsertMode.insertOrReplace, ); - } - - /// Retrieves a list of [Player]s associated with the given [matchId]. - /// Returns null if no players are found. - Future?> 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 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 matchHasPlayers({required String matchId}) async { + Future hasMatchPlayers({required String matchId}) async { final count = await (selectOnly(playerMatchTable) ..where(playerMatchTable.matchId.equals(matchId)) @@ -87,32 +61,80 @@ class PlayerMatchDao extends DatabaseAccessor 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 removePlayerFromMatch({ + /// Retrieves a list of [Player]s associated with the given [matchId]. + /// Returns empty list if no players are found. + Future> 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> 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 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 /// longer associated with the match. - Future updatePlayersFromMatch({ + Future updateMatchPlayers({ required String matchId, required List newPlayer, }) async { + if (newPlayer.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 currentPlayerIds = currentPlayers.map((p) => p.id).toSet(); final newPlayerIdsSet = newPlayer.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); final playersToRemove = currentPlayerIds.difference(newPlayerIdsSet); @@ -147,22 +169,22 @@ class PlayerMatchDao extends DatabaseAccessor ); } }); + return true; } - /// Retrieves all players in a specific team for a match. - Future> 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 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; } } diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart index 92601f0..2cc5185 100644 --- a/test/db_tests/relationships/player_match_test.dart +++ b/test/db_tests/relationships/player_match_test.dart @@ -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,359 @@ 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); - expect(players.isEmpty, true); - }); - - test('getPlayersInTeam returns all players of a 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, - ); - await database.playerMatchDao.addPlayerToMatch( - matchId: testMatchOnlyGroup.id, - playerId: testPlayer2.id, - teamId: testTeam1.id, - ); - - final playersInTeam = await database.playerMatchDao.getPlayersInTeam( - matchId: testMatchOnlyGroup.id, - teamId: testTeam1.id, - ); - 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); - - final removed = await database.playerMatchDao.removePlayerFromMatch( - playerId: 'non-existent-player-id', - matchId: testMatchOnlyPlayers.id, + final isInMatch = await database.playerMatchDao.isPlayerInMatch( + matchId: testMatch1.id, + playerId: testPlayer5.id, ); + expect(isInMatch, isFalse); - 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, + matchId: testMatch1.id, ); - expect(players?.length, 3); + expect(players.length, testMatch1.players.length); - // Update with empty list - await database.playerMatchDao.updatePlayersFromMatch( - matchId: testMatchOnlyPlayers.id, + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatch1.id, + playerId: testPlayer1.id, + ); + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatch1.id, + playerId: testPlayer1.id, + ); + + players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatch1.id, + ); + + expect(players.length, testMatch1.players.length + 1); + }); + }); + + 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); + }); + + 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 { + 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); + }, + ); + + 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); + }, + ); + }); + + 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, + newPlayer: 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, + newPlayer: 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, + newPlayer: 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, newPlayer: [], ); - // Verify all players are removed - players = await database.playerMatchDao.getPlayersOfMatch( - matchId: testMatchOnlyPlayers.id, + 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: playerToRemove.id, + matchId: testMatch1.id, ); - expect(players, isNull); - }, - ); + expect(removed, isTrue); - test('updatePlayersFromMatch with same players makes no changes', () async { - await database.matchDao.addMatch(match: testMatchOnlyPlayers); - - final originalPlayers = [testPlayer4, testPlayer5, testPlayer6]; - - await database.playerMatchDao.updatePlayersFromMatch( - matchId: testMatchOnlyPlayers.id, - newPlayer: originalPlayers, - ); - - 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, + final result = await database.matchDao.getMatchById( + matchId: testMatch1.id, ); + expect(result.players.length, testMatch1.players.length - 1); - 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), + final playerExists = result.players.any( + (p) => p.id == playerToRemove.id, ); - }, - ); + expect(playerExists, isFalse); + }); - // 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); + test( + 'removePlayerFromMatch() returns false for non-existent player', + () async { + await database.matchDao.addMatch(match: testMatch1); - // Add player to match without team - await database.playerMatchDao.addPlayerToMatch( - matchId: testMatchOnlyGroup.id, - playerId: testPlayer1.id, + final removed = await database.playerMatchDao.removePlayerFromMatch( + playerId: 'non-existent-player-id', + matchId: testMatch1.id, + ); + + expect(removed, isFalse); + }, ); - - // 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); }); }); }