From bce36b5ab4189b4fce177d714d3ecf97f05f1deb Mon Sep 17 00:00:00 2001 From: MasterGordon Date: Thu, 11 Sep 2025 15:01:29 +0200 Subject: [PATCH] improved test coverage --- .gitignore | 1 + backend/repositories/gameRepository.test.ts | 116 +++++++++++++++++++ backend/repositories/gameRepository.ts | 2 +- backend/repositories/scoreRepository.test.ts | 51 ++++++++ backend/repositories/userRepository.test.ts | 70 ++++++++++- shared/game.test.ts | 42 ++++++- 6 files changed, 279 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4caea98..758d6e1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ temp_dbs deploy.sh sqlite.db +coverage diff --git a/backend/repositories/gameRepository.test.ts b/backend/repositories/gameRepository.test.ts index ceccfc2..316a6c1 100644 --- a/backend/repositories/gameRepository.test.ts +++ b/backend/repositories/gameRepository.test.ts @@ -4,9 +4,14 @@ import { Game } from "../schema"; import { getCurrentGame, getGame, + getGames, getGamesCount, + getTotalGamesPlayed, + parseGameState, upsertGame, + upsertGameState, } from "./gameRepository"; +import { encode } from "@msgpack/msgpack"; describe("GameRepository", () => { it("should get game by uuid", async () => { @@ -136,4 +141,115 @@ describe("GameRepository", () => { started: started + 1, }); }); + + it("should get finished games for user", async () => { + const db = getTestDb(); + const started = Date.now(); + await db.insert(Game).values({ + uuid: "TestUuid1", + user: "TestUser", + stage: 1, + gameState: Buffer.from("ANY"), + finished: 1, + started, + }); + await db.insert(Game).values({ + uuid: "TestUuid2", + user: "TestUser", + stage: 2, + gameState: Buffer.from("ANY"), + finished: 0, + started: started + 1, + }); + await db.insert(Game).values({ + uuid: "TestUuid3", + user: "OtherUser", + stage: 1, + gameState: Buffer.from("ANY"), + finished: 1, + started: started + 2, + }); + const games = await getGames(db, "TestUser"); + expect(games).toHaveLength(1); + expect(games[0].uuid).toBe("TestUuid1"); + }); + + it("should get total games played for user", async () => { + const db = getTestDb(); + const started = Date.now(); + await db.insert(Game).values({ + uuid: "TestUuid1", + user: "TestUser", + stage: 1, + gameState: Buffer.from("ANY"), + finished: 1, + started, + }); + await db.insert(Game).values({ + uuid: "TestUuid2", + user: "TestUser", + stage: 2, + gameState: Buffer.from("ANY"), + finished: 0, + started: started + 1, + }); + const totalGames = await getTotalGamesPlayed(db, "TestUser"); + expect(totalGames).toBe(1); + }); + + it("should get total games played for all users", async () => { + const db = getTestDb(); + const started = Date.now(); + await db.insert(Game).values({ + uuid: "TestUuid1", + user: "TestUser", + stage: 1, + gameState: Buffer.from("ANY"), + finished: 1, + started, + }); + await db.insert(Game).values({ + uuid: "TestUuid2", + user: "OtherUser", + stage: 2, + gameState: Buffer.from("ANY"), + finished: 1, + started: started + 1, + }); + const totalGames = await getTotalGamesPlayed(db); + expect(totalGames).toBe(2); + }); + + it("should parse game state", () => { + const gameData = { test: "data", number: 42 }; + const buffer = Buffer.from(encode(gameData)); + const parsed = parseGameState(buffer); + expect(parsed).toEqual(gameData); + }); + + it("should upsert game state", async () => { + const db = getTestDb(); + const serverGame = { + uuid: "TestUuid", + user: "TestUser", + stage: 1, + finished: 1, + started: Date.now(), + // Other ServerGame properties don't matter for this test + mines: [[false]], + width: 1, + height: 1, + isRevealed: [[false]], + isFlagged: [[false]], + isQuestionMark: [[false]], + minesCount: 0, + lastClick: [0, 0] as [number, number], + theme: "default" as const, + }; + await upsertGameState(db, serverGame); + const game = await getGame(db, "TestUuid"); + expect(game?.uuid).toBe("TestUuid"); + expect(game?.user).toBe("TestUser"); + expect(game?.stage).toBe(1); + }); }); diff --git a/backend/repositories/gameRepository.ts b/backend/repositories/gameRepository.ts index 9c16b4f..088e966 100644 --- a/backend/repositories/gameRepository.ts +++ b/backend/repositories/gameRepository.ts @@ -82,7 +82,7 @@ export const upsertGameState = async ( game: ServerGame, ) => { const { uuid, user, stage, finished, started } = game; - upsertGame(db, { + await upsertGame(db, { uuid, user, stage, diff --git a/backend/repositories/scoreRepository.test.ts b/backend/repositories/scoreRepository.test.ts index 3b1ac03..06735de 100644 --- a/backend/repositories/scoreRepository.test.ts +++ b/backend/repositories/scoreRepository.test.ts @@ -37,4 +37,55 @@ describe("ScoreRepository", () => { const result = await getScoreBoard(db); expect(result).toEqual([{ stage: 10, user: "TestUser" }]); }); + + it("should return empty array when no finished games exist", async () => { + const db = getTestDb(); + await db.insert(User).values({ + name: "TestUser2", + password: "test", + }); + await db.insert(Game).values({ + user: "TestUser2", + uuid: crypto.randomUUID(), + stage: 5, + gameState: Buffer.from("ANY"), + finished: 0, + started: Date.now(), + }); + const result = await getScoreBoard(db); + expect(result).toEqual([]); + }); + + it("should handle multiple users and sort by highest stage", async () => { + const db = getTestDb(); + await db.insert(User).values({ + name: "User1", + password: "test", + }); + await db.insert(User).values({ + name: "User2", + password: "test", + }); + await db.insert(Game).values({ + user: "User1", + uuid: crypto.randomUUID(), + stage: 15, + gameState: Buffer.from("ANY"), + finished: 1, + started: Date.now(), + }); + await db.insert(Game).values({ + user: "User2", + uuid: crypto.randomUUID(), + stage: 25, + gameState: Buffer.from("ANY"), + finished: 1, + started: Date.now(), + }); + const result = await getScoreBoard(db); + expect(result).toEqual([ + { stage: 25, user: "User2" }, + { stage: 15, user: "User1" } + ]); + }); }); diff --git a/backend/repositories/userRepository.test.ts b/backend/repositories/userRepository.test.ts index f77ff82..d04e17c 100644 --- a/backend/repositories/userRepository.test.ts +++ b/backend/repositories/userRepository.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "bun:test"; import { getTestDb } from "../database/getTestDb"; -import { getUser, loginUser, registerUser } from "./userRepository"; +import { getUser, getUserCount, getUserSettings, loginUser, registerUser, upsertUserSettings } from "./userRepository"; describe("UserRepository", () => { it("should register a user", async () => { @@ -45,4 +45,72 @@ describe("UserRepository", () => { password: undefined, }); }); + + it("should throw error if password is incorrect", async () => { + const db = getTestDb(); + await registerUser(db, "TestUser", "test"); + expect(loginUser(db, "TestUser", "wrongpassword")).rejects.toThrow( + "Incorrect password", + ); + }); + + it("should handle getUser for nonexistent user", async () => { + const db = getTestDb(); + const user = await getUser(db, "NonexistentUser"); + expect(user.name).toBeUndefined(); + }); + + it("should get user count", async () => { + const db = getTestDb(); + await registerUser(db, "TestUser1", "test"); + await registerUser(db, "TestUser2", "test"); + const count = await getUserCount(db); + expect(count).toBe(2); + }); + + it("should get default user settings", async () => { + const db = getTestDb(); + const settings = await getUserSettings(db, "TestUser"); + expect(settings).toEqual({ + placeQuestionMark: false, + longPressOnDesktop: false, + showRevealAnimation: true, + soundEnabled: true, + }); + }); + + it("should upsert user settings - insert", async () => { + const db = getTestDb(); + const newSettings = { + placeQuestionMark: true, + longPressOnDesktop: true, + showRevealAnimation: false, + soundEnabled: false, + }; + await upsertUserSettings(db, "TestUser", newSettings); + const settings = await getUserSettings(db, "TestUser"); + expect(settings).toEqual(newSettings); + }); + + it("should upsert user settings - update", async () => { + const db = getTestDb(); + const initialSettings = { + placeQuestionMark: false, + longPressOnDesktop: false, + showRevealAnimation: true, + soundEnabled: true, + }; + await upsertUserSettings(db, "TestUser", initialSettings); + + const updatedSettings = { + placeQuestionMark: true, + longPressOnDesktop: true, + showRevealAnimation: false, + soundEnabled: false, + }; + await upsertUserSettings(db, "TestUser", updatedSettings); + + const settings = await getUserSettings(db, "TestUser"); + expect(settings).toEqual(updatedSettings); + }); }); diff --git a/shared/game.test.ts b/shared/game.test.ts index 105ec4b..381997e 100644 --- a/shared/game.test.ts +++ b/shared/game.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "bun:test"; -import { getValue, ServerGame, serverToClientGame } from "./game"; +import { getValue, isServerGame, isClientGame, ServerGame, ClientGame, serverToClientGame } from "./game"; describe("Game", () => { it("should get value", () => { @@ -15,6 +15,46 @@ describe("Game", () => { expect(getValue(mines, 1, 3)).toEqual(8); }); + it("should identify server game", () => { + const serverGame: ServerGame = { + theme: "default", + mines: [[false]], + minesCount: 0, + isRevealed: [[false]], + isFlagged: [[false]], + isQuestionMark: [[false]], + started: Date.now(), + finished: 0, + lastClick: [0, 0], + uuid: "test-uuid", + width: 1, + height: 1, + user: "TestUser", + stage: 1, + }; + expect(isServerGame(serverGame)).toBe(true); + }); + + it("should identify client game", () => { + const clientGame: ClientGame = { + theme: "default", + minesCount: 0, + isRevealed: [[false]], + isFlagged: [[false]], + isQuestionMark: [[false]], + values: [[0]], + started: Date.now(), + lastClick: [0, 0], + uuid: "test-uuid", + width: 1, + height: 1, + user: "TestUser", + stage: 1, + }; + expect(isClientGame(clientGame)).toBe(true); + expect(isServerGame(clientGame)).toBe(false); + }); + it("should convert server to client game", () => { const serverGame: ServerGame = { theme: "default",