From dec74e9b627712fcf7017300c63c23826b5c90c2 Mon Sep 17 00:00:00 2001 From: gelbeinhalb Date: Tue, 20 Jan 2026 15:41:05 +0100 Subject: [PATCH] add game tests --- test/db_tests/game_test.dart | 522 +++++++++++++++++++++++++++++++++++ 1 file changed, 522 insertions(+) create mode 100644 test/db_tests/game_test.dart diff --git a/test/db_tests/game_test.dart b/test/db_tests/game_test.dart new file mode 100644 index 0000000..924e673 --- /dev/null +++ b/test/db_tests/game_test.dart @@ -0,0 +1,522 @@ +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:game_tracker/data/db/database.dart'; +import 'package:game_tracker/data/dto/game.dart'; + +void main() { + late AppDatabase database; + late Game testGame1; + late Game testGame2; + late Game testGame3; + final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testGame1 = Game( + name: 'Chess', + ruleset: 'winner.single', + description: 'A classic strategy game', + color: 0xFF0000FF, + icon: 'chess_icon', + ); + testGame2 = Game( + id: 'game2', + name: 'Poker', + ruleset: 'Texas Hold\'em rules', + description: 'winner.multiple', + color: 0xFFFF0000, + icon: 'poker_icon', + ); + testGame3 = Game( + id: 'game3', + name: 'Monopoly', + description: 'A board game about real estate', + ); + }); + }); + + tearDown(() async { + await database.close(); + }); + + group('Game Tests', () { + // ==================== getAllGames ==================== + test('getAllGames returns empty list when no games exist', () async { + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + test('Adding and fetching a single game works correctly', () async { + await database.gameDao.addGame(game: testGame1); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + expect(allGames.first.id, testGame1.id); + expect(allGames.first.name, testGame1.name); + expect(allGames.first.ruleset, testGame1.ruleset); + expect(allGames.first.description, testGame1.description); + expect(allGames.first.color, testGame1.color); + expect(allGames.first.icon, testGame1.icon); + expect(allGames.first.createdAt, testGame1.createdAt); + }); + + test('Adding and fetching multiple games works correctly', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + await database.gameDao.addGame(game: testGame3); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 3); + + final names = allGames.map((g) => g.name).toList(); + expect(names, containsAll(['Chess', 'Poker', 'Monopoly'])); + }); + + // ==================== getGameById ==================== + test('getGameById returns correct game', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + + final game = await database.gameDao.getGameById(gameId: testGame2.id); + expect(game.id, testGame2.id); + expect(game.name, testGame2.name); + expect(game.ruleset, testGame2.ruleset); + expect(game.description, testGame2.description); + expect(game.color, testGame2.color); + expect(game.icon, testGame2.icon); + }); + + test('getGameById throws exception for non-existent game', () async { + expect( + () => database.gameDao.getGameById(gameId: 'non-existent-id'), + throwsA(isA()), + ); + }); + + // ==================== addGame ==================== + test('addGame returns true when game is added successfully', () async { + final result = await database.gameDao.addGame(game: testGame1); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + }); + + test('addGame returns false when game already exists', () async { + final firstAdd = await database.gameDao.addGame(game: testGame1); + expect(firstAdd, true); + + final secondAdd = await database.gameDao.addGame(game: testGame1); + expect(secondAdd, false); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + }); + + test('addGame handles game with null optional fields', () async { + final gameWithNulls = Game(name: 'Simple Game'); + final result = await database.gameDao.addGame(game: gameWithNulls); + expect(result, true); + + final fetchedGame = await database.gameDao.getGameById( + gameId: gameWithNulls.id, + ); + expect(fetchedGame.name, 'Simple Game'); + expect(fetchedGame.description, isNull); + expect(fetchedGame.color, isNull); + expect(fetchedGame.icon, isNull); + }); + + // ==================== addGamesAsList ==================== + test('addGamesAsList adds multiple games correctly', () async { + final result = await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 3); + }); + + test('addGamesAsList returns false for empty list', () async { + final result = await database.gameDao.addGamesAsList(games: []); + expect(result, false); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 0); + }); + + test('addGamesAsList ignores duplicate games', () async { + await database.gameDao.addGame(game: testGame1); + + final result = await database.gameDao.addGamesAsList( + games: [testGame1, testGame2], + ); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 2); + }); + + // ==================== deleteGame ==================== + test('deleteGame returns true when game is deleted', () async { + await database.gameDao.addGame(game: testGame1); + + final result = await database.gameDao.deleteGame(gameId: testGame1.id); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + test('deleteGame returns false for non-existent game', () async { + final result = await database.gameDao.deleteGame( + gameId: 'non-existent-id', + ); + expect(result, false); + }); + + test('deleteGame only deletes the specified game', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + await database.gameDao.deleteGame(gameId: testGame2.id); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 2); + expect(allGames.any((g) => g.id == testGame2.id), false); + expect(allGames.any((g) => g.id == testGame1.id), true); + expect(allGames.any((g) => g.id == testGame3.id), true); + }); + + // ==================== gameExists ==================== + test('gameExists returns true for existing game', () async { + await database.gameDao.addGame(game: testGame1); + + final exists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(exists, true); + }); + + test('gameExists returns false for non-existent game', () async { + final exists = await database.gameDao.gameExists( + gameId: 'non-existent-id', + ); + expect(exists, false); + }); + + test('gameExists returns false after game is deleted', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.deleteGame(gameId: testGame1.id); + + final exists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(exists, false); + }); + + // ==================== updateGameName ==================== + test('updateGameName updates the name correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameName( + gameId: testGame1.id, + newName: 'Updated Chess', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.name, 'Updated Chess'); + expect(updatedGame.ruleset, testGame1.ruleset); + }); + + test('updateGameName does nothing for non-existent game', () async { + await database.gameDao.updateGameName( + gameId: 'non-existent-id', + newName: 'New Name', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // ==================== updateGameRuleset ==================== + test('updateGameRuleset updates the ruleset correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameRuleset( + gameId: testGame1.id, + newRuleset: 'New ruleset for chess', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.ruleset, 'New ruleset for chess'); + expect(updatedGame.name, testGame1.name); + }); + + test('updateGameRuleset does nothing for non-existent game', () async { + await database.gameDao.updateGameRuleset( + gameId: 'non-existent-id', + newRuleset: 'New Ruleset', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // ==================== updateGameDescription ==================== + test('updateGameDescription updates the description correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: 'An updated description', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.description, 'An updated description'); + }); + + test('updateGameDescription can set description to null', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: null, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.description, isNull); + }); + + test('updateGameDescription does nothing for non-existent game', () async { + await database.gameDao.updateGameDescription( + gameId: 'non-existent-id', + newDescription: 'New Description', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // ==================== updateGameColor ==================== + test('updateGameColor updates the color correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameColor( + gameId: testGame1.id, + newColor: 0xFF00FF00, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.color, 0xFF00FF00); + }); + + test('updateGameColor can set color to null', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameColor( + gameId: testGame1.id, + newColor: null, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.color, isNull); + }); + + test('updateGameColor does nothing for non-existent game', () async { + await database.gameDao.updateGameColor( + gameId: 'non-existent-id', + newColor: 0xFF00FF00, + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // ==================== updateGameIcon ==================== + test('updateGameIcon updates the icon correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameIcon( + gameId: testGame1.id, + newIcon: 'new_chess_icon', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.icon, 'new_chess_icon'); + }); + + test('updateGameIcon can set icon to null', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameIcon( + gameId: testGame1.id, + newIcon: null, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.icon, isNull); + }); + + test('updateGameIcon does nothing for non-existent game', () async { + await database.gameDao.updateGameIcon( + gameId: 'non-existent-id', + newIcon: 'some_icon', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // ==================== getGameCount ==================== + test('getGameCount returns 0 when no games exist', () async { + final count = await database.gameDao.getGameCount(); + expect(count, 0); + }); + + test('getGameCount returns correct count after adding games', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + final count = await database.gameDao.getGameCount(); + expect(count, 3); + }); + + test('getGameCount updates correctly after deletion', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2], + ); + + final countBefore = await database.gameDao.getGameCount(); + expect(countBefore, 2); + + await database.gameDao.deleteGame(gameId: testGame1.id); + + final countAfter = await database.gameDao.getGameCount(); + expect(countAfter, 1); + }); + + // ==================== deleteAllGames ==================== + test('deleteAllGames removes all games', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + final countBefore = await database.gameDao.getGameCount(); + expect(countBefore, 3); + + final result = await database.gameDao.deleteAllGames(); + expect(result, true); + + final countAfter = await database.gameDao.getGameCount(); + expect(countAfter, 0); + }); + + test('deleteAllGames returns false when no games exist', () async { + final result = await database.gameDao.deleteAllGames(); + expect(result, false); + }); + + // ==================== Edge Cases ==================== + test('Game with special characters in name is stored correctly', () async { + final specialGame = Game( + name: 'Game\'s & "Special" ', + description: 'Description with émojis 🎮🎲', + ); + await database.gameDao.addGame(game: specialGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: specialGame.id, + ); + expect(fetchedGame.name, 'Game\'s & "Special" '); + expect(fetchedGame.description, 'Description with émojis 🎮🎲'); + }); + + test('Game with empty string fields is stored correctly', () async { + final emptyGame = Game( + name: '', + ruleset: '', + description: '', + icon: '', + ); + await database.gameDao.addGame(game: emptyGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: emptyGame.id, + ); + expect(fetchedGame.name, ''); + expect(fetchedGame.ruleset, ''); + expect(fetchedGame.description, ''); + expect(fetchedGame.icon, ''); + }); + + test('Game with very long strings is stored correctly', () async { + final longString = 'A' * 10000; + final longGame = Game( + name: longString, + description: longString, + ruleset: longString, + ); + await database.gameDao.addGame(game: longGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: longGame.id, + ); + expect(fetchedGame.name.length, 10000); + expect(fetchedGame.description?.length, 10000); + expect(fetchedGame.ruleset?.length, 10000); + }); + + test('Multiple updates to the same game work correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameName( + gameId: testGame1.id, + newName: 'Updated Name', + ); + await database.gameDao.updateGameColor( + gameId: testGame1.id, + newColor: 0xFF123456, + ); + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: 'Updated Description', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.name, 'Updated Name'); + expect(updatedGame.color, 0xFF123456); + expect(updatedGame.description, 'Updated Description'); + expect(updatedGame.ruleset, testGame1.ruleset); + expect(updatedGame.icon, testGame1.icon); + }); + }); +} +