diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index 94d010c..283c02f 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -15,11 +15,21 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { Future> getAllGames() async { final query = select(gameTable); final result = await query.get(); - return result - .map( - (row) => Game(id: row.id, name: row.name, createdAt: row.createdAt), - ) - .toList(); + + return Future.wait( + result.map((row) async { + final group = await db.groupGameDao.getGroupOfGame(gameId: row.id); + final players = await db.playerGameDao.getPlayersOfGame(gameId: row.id); + return Game( + id: row.id, + name: row.name, + group: group, + players: players, + createdAt: row.createdAt, + winner: row.winnerId, + ); + }), + ); } /// Retrieves a [Game] by its [gameId]. @@ -29,11 +39,11 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { List? players; if (await db.playerGameDao.gameHasPlayers(gameId: gameId)) { - players = await db.playerGameDao.getPlayersByGameId(gameId: gameId); + players = await db.playerGameDao.getPlayersOfGame(gameId: gameId); } Group? group; - if (await db.groupGameDao.hasGameGroup(gameId: gameId)) { - group = await db.groupGameDao.getGroupByGameId(gameId: gameId); + if (await db.groupGameDao.gameHasGroup(gameId: gameId)) { + group = await db.groupGameDao.getGroupOfGame(gameId: gameId); } return Game( diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index 3378948..9b16801 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -16,7 +16,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final result = await query.get(); return Future.wait( result.map((groupData) async { - final members = await db.playerGroupDao.getPlayersOfGroupById( + final members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupData.id, ); return Group( @@ -34,7 +34,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final query = select(groupTable)..where((g) => g.id.equals(groupId)); final result = await query.getSingle(); - List members = await db.playerGroupDao.getPlayersOfGroupById( + List members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupId, ); diff --git a/lib/data/dao/group_game_dao.dart b/lib/data/dao/group_game_dao.dart index d3b30ca..f3ddcc7 100644 --- a/lib/data/dao/group_game_dao.dart +++ b/lib/data/dao/group_game_dao.dart @@ -10,9 +10,33 @@ class GroupGameDao extends DatabaseAccessor with _$GroupGameDaoMixin { GroupGameDao(super.db); + /// Associates a group with a game by inserting a record into the + /// [GroupGameTable]. + Future addGroupToGame(String gameId, String groupId) async { + await into(groupGameTable).insert( + GroupGameTableCompanion.insert(groupId: groupId, gameId: gameId), + mode: InsertMode.insertOrReplace, + ); + } + + /// Retrieves the [Group] associated with the given [gameId]. + /// Returns `null` if no group is found. + Future getGroupOfGame({required String gameId}) async { + final result = await (select( + groupGameTable, + )..where((g) => g.gameId.equals(gameId))).getSingleOrNull(); + + if (result == null) { + return null; + } + + final group = await db.groupDao.getGroupById(groupId: result.groupId); + return group; + } + /// Checks if there is a group associated with the given [gameId]. /// Returns `true` if there is a group, otherwise `false`. - Future hasGameGroup({required String gameId}) async { + Future gameHasGroup({required String gameId}) async { final count = await (selectOnly(groupGameTable) ..where(groupGameTable.gameId.equals(gameId)) @@ -22,21 +46,34 @@ class GroupGameDao extends DatabaseAccessor return (count ?? 0) > 0; } - /// Retrieves the [Group] associated with the given [gameId]. - Future getGroupByGameId({required String gameId}) async { - final result = await (select( - groupGameTable, - )..where((g) => g.gameId.equals(gameId))).getSingle(); - - final group = await db.groupDao.getGroupById(groupId: result.groupId); - return group; + /// Checks if a specific group is associated with a specific game. + /// Returns `true` if the group is in the game, otherwise `false`. + Future isGroupInGame({ + required String gameId, + required String groupId, + }) async { + final count = + await (selectOnly(groupGameTable) + ..where( + groupGameTable.gameId.equals(gameId) & + groupGameTable.groupId.equals(groupId), + ) + ..addColumns([groupGameTable.groupId.count()])) + .map((row) => row.read(groupGameTable.groupId.count())) + .getSingle(); + return (count ?? 0) > 0; } - /// Associates a group with a game by inserting a record into the - /// [GroupGameTable]. - Future addGroupToGame(String gameId, String groupId) async { - await into( - groupGameTable, - ).insert(GroupGameTableCompanion.insert(groupId: groupId, gameId: gameId)); + /// Removes the association of a group from a game based on [groupId] and + /// [gameId]. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future removeGroupFromGame({ + required String gameId, + required String groupId, + }) async { + final query = delete(groupGameTable) + ..where((g) => g.gameId.equals(gameId) & g.groupId.equals(groupId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; } } diff --git a/lib/data/dao/player_game_dao.dart b/lib/data/dao/player_game_dao.dart index 8f367f8..ef15a80 100644 --- a/lib/data/dao/player_game_dao.dart +++ b/lib/data/dao/player_game_dao.dart @@ -10,6 +10,34 @@ class PlayerGameDao extends DatabaseAccessor with _$PlayerGameDaoMixin { PlayerGameDao(super.db); + /// Associates a player with a game by inserting a record into the + /// [PlayerGameTable]. + Future addPlayerToGame({ + required String gameId, + required String playerId, + }) async { + await into(playerGameTable).insert( + PlayerGameTableCompanion.insert(playerId: playerId, gameId: gameId), + mode: InsertMode.insertOrReplace, + ); + } + + /// Retrieves a list of [Player]s associated with the given [gameId]. + /// Returns null if no players are found. + Future?> getPlayersOfGame({required String gameId}) async { + final result = await (select( + playerGameTable, + )..where((p) => p.gameId.equals(gameId))).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; + } + /// Checks if there are any players associated with the given [gameId]. /// Returns `true` if there are players, otherwise `false`. Future gameHasPlayers({required String gameId}) async { @@ -22,30 +50,33 @@ class PlayerGameDao extends DatabaseAccessor return (count ?? 0) > 0; } - /// Retrieves a list of [Player]s associated with the given [gameId]. - /// Returns an empty list if no players are found. - Future> getPlayersByGameId({required String gameId}) async { - final result = await (select( - playerGameTable, - )..where((p) => p.gameId.equals(gameId))).get(); - - if (result.isEmpty) return []; - - final futures = result.map( - (row) => db.playerDao.getPlayerById(playerId: row.playerId), - ); - final players = await Future.wait(futures); - return players.whereType().toList(); - } - - /// Associates a player with a game by inserting a record into the - /// [PlayerGameTable]. - Future addPlayerToGame({ + /// Checks if a specific player is associated with a specific game. + /// Returns `true` if the player is in the game, otherwise `false`. + Future isPlayerInGame({ required String gameId, required String playerId, }) async { - await into(playerGameTable).insert( - PlayerGameTableCompanion.insert(playerId: playerId, gameId: gameId), - ); + final count = + await (selectOnly(playerGameTable) + ..where(playerGameTable.gameId.equals(gameId)) + ..where(playerGameTable.playerId.equals(playerId)) + ..addColumns([playerGameTable.playerId.count()])) + .map((row) => row.read(playerGameTable.playerId.count())) + .getSingle(); + return (count ?? 0) > 0; + } + + /// Removes the association of a player with a game by deleting the record + /// from the [PlayerGameTable]. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future removePlayerFromGame({ + required String gameId, + required String playerId, + }) async { + final query = delete(playerGameTable) + ..where((pg) => pg.gameId.equals(gameId)) + ..where((pg) => pg.playerId.equals(playerId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; } } diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index fe067ae..8cf96c2 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -10,8 +10,34 @@ class PlayerGroupDao extends DatabaseAccessor with _$PlayerGroupDaoMixin { PlayerGroupDao(super.db); + /// No need for a groupHasPlayers method since the members attribute is + /// not nullable + + /// Adds a [player] to a group with the given [groupId]. + /// If the player is already in the group, no action is taken. + /// If the player does not exist in the player table, they are added. + /// Returns `true` if the player was added, otherwise `false`. + Future addPlayerToGroup({ + required Player player, + required String groupId, + }) async { + if (await isPlayerInGroup(playerId: player.id, groupId: groupId)) { + return false; + } + + if (!await db.playerDao.playerExists(playerId: player.id)) { + db.playerDao.addPlayer(player: player); + } + + await into(playerGroupTable).insert( + PlayerGroupTableCompanion.insert(playerId: player.id, groupId: groupId), + ); + + return true; + } + /// Retrieves all players belonging to a specific group by [groupId]. - Future> getPlayersOfGroupById({required String groupId}) async { + Future> getPlayersOfGroup({required String groupId}) async { final query = select(playerGroupTable) ..where((pG) => pG.groupId.equals(groupId)); final result = await query.get(); @@ -38,29 +64,6 @@ class PlayerGroupDao extends DatabaseAccessor return rowsAffected > 0; } - /// Adds a [player] to a group with the given [groupId]. - /// If the player is already in the group, no action is taken. - /// If the player does not exist in the player table, they are added. - /// Returns `true` if the player was added, otherwise `false`. - Future addPlayerToGroup({ - required Player player, - required String groupId, - }) async { - if (await isPlayerInGroup(playerId: player.id, groupId: groupId)) { - return false; - } - - if (await db.playerDao.playerExists(playerId: player.id) == false) { - db.playerDao.addPlayer(player: player); - } - - await into(playerGroupTable).insert( - PlayerGroupTableCompanion.insert(playerId: player.id, groupId: groupId), - ); - - return true; - } - /// Checks if a player with [playerId] is in the group with [groupId]. /// Returns `true` if the player is in the group, otherwise `false`. Future isPlayerInGroup({ diff --git a/test/db_tests/game_test.dart b/test/db_tests/game_test.dart index d726425..7590a97 100644 --- a/test/db_tests/game_test.dart +++ b/test/db_tests/game_test.dart @@ -9,13 +9,17 @@ import 'package:game_tracker/data/dto/player.dart'; void main() { late AppDatabase database; - late Player player1; - late Player player2; - late Player player3; - late Player player4; - late Player player5; - late Group testgroup; - late Game testgame; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Group testGroup1; + late Group testGroup2; + late Game testGame1; + late Game testGame2; + late Game testGameOnlyPlayers; + late Game testGameOnlyGroup; final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); final fakeClock = Clock(() => fixedDate); @@ -29,88 +33,192 @@ void main() { ); withClock(fakeClock, () { - player1 = Player(name: 'Alice'); - player2 = Player(name: 'Bob'); - player3 = Player(name: 'Charlie'); - player4 = Player(name: 'Diana'); - player5 = Player(name: 'Eve'); - testgroup = Group( - name: 'Test Group', - members: [player1, player2, player3], + testPlayer1 = Player(name: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testPlayer5 = Player(name: 'Eve'); + testGroup1 = Group( + name: 'Test Group 2', + members: [testPlayer1, testPlayer2, testPlayer3], ); - testgame = Game( - name: 'Test Game', - group: testgroup, - players: [player4, player5], + testGroup2 = Group( + name: 'Test Group 2', + members: [testPlayer4, testPlayer5], ); + testGame1 = Game( + name: 'First Test Game', + group: testGroup1, + players: [testPlayer4, testPlayer5], + ); + testGame2 = Game( + name: 'Second Test Game', + group: testGroup2, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testGameOnlyPlayers = Game( + name: 'Test Game with Players', + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testGameOnlyGroup = Game(name: 'Test Game with Group', group: testGroup2); }); }); tearDown(() async { await database.close(); }); - group('game tests', () { - test('game is added correctly', () async { - await database.gameDao.addGame(game: testgame); + group('Game Tests', () { + test('Adding and fetching single game works correctly', () async { + await database.gameDao.addGame(game: testGame1); - final result = await database.gameDao.getGameById(gameId: testgame.id); + final result = await database.gameDao.getGameById(gameId: testGame1.id); - expect(result.id, testgame.id); - expect(result.name, testgame.name); - expect(result.winner, testgame.winner); - expect(result.createdAt, testgame.createdAt); + expect(result.id, testGame1.id); + expect(result.name, testGame1.name); + expect(result.winner, testGame1.winner); + expect(result.createdAt, testGame1.createdAt); if (result.group != null) { - expect(result.group!.members.length, testgroup.members.length); + expect(result.group!.members.length, testGroup1.members.length); - for (int i = 0; i < testgroup.members.length; i++) { - expect(result.group!.members[i].id, testgroup.members[i].id); - expect(result.group!.members[i].name, testgroup.members[i].name); + 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'); } if (result.players != null) { - expect(result.players!.length, testgame.players!.length); + expect(result.players!.length, testGame1.players!.length); - for (int i = 0; i < testgame.players!.length; i++) { - expect(result.players![i].id, testgame.players![i].id); - expect(result.players![i].name, testgame.players![i].name); - expect(result.players![i].createdAt, testgame.players![i].createdAt); + for (int i = 0; i < testGame1.players!.length; i++) { + expect(result.players![i].id, testGame1.players![i].id); + expect(result.players![i].name, testGame1.players![i].name); + expect(result.players![i].createdAt, testGame1.players![i].createdAt); } } else { fail('Players is null'); } }); - test('game is deleted correctly', () async { - await database.gameDao.addGame(game: testgame); + test('Adding and fetching multiple games works correctly', () async { + // TODO: Use upcoming addGames() method + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + await database.gameDao.addGame(game: testGameOnlyGroup); + await database.gameDao.addGame(game: testGameOnlyPlayers); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 4); + + final testGames = { + testGame1.id: testGame1, + testGame2.id: testGame2, + testGameOnlyGroup.id: testGameOnlyGroup, + testGameOnlyPlayers.id: testGameOnlyPlayers, + }; + + for (final game in allGames) { + final testGame = testGames[game.id]!; + + // Game-Checks + expect(game.id, testGame.id); + expect(game.name, testGame.name); + expect(game.createdAt, testGame.createdAt); + expect(game.winner, testGame.winner); + + // Group-Checks + if (testGame.group != null) { + expect(game.group!.id, testGame.group!.id); + expect(game.group!.name, testGame.group!.name); + expect(game.group!.createdAt, testGame.group!.createdAt); + + // Group Members-Checks + expect(game.group!.members.length, testGame.group!.members.length); + for (int i = 0; i < testGame.group!.members.length; i++) { + expect(game.group!.members[i].id, testGame.group!.members[i].id); + expect( + game.group!.members[i].name, + testGame.group!.members[i].name, + ); + expect( + game.group!.members[i].createdAt, + testGame.group!.members[i].createdAt, + ); + } + } else { + expect(game.group, null); + } + + // Players-Checks + if (testGame.players != null) { + expect(game.players!.length, testGame.players!.length); + for (int i = 0; i < testGame.players!.length; i++) { + expect(game.players![i].id, testGame.players![i].id); + expect(game.players![i].name, testGame.players![i].name); + expect(game.players![i].createdAt, testGame.players![i].createdAt); + } + } else { + expect(game.players, null); + } + } + }); + + test('Adding the same game twice does not create duplicates', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame1); + + final gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 1); + }); + + test('Game existence check works correctly', () async { + var gameExists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(gameExists, false); + + await database.gameDao.addGame(game: testGame1); + + gameExists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(gameExists, true); + }); + + test('Deleting a game works correctly', () async { + await database.gameDao.addGame(game: testGame1); final gameDeleted = await database.gameDao.deleteGame( - gameId: testgame.id, + gameId: testGame1.id, ); expect(gameDeleted, true); - final gameExists = await database.gameDao.gameExists(gameId: testgame.id); + final gameExists = await database.gameDao.gameExists( + gameId: testGame1.id, + ); expect(gameExists, false); }); - test('get game count works correctly', () async { - final initialCount = await database.gameDao.getGameCount(); - expect(initialCount, 0); + test('Getting the game count works correctly', () async { + var gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 0); - await database.gameDao.addGame(game: testgame); + await database.gameDao.addGame(game: testGame1); - final gameAdded = await database.gameDao.getGameCount(); - expect(gameAdded, 1); + gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 1); - final gameRemoved = await database.gameDao.deleteGame( - gameId: testgame.id, - ); - expect(gameRemoved, true); + await database.gameDao.addGame(game: testGame2); - final finalCount = await database.gameDao.getGameCount(); - expect(finalCount, 0); + gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 2); + + await database.gameDao.deleteGame(gameId: testGame1.id); + + gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 1); + + await database.gameDao.deleteGame(gameId: testGame2.id); + + gameCount = await database.gameDao.getGameCount(); + expect(gameCount, 0); }); }); } diff --git a/test/db_tests/group_game_test.dart b/test/db_tests/group_game_test.dart new file mode 100644 index 0000000..1733243 --- /dev/null +++ b/test/db_tests/group_game_test.dart @@ -0,0 +1,134 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:game_tracker/data/db/database.dart'; +import 'package:game_tracker/data/dto/game.dart'; +import 'package:game_tracker/data/dto/group.dart'; +import 'package:game_tracker/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Group testgroup; + late Game testgameWithGroup; + late Game testgameWithPlayers; + 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: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testPlayer5 = Player(name: 'Eve'); + testgroup = Group( + name: 'Test Group', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testgameWithPlayers = Game( + name: 'Test Game with Players', + players: [testPlayer4, testPlayer5], + ); + testgameWithGroup = Game(name: 'Test Game with Group', group: testgroup); + }); + }); + tearDown(() async { + await database.close(); + }); + group('Group-Game Tests', () { + test('Game has group works correctly', () async { + await database.gameDao.addGame(game: testgameWithPlayers); + await database.groupDao.addGroup(group: testgroup); + + var gameHasGroup = await database.groupGameDao.gameHasGroup( + gameId: testgameWithPlayers.id, + ); + + expect(gameHasGroup, false); + + await database.groupGameDao.addGroupToGame( + testgameWithPlayers.id, + testgroup.id, + ); + + gameHasGroup = await database.groupGameDao.gameHasGroup( + gameId: testgameWithPlayers.id, + ); + + expect(gameHasGroup, true); + }); + + test('Adding a group to a game works correctly', () async { + await database.gameDao.addGame(game: testgameWithPlayers); + await database.groupDao.addGroup(group: testgroup); + await database.groupGameDao.addGroupToGame( + testgameWithPlayers.id, + testgroup.id, + ); + + var groupAdded = await database.groupGameDao.isGroupInGame( + gameId: testgameWithPlayers.id, + groupId: testgroup.id, + ); + expect(groupAdded, true); + + groupAdded = await database.groupGameDao.isGroupInGame( + gameId: testgameWithPlayers.id, + groupId: '', + ); + expect(groupAdded, false); + }); + + test('Removing group from game works correctly', () async { + await database.gameDao.addGame(game: testgameWithGroup); + + final groupToRemove = testgameWithGroup.group!; + + final removed = await database.groupGameDao.removeGroupFromGame( + groupId: groupToRemove.id, + gameId: testgameWithGroup.id, + ); + expect(removed, true); + + final result = await database.gameDao.getGameById( + gameId: testgameWithGroup.id, + ); + expect(result.group, null); + }); + + test('Retrieving group of a game works correctly', () async { + await database.gameDao.addGame(game: testgameWithGroup); + final group = await database.groupGameDao.getGroupOfGame( + gameId: testgameWithGroup.id, + ); + + if (group == null) { + fail('Group should not be null'); + } + + 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 < group.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); + } + }); + }); +} diff --git a/test/db_tests/group_test.dart b/test/db_tests/group_test.dart index a076ab0..6c8e6c9 100644 --- a/test/db_tests/group_test.dart +++ b/test/db_tests/group_test.dart @@ -8,12 +8,14 @@ import 'package:game_tracker/data/dto/player.dart'; void main() { late AppDatabase database; - late Player player1; - late Player player2; - late Player player3; - late Player player4; - late Group testgroup; - late Group testgroup2; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Group testGroup1; + late Group testGroup2; + late Group testGroup3; + late Group testGroup4; final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); final fakeClock = Clock(() => fixedDate); @@ -27,157 +29,146 @@ void main() { ); withClock(fakeClock, () { - player1 = Player(name: 'Alice'); - player2 = Player(name: 'Bob'); - player3 = Player(name: 'Charlie'); - player4 = Player(name: 'Diana'); - testgroup = Group( + testPlayer1 = Player(name: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testGroup1 = Group( name: 'Test Group', - members: [player1, player2, player3], + members: [testPlayer1, testPlayer2, testPlayer3], ); - testgroup2 = Group( + testGroup2 = Group( id: 'gr2', name: 'Second Group', - members: [player2, player3, player4], + members: [testPlayer2, testPlayer3, testPlayer4], + ); + testGroup3 = Group( + id: 'gr2', + name: 'Second Group', + members: [testPlayer2, testPlayer4], + ); + testGroup4 = Group( + id: 'gr2', + name: 'Second Group', + members: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], ); }); }); tearDown(() async { await database.close(); }); - group('group tests', () { - test('all groups get fetched correctly', () async { - await database.groupDao.addGroup(group: testgroup); - await database.groupDao.addGroup(group: testgroup2); + group('Group Tests', () { + 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, + ); + } + }); + + test('Adding and fetching multiple groups works correctly', () async { + // TODO: Use upcoming addGroups() method + await database.groupDao.addGroup(group: testGroup1); + await database.groupDao.addGroup(group: testGroup2); + await database.groupDao.addGroup(group: testGroup3); + await database.groupDao.addGroup(group: testGroup4); final allGroups = await database.groupDao.getAllGroups(); expect(allGroups.length, 2); - final fetchedGroup1 = allGroups.firstWhere((g) => g.id == testgroup.id); - expect(fetchedGroup1.name, testgroup.name); - expect(fetchedGroup1.members.length, testgroup.members.length); - expect(fetchedGroup1.members.elementAt(0).id, player1.id); - expect(fetchedGroup1.members.elementAt(0).createdAt, player1.createdAt); + final testGroups = {testGroup1.id: testGroup1, testGroup2.id: testGroup2}; - final fetchedGroup2 = allGroups.firstWhere((g) => g.id == testgroup2.id); - expect(fetchedGroup2.name, testgroup2.name); - expect(fetchedGroup2.members.length, testgroup2.members.length); - expect(fetchedGroup2.members.elementAt(0).id, player2.id); - expect(fetchedGroup2.members.elementAt(0).createdAt, player2.createdAt); - }); + for (final group in allGroups) { + final testGroup = testGroups[group.id]!; - test('group and group members gets added correctly', () async { - await database.groupDao.addGroup(group: testgroup); + expect(group.id, testGroup.id); + expect(group.name, testGroup.name); + expect(group.createdAt, testGroup.createdAt); - final result = await database.groupDao.getGroupById( - groupId: testgroup.id, - ); - - expect(result.id, testgroup.id); - expect(result.name, testgroup.name); - expect(result.createdAt, testgroup.createdAt); - - expect(result.members.length, testgroup.members.length); - for (int i = 0; i < testgroup.members.length; i++) { - expect(result.members[i].id, testgroup.members[i].id); - expect(result.members[i].name, testgroup.members[i].name); - expect(result.members[i].createdAt, testgroup.members[i].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('group gets deleted correctly', () async { - await database.groupDao.addGroup(group: testgroup); + 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); + }); + + 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); + }); + + test('Deleting a group works correctly', () async { + await database.groupDao.addGroup(group: testGroup1); final groupDeleted = await database.groupDao.deleteGroup( - groupId: testgroup.id, + groupId: testGroup1.id, ); expect(groupDeleted, true); final groupExists = await database.groupDao.groupExists( - groupId: testgroup.id, + groupId: testGroup1.id, ); expect(groupExists, false); }); - test('group name gets updated correcly ', () async { - await database.groupDao.addGroup(group: testgroup); + test('Updating a group name works correcly', () async { + await database.groupDao.addGroup(group: testGroup1); const newGroupName = 'new group name'; await database.groupDao.updateGroupname( - groupId: testgroup.id, + groupId: testGroup1.id, newName: newGroupName, ); final result = await database.groupDao.getGroupById( - groupId: testgroup.id, + groupId: testGroup1.id, ); expect(result.name, newGroupName); }); - test('Adding player to group works correctly', () async { - await database.groupDao.addGroup(group: testgroup); - - await database.playerGroupDao.addPlayerToGroup( - player: player4, - groupId: testgroup.id, - ); - - final playerAdded = await database.playerGroupDao.isPlayerInGroup( - playerId: player4.id, - groupId: testgroup.id, - ); - - expect(playerAdded, true); - - final playerNotAdded = !await database.playerGroupDao.isPlayerInGroup( - playerId: '', - groupId: testgroup.id, - ); - - expect(playerNotAdded, true); - - final result = await database.groupDao.getGroupById( - groupId: testgroup.id, - ); - expect(result.members.length, testgroup.members.length + 1); - - final addedPlayer = result.members.firstWhere((p) => p.id == player4.id); - expect(addedPlayer.name, player4.name); - expect(addedPlayer.createdAt, player4.createdAt); - }); - - 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); - }); - - test('get group count works correctly', () async { + test('Getting the group count works correctly', () async { final initialCount = await database.groupDao.getGroupCount(); expect(initialCount, 0); - await database.groupDao.addGroup(group: testgroup); + await database.groupDao.addGroup(group: testGroup1); final groupAdded = await database.groupDao.getGroupCount(); expect(groupAdded, 1); final groupRemoved = await database.groupDao.deleteGroup( - groupId: testgroup.id, + groupId: testGroup1.id, ); expect(groupRemoved, true); diff --git a/test/db_tests/player_game_test.dart b/test/db_tests/player_game_test.dart new file mode 100644 index 0000000..f50afc1 --- /dev/null +++ b/test/db_tests/player_game_test.dart @@ -0,0 +1,140 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:game_tracker/data/db/database.dart'; +import 'package:game_tracker/data/dto/game.dart'; +import 'package:game_tracker/data/dto/group.dart'; +import 'package:game_tracker/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Player testPlayer6; + late Group testgroup; + late Game testGameOnlyGroup; + late Game testGameOnlyPlayers; + 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: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testPlayer5 = Player(name: 'Eve'); + testPlayer6 = Player(name: 'Frank'); + testgroup = Group( + name: 'Test Group', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testGameOnlyGroup = Game(name: 'Test Game with Group', group: testgroup); + testGameOnlyPlayers = Game( + name: 'Test Game with Players', + players: [testPlayer4, testPlayer5, testPlayer6], + ); + }); + }); + tearDown(() async { + await database.close(); + }); + + group('Player-Game Tests', () { + test('Game has player works correctly', () async { + await database.gameDao.addGame(game: testGameOnlyGroup); + await database.playerDao.addPlayer(player: testPlayer1); + + var gameHasPlayers = await database.playerGameDao.gameHasPlayers( + gameId: testGameOnlyGroup.id, + ); + + expect(gameHasPlayers, false); + + database.playerGameDao.addPlayerToGame( + gameId: testGameOnlyGroup.id, + playerId: testPlayer1.id, + ); + + gameHasPlayers = await database.playerGameDao.gameHasPlayers( + gameId: testGameOnlyGroup.id, + ); + + expect(gameHasPlayers, true); + }); + + test('Adding a player to a game works correctly', () async { + await database.gameDao.addGame(game: testGameOnlyGroup); + await database.playerDao.addPlayer(player: testPlayer5); + await database.playerGameDao.addPlayerToGame( + gameId: testGameOnlyGroup.id, + playerId: testPlayer5.id, + ); + + var playerAdded = await database.playerGameDao.isPlayerInGame( + gameId: testGameOnlyGroup.id, + playerId: testPlayer5.id, + ); + + expect(playerAdded, true); + + playerAdded = await database.playerGameDao.isPlayerInGame( + gameId: testGameOnlyGroup.id, + playerId: '', + ); + + expect(playerAdded, false); + }); + + test('Removing player from game works correctly', () async { + await database.gameDao.addGame(game: testGameOnlyPlayers); + + final playerToRemove = testGameOnlyPlayers.players![0]; + + final removed = await database.playerGameDao.removePlayerFromGame( + playerId: playerToRemove.id, + gameId: testGameOnlyPlayers.id, + ); + expect(removed, true); + + final result = await database.gameDao.getGameById( + gameId: testGameOnlyPlayers.id, + ); + expect(result.players!.length, testGameOnlyPlayers.players!.length - 1); + + final playerExists = result.players!.any( + (p) => p.id == playerToRemove.id, + ); + expect(playerExists, false); + }); + + test('Retrieving players of a game works correctly', () async { + await database.gameDao.addGame(game: testGameOnlyPlayers); + final players = await database.playerGameDao.getPlayersOfGame( + gameId: testGameOnlyPlayers.id, + ); + + if (players == null) { + fail('Players should not be null'); + } + + for (int i = 0; i < players.length; i++) { + expect(players[i].id, testGameOnlyPlayers.players![i].id); + expect(players[i].name, testGameOnlyPlayers.players![i].name); + expect(players[i].createdAt, testGameOnlyPlayers.players![i].createdAt); + } + }); + }); +} diff --git a/test/db_tests/player_group_test.dart b/test/db_tests/player_group_test.dart new file mode 100644 index 0000000..2783430 --- /dev/null +++ b/test/db_tests/player_group_test.dart @@ -0,0 +1,103 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:game_tracker/data/db/database.dart'; +import 'package:game_tracker/data/dto/group.dart'; +import 'package:game_tracker/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Group testgroup; + 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: 'Alice'); + testPlayer2 = Player(name: 'Bob'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); + testgroup = Group( + name: 'Test Group', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + }); + }); + tearDown(() async { + await database.close(); + }); + + group('Player-Group Tests', () { + /// No need to test if group has players since the members attribute is + /// not nullable + + 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); + }); + + 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); + }); + + 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); + } + }); + }); +} diff --git a/test/db_tests/player_test.dart b/test/db_tests/player_test.dart index fa65f67..30d9a14 100644 --- a/test/db_tests/player_test.dart +++ b/test/db_tests/player_test.dart @@ -7,8 +7,10 @@ import 'package:game_tracker/data/dto/player.dart'; void main() { late AppDatabase database; - late Player testPlayer; + 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); @@ -22,27 +24,29 @@ void main() { ); withClock(fakeClock, () { - testPlayer = Player(name: 'Test Player'); - testPlayer2 = Player(name: 'Second Group'); + testPlayer1 = Player(name: 'Test Player'); + testPlayer2 = Player(name: 'Second Player'); + testPlayer3 = Player(name: 'Charlie'); + testPlayer4 = Player(name: 'Diana'); }); }); tearDown(() async { await database.close(); }); - group('player tests', () { - test('all players get fetched correctly', () async { - await database.playerDao.addPlayer(player: testPlayer); + group('Player Tests', () { + 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 == testPlayer.id, + (g) => g.id == testPlayer1.id, ); - expect(fetchedPlayer1.name, testPlayer.name); - expect(fetchedPlayer1.createdAt, testPlayer.createdAt); + expect(fetchedPlayer1.name, testPlayer1.name); + expect(fetchedPlayer1.createdAt, testPlayer1.createdAt); final fetchedPlayer2 = allPlayers.firstWhere( (g) => g.id == testPlayer2.id, @@ -51,62 +55,107 @@ void main() { expect(fetchedPlayer2.createdAt, testPlayer2.createdAt); }); - test('players get inserted correcly ', () async { - await database.playerDao.addPlayer(player: testPlayer); - final result = await database.playerDao.getPlayerById( - playerId: testPlayer.id, - ); + test('Adding and fetching multiple players works correctly', () async { + // TODO: Use upcoming addPlayers() method + await database.playerDao.addPlayer(player: testPlayer1); + await database.playerDao.addPlayer(player: testPlayer2); + await database.playerDao.addPlayer(player: testPlayer3); + await database.playerDao.addPlayer(player: testPlayer4); - expect(result.id, testPlayer.id); - expect(result.name, testPlayer.name); - expect(result.createdAt, testPlayer.createdAt); + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers.length, 4); + + // Map for connencting 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); + } }); - test('players get deleted correcly ', () async { - await database.playerDao.addPlayer(player: testPlayer); + 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('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); + }); + + test('Deleting a player works correctly', () async { + await database.playerDao.addPlayer(player: testPlayer1); final playerDeleted = await database.playerDao.deletePlayer( - playerId: testPlayer.id, + playerId: testPlayer1.id, ); expect(playerDeleted, true); final playerExists = await database.playerDao.playerExists( - playerId: testPlayer.id, + playerId: testPlayer1.id, ); expect(playerExists, false); }); - test('player name gets updated correcly ', () async { - await database.playerDao.addPlayer(player: testPlayer); + test('Updating a player name works correcly', () async { + await database.playerDao.addPlayer(player: testPlayer1); const newPlayerName = 'new player name'; await database.playerDao.updatePlayername( - playerId: testPlayer.id, + playerId: testPlayer1.id, newName: newPlayerName, ); final result = await database.playerDao.getPlayerById( - playerId: testPlayer.id, + playerId: testPlayer1.id, ); expect(result.name, newPlayerName); }); - test('get player count works correctly', () async { - final initialCount = await database.playerDao.getPlayerCount(); - expect(initialCount, 0); + test('Getting the player count works correctly', () async { + var playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 0); - await database.playerDao.addPlayer(player: testPlayer); + await database.playerDao.addPlayer(player: testPlayer1); - final playerAdded = await database.playerDao.getPlayerCount(); - expect(playerAdded, 1); + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 1); - final playerRemoved = await database.playerDao.deletePlayer( - playerId: testPlayer.id, - ); - expect(playerRemoved, true); + await database.playerDao.addPlayer(player: testPlayer2); - final finalCount = await database.playerDao.getPlayerCount(); - expect(finalCount, 0); + 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); }); }); }