refactor to react/vite/hono/drizzle/postgresql stack
replace vanilla TS + Bun.serve() + MariaDB with: - frontend: React 19, Vite 6, TanStack Router/Query, Tailwind v4 - backend: Hono + @hono/node-server, Drizzle ORM, PostgreSQL, Zod - shared: algorithm, round-state, types in src/shared/ - tooling: Biome (lint/format), Vitest (tests) - deploy: static files at /movie-select/, API at /movie-select/api URL scheme changes from /movies/ to /movie-select/, API from action-based to RESTful endpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
32
tests/shared/algorithm.test.ts
Normal file
32
tests/shared/algorithm.test.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { decideMovie, paretoFilter } from "../../src/shared/algorithm.ts";
|
||||
|
||||
test("paretoFilter removes dominated movies", () => {
|
||||
const movies = ["A", "B"];
|
||||
const people = ["P1", "P2"];
|
||||
const ratings = { P1: { A: 1, B: 3 }, P2: { A: 2, B: 4 } };
|
||||
expect(paretoFilter(movies, people, ratings)).toEqual(["B"]);
|
||||
});
|
||||
|
||||
test("nash protects against hard no", () => {
|
||||
const movies = ["Consensus", "Polarizing"];
|
||||
const people = ["A", "B", "C"];
|
||||
const ratings = {
|
||||
A: { Consensus: 3, Polarizing: 5 },
|
||||
B: { Consensus: 3, Polarizing: 5 },
|
||||
C: { Consensus: 3, Polarizing: 0 },
|
||||
};
|
||||
const result = decideMovie({ movies, people, ratings });
|
||||
expect(result.winner.movie).toBe("Consensus");
|
||||
});
|
||||
|
||||
test("tie-breaker uses alphabetical order", () => {
|
||||
const movies = ["Alpha", "Beta"];
|
||||
const people = ["A", "B"];
|
||||
const ratings = {
|
||||
A: { Alpha: 2, Beta: 2 },
|
||||
B: { Alpha: 2, Beta: 2 },
|
||||
};
|
||||
const result = decideMovie({ movies, people, ratings });
|
||||
expect(result.winner.movie).toBe("Alpha");
|
||||
});
|
||||
25
tests/shared/round-state.test.ts
Normal file
25
tests/shared/round-state.test.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { expect, test } from "vitest";
|
||||
import {
|
||||
allUsersDone,
|
||||
collectCompleteRatings,
|
||||
hasCompleteRatings,
|
||||
} from "../../src/shared/round-state.ts";
|
||||
|
||||
test("allUsersDone returns true when all done", () => {
|
||||
expect(allUsersDone(["A", "B"], ["A", "B"])).toBe(true);
|
||||
expect(allUsersDone(["A", "B"], ["A"])).toBe(false);
|
||||
expect(allUsersDone([], [])).toBe(false);
|
||||
});
|
||||
|
||||
test("hasCompleteRatings detects missing ratings", () => {
|
||||
expect(hasCompleteRatings(["M1", "M2"], { M1: 2, M2: 5 })).toBe(true);
|
||||
expect(hasCompleteRatings(["M1", "M2"], { M1: 2 })).toBe(false);
|
||||
});
|
||||
|
||||
test("collectCompleteRatings filters incomplete voters", () => {
|
||||
const result = collectCompleteRatings(["A", "B"], ["M1", "M2"], {
|
||||
A: { M1: 1, M2: 2 },
|
||||
B: { M1: 3 },
|
||||
});
|
||||
expect(result).toEqual({ A: { M1: 1, M2: 2 } });
|
||||
});
|
||||
Reference in New Issue
Block a user