|
|
@@ -0,0 +1,62 @@
|
|
|
+import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
+import { selectRandomMovie } from "@/lib/dice/randomizer";
|
|
|
+import type { Movie } from "@/types/movie";
|
|
|
+
|
|
|
+function makeMovie(id: string, watched = false): Movie {
|
|
|
+ return {
|
|
|
+ id,
|
|
|
+ group_id: "g1",
|
|
|
+ tmdb_id: Number(id) || 0,
|
|
|
+ title: `Movie ${id}`,
|
|
|
+ year: 2020,
|
|
|
+ poster_path: null,
|
|
|
+ genres: [],
|
|
|
+ trailer_url: null,
|
|
|
+ trailer_url_refreshed_at: null,
|
|
|
+ metadata_refreshed_at: null,
|
|
|
+ added_by: null,
|
|
|
+ watched,
|
|
|
+ watched_at: null,
|
|
|
+ added_at: "2026-01-01T00:00:00Z",
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+describe("selectRandomMovie", () => {
|
|
|
+ afterEach(() => {
|
|
|
+ vi.restoreAllMocks();
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns null for an empty pool", () => {
|
|
|
+ expect(selectRandomMovie([])).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns the only movie when the pool has one entry", () => {
|
|
|
+ const only = makeMovie("1");
|
|
|
+ expect(selectRandomMovie([only])).toBe(only);
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns null when every movie is watched", () => {
|
|
|
+ const pool = [makeMovie("1", true), makeMovie("2", true), makeMovie("3", true)];
|
|
|
+ expect(selectRandomMovie(pool)).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns the unwatched movie when only one remains unwatched", () => {
|
|
|
+ const survivor = makeMovie("2");
|
|
|
+ const pool = [makeMovie("1", true), survivor, makeMovie("3", true)];
|
|
|
+ expect(selectRandomMovie(pool)).toBe(survivor);
|
|
|
+ });
|
|
|
+
|
|
|
+ it("selects deterministically when crypto.getRandomValues is stubbed", () => {
|
|
|
+ const unwatched = [makeMovie("a"), makeMovie("b"), makeMovie("c"), makeMovie("d")];
|
|
|
+ const pool: Movie[] = [unwatched[0], makeMovie("x", true), unwatched[1], unwatched[2], unwatched[3]];
|
|
|
+
|
|
|
+ // Force index 2 within the unwatched subset (length 4): 6 % 4 === 2
|
|
|
+ const spy = vi.spyOn(crypto, "getRandomValues").mockImplementation((array) => {
|
|
|
+ (array as Uint32Array)[0] = 6;
|
|
|
+ return array;
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(selectRandomMovie(pool)).toBe(unwatched[2]);
|
|
|
+ expect(spy).toHaveBeenCalledTimes(1);
|
|
|
+ });
|
|
|
+});
|