fix(lint): resolve all eslint errors across monorepo

- Type response bodies in gameController.test.ts to fix no-unsafe-member-access
- Replace async methods with Promise.resolve() in InMemoryGameSessionStore
  and InMemoryLobbyGameStore to satisfy require-await rule
- Add argsIgnorePattern and varsIgnorePattern to eslint config so
  underscore-prefixed params are globally ignored
- Fix no-misused-promises in ws/index.ts, lobbyHandlers, gameHandlers,
  __root.tsx, login.tsx and play.tsx by using void + .catch()
- Fix no-floating-promises on navigate calls in login.tsx
- Move API_URL outside Play component to fix useCallback dependency warning
- Type fetch response bodies in play.tsx to fix no-unsafe-assignment
- Add only-throw-error: off for route files (TanStack Router throw redirect)
- Remove unused WebSocket import from express.d.ts
- Fix unsafe return in connections.ts by typing empty Map constructor
- Exclude scripts/ folder from eslint
- Add targeted override for better-auth auth-client.ts (upstream typing issue)
This commit is contained in:
lila 2026-04-17 16:46:33 +02:00
parent a6d8ddec3b
commit ce19740cc8
12 changed files with 160 additions and 91 deletions

View file

@ -1,5 +1,11 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import request from "supertest";
import type { GameSession, AnswerResult } from "@lila/shared";
type SuccessResponse<T> = { success: true; data: T };
type ErrorResponse = { success: false; error: string };
type GameStartResponse = SuccessResponse<GameSession>;
type GameAnswerResponse = SuccessResponse<AnswerResult>;
vi.mock("@lila/db", () => ({ getGameTerms: vi.fn(), getDistractors: vi.fn() }));
@ -33,49 +39,48 @@ beforeEach(() => {
describe("POST /api/v1/game/start", () => {
it("returns 200 with a valid game session", async () => {
const res = await request(app).post("/api/v1/game/start").send(validBody);
const body = res.body as GameStartResponse;
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
expect(res.body.data.sessionId).toBeDefined();
expect(res.body.data.questions).toHaveLength(3);
expect(body.success).toBe(true);
expect(body.data.sessionId).toBeDefined();
expect(body.data.questions).toHaveLength(3);
});
it("returns 400 when the body is empty", async () => {
const res = await request(app).post("/api/v1/game/start").send({});
const body = res.body as ErrorResponse;
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(res.body.error).toBeDefined();
expect(body.success).toBe(false);
expect(body.error).toBeDefined();
});
it("returns 400 when required fields are missing", async () => {
const res = await request(app)
.post("/api/v1/game/start")
.send({ source_language: "en" });
const body = res.body as ErrorResponse;
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(body.success).toBe(false);
});
it("returns 400 when a field has an invalid value", async () => {
const res = await request(app)
.post("/api/v1/game/start")
.send({ ...validBody, difficulty: "impossible" });
const body = res.body as ErrorResponse;
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(body.success).toBe(false);
});
});
describe("POST /api/v1/game/answer", () => {
it("returns 200 with an answer result for a valid submission", async () => {
// Start a game first
const startRes = await request(app)
.post("/api/v1/game/start")
.send(validBody);
const { sessionId, questions } = startRes.body.data;
const question = questions[0];
const startBody = startRes.body as GameStartResponse;
const { sessionId, questions } = startBody.data;
const question = questions[0]!;
const res = await request(app)
.post("/api/v1/game/answer")
@ -84,20 +89,20 @@ describe("POST /api/v1/game/answer", () => {
questionId: question.questionId,
selectedOptionId: 0,
});
const body = res.body as GameAnswerResponse;
expect(res.status).toBe(200);
expect(res.body.success).toBe(true);
expect(res.body.data.questionId).toBe(question.questionId);
expect(typeof res.body.data.isCorrect).toBe("boolean");
expect(typeof res.body.data.correctOptionId).toBe("number");
expect(res.body.data.selectedOptionId).toBe(0);
expect(body.success).toBe(true);
expect(body.data.questionId).toBe(question.questionId);
expect(typeof body.data.isCorrect).toBe("boolean");
expect(typeof body.data.correctOptionId).toBe("number");
expect(body.data.selectedOptionId).toBe(0);
});
it("returns 400 when the body is empty", async () => {
const res = await request(app).post("/api/v1/game/answer").send({});
const body = res.body as ErrorResponse;
expect(res.status).toBe(400);
expect(res.body.success).toBe(false);
expect(body.success).toBe(false);
});
it("returns 404 when the session does not exist", async () => {
@ -108,18 +113,18 @@ describe("POST /api/v1/game/answer", () => {
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
});
const body = res.body as ErrorResponse;
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
expect(res.body.error).toContain("Game session not found");
expect(body.success).toBe(false);
expect(body.error).toContain("Game session not found");
});
it("returns 404 when the question does not exist in the session", async () => {
const startRes = await request(app)
.post("/api/v1/game/start")
.send(validBody);
const { sessionId } = startRes.body.data;
const startBody = startRes.body as GameStartResponse;
const { sessionId } = startBody.data;
const res = await request(app)
.post("/api/v1/game/answer")
@ -128,9 +133,9 @@ describe("POST /api/v1/game/answer", () => {
questionId: "00000000-0000-0000-0000-000000000000",
selectedOptionId: 0,
});
const body = res.body as ErrorResponse;
expect(res.status).toBe(404);
expect(res.body.success).toBe(false);
expect(res.body.error).toContain("Question not found");
expect(body.success).toBe(false);
expect(body.error).toContain("Question not found");
});
});