import 'package:clock/clock.dart'; import 'package:drift/drift.dart' hide isNotNull, isNull; import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/team.dart'; void main() { late AppDatabase database; late Player testPlayer1; late Player testPlayer2; late Player testPlayer3; late Player testPlayer4; late Player testPlayer5; late Game testGame; late Match testMatch1; late Team testTeam1; late Team testTeam2; final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); final fakeClock = Clock(() => fixedDate); setUp(() async { database = AppDatabase( DatabaseConnection( NativeDatabase.memory(), // Recommended for widget tests to avoid test errors. closeStreamsSynchronously: true, ), ); withClock(fakeClock, () { testPlayer1 = Player(name: 'Alice'); testPlayer2 = Player(name: 'Bob'); testPlayer3 = Player(name: 'Charlie'); testPlayer4 = Player(name: 'Diana'); testPlayer5 = Player(name: 'Eve'); testGame = Game( name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: GameColor.blue, icon: '', ); testMatch1 = Match( name: 'Test Match with Players', game: testGame, 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], ); await database.gameDao.addGame(game: testGame); }); tearDown(() async { await database.close(); }); group('Player-Match Tests', () { group('CREATE', () { test('addPlayerToMatch() works correctly', () async { await database.matchDao.addMatch(match: testMatch1); await database.playerDao.addPlayer(player: testPlayer1); var added = await database.playerMatchDao.addPlayerToMatch( matchId: testMatch1.id, playerId: testPlayer1.id, ); expect(added, isTrue); added = await database.playerMatchDao.isPlayerInMatch( matchId: testMatch1.id, playerId: testPlayer1.id, ); expect(added, isTrue); }); test('addPlayerToMatch() with team works correctly', () async { await database.matchDao.addMatch(match: testMatch1); await database.teamDao.addTeam(team: testTeam1, matchId: testMatch1.id); await database.playerMatchDao.addPlayerToMatch( matchId: testMatch1.id, playerId: testPlayer3.id, teamId: testTeam1.id, ); final playersInTeam = await database.playerMatchDao .getPlayersOfTeamInMatch( matchId: testMatch1.id, teamId: testTeam1.id, ); expect(playersInTeam, isNotEmpty); expect(playersInTeam.length, 3); }); test('addPlayerToMatch() ignores duplicates', () async { await database.matchDao.addMatch(match: testMatch1); await database.playerDao.addPlayer(player: testPlayer5); 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); }); }); 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 { 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); }, ); 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, 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: playerToRemove.id, matchId: testMatch1.id, ); expect(removed, isTrue); final result = await database.matchDao.getMatchById( matchId: testMatch1.id, ); expect(result.players.length, testMatch1.players.length - 1); final playerExists = result.players.any( (p) => p.id == playerToRemove.id, ); expect(playerExists, isFalse); }); test( 'removePlayerFromMatch() returns false for non-existent player', () async { await database.matchDao.addMatch(match: testMatch1); final removed = await database.playerMatchDao.removePlayerFromMatch( playerId: 'non-existent-player-id', matchId: testMatch1.id, ); expect(removed, isFalse); }, ); }); }); }