import 'package:clock/clock.dart'; import 'package:drift/drift.dart' hide isNull; import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/dto/player.dart'; void main() { late AppDatabase database; late Player testPlayer1; late Player testPlayer2; late Player testPlayer3; late Player testPlayer4; final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); final fakeClock = Clock(() => fixedDate); setUp(() { database = AppDatabase( DatabaseConnection( NativeDatabase.memory(), // Recommended for widget tests to avoid test errors. closeStreamsSynchronously: true, ), ); withClock(fakeClock, () { testPlayer1 = Player(name: 'Test Player', description: ''); testPlayer2 = Player(name: 'Second Player', description: ''); testPlayer3 = Player(name: 'Charlie', description: ''); testPlayer4 = Player(name: 'Diana', description: ''); }); }); tearDown(() async { await database.close(); }); 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); 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 fetchedPlayer2 = allPlayers.firstWhere( (g) => g.id == testPlayer2.id, ); expect(fetchedPlayer2.name, testPlayer2.name); expect(fetchedPlayer2.createdAt, testPlayer2.createdAt); }); // 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()), ); }); // 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: ''); await database.playerDao.addPlayer(player: specialPlayer); final fetchedPlayer = await database.playerDao.getPlayerById( playerId: specialPlayer.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', ); 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: ''); await database.playerDao.addPlayer(player: playerWithoutDescription); final fetchedPlayer = await database.playerDao.getPlayerById( playerId: playerWithoutDescription.id, ); expect(fetchedPlayer.description, ''); }); // 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); await database.playerDao.updatePlayerName( playerId: testPlayer1.id, newName: 'First Update', ); var fetchedPlayer = await database.playerDao.getPlayerById( playerId: testPlayer1.id, ); expect(fetchedPlayer.name, 'First Update'); await database.playerDao.updatePlayerName( playerId: testPlayer1.id, newName: 'Second Update', ); fetchedPlayer = await database.playerDao.getPlayerById( playerId: testPlayer1.id, ); expect(fetchedPlayer.name, 'Second Update'); await database.playerDao.updatePlayerName( playerId: testPlayer1.id, newName: 'Third Update', ); fetchedPlayer = await database.playerDao.getPlayerById( playerId: testPlayer1.id, ); expect(fetchedPlayer.name, 'Third Update'); }); // 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: '', description: ''); 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, description: ''); 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); }); }); }