Browse Source

Merge branch 'worktree-agent-af8473cf'

User 2 tháng trước cách đây
mục cha
commit
3adac8f1de

+ 47 - 0
src/__tests__/movies/genre-filter.test.ts

@@ -0,0 +1,47 @@
+import { renderHook, act } from "@testing-library/react";
+import { useGenreFilter } from "@/hooks/use-genre-filter";
+
+describe("useGenreFilter", () => {
+  it("initial state has selectedGenre === null", () => {
+    const { result } = renderHook(() => useGenreFilter());
+    expect(result.current.selectedGenre).toBeNull();
+  });
+
+  it("filterByGenre sets selectedGenre", () => {
+    const { result } = renderHook(() => useGenreFilter());
+
+    act(() => {
+      result.current.filterByGenre("Action");
+    });
+
+    expect(result.current.selectedGenre).toBe("Action");
+  });
+
+  it("filterByGenre toggles back to null when called with same genre", () => {
+    const { result } = renderHook(() => useGenreFilter());
+
+    act(() => {
+      result.current.filterByGenre("Action");
+    });
+    expect(result.current.selectedGenre).toBe("Action");
+
+    act(() => {
+      result.current.filterByGenre("Action");
+    });
+    expect(result.current.selectedGenre).toBeNull();
+  });
+
+  it("clearGenreFilter resets to null", () => {
+    const { result } = renderHook(() => useGenreFilter());
+
+    act(() => {
+      result.current.filterByGenre("Comedy");
+    });
+    expect(result.current.selectedGenre).toBe("Comedy");
+
+    act(() => {
+      result.current.clearGenreFilter();
+    });
+    expect(result.current.selectedGenre).toBeNull();
+  });
+});

+ 31 - 0
src/__tests__/movies/query-keys.test.ts

@@ -0,0 +1,31 @@
+import { readFileSync } from "fs";
+import path from "path";
+
+describe("Query key consistency", () => {
+  const hooksDir = path.resolve(__dirname, "../../hooks");
+
+  it("useGroupMovies uses group-movies key", () => {
+    const src = readFileSync(path.join(hooksDir, "use-group-movies.ts"), "utf-8");
+    expect(src).toContain('"group-movies"');
+  });
+
+  it("useAddMovie invalidates group-movies key", () => {
+    const src = readFileSync(path.join(hooksDir, "use-add-movie.ts"), "utf-8");
+    expect(src).toContain('"group-movies"');
+  });
+
+  it("useDeleteMovie invalidates group-movies key", () => {
+    const src = readFileSync(path.join(hooksDir, "use-delete-movie.ts"), "utf-8");
+    expect(src).toContain('"group-movies"');
+  });
+
+  it("useToggleWatched invalidates group-movies key", () => {
+    const src = readFileSync(path.join(hooksDir, "use-toggle-watched.ts"), "utf-8");
+    expect(src).toContain('"group-movies"');
+  });
+
+  it("useRealtimeMovies uses group-movies key", () => {
+    const src = readFileSync(path.join(hooksDir, "use-realtime-movies.ts"), "utf-8");
+    expect(src).toContain('"group-movies"');
+  });
+});

+ 40 - 0
src/__tests__/movies/realtime-cache.test.ts

@@ -0,0 +1,40 @@
+import { readFileSync } from "fs";
+import path from "path";
+
+describe("Realtime movies cache update logic", () => {
+  const src = readFileSync(
+    path.resolve(__dirname, "../../hooks/use-realtime-movies.ts"),
+    "utf-8",
+  );
+
+  it("handles INSERT events with duplicate guard", () => {
+    expect(src).toContain("INSERT");
+    // Verify duplicate check exists to prevent optimistic update collisions
+    expect(src).toMatch(/old\.some\(.*\.id\s*===\s*newMovie\.id/);
+  });
+
+  it("handles UPDATE events with map replacement", () => {
+    expect(src).toContain("UPDATE");
+    // Verify map-based replacement pattern
+    expect(src).toMatch(/old\.map\(/);
+  });
+
+  it("handles DELETE events with filter removal", () => {
+    expect(src).toContain("DELETE");
+    // Verify filter-based removal pattern
+    expect(src).toMatch(/old\.filter\(/);
+  });
+
+  it("guards against null old data in setQueryData callbacks", () => {
+    // Each callback should handle the case where old cache data is undefined/null
+    const setQueryDataBlocks = src.match(/setQueryData.*?\(.*?old.*?\)/gs);
+    expect(setQueryDataBlocks).not.toBeNull();
+    expect(src).toMatch(/if\s*\(\s*!old\s*\)/);
+  });
+
+  it("uses moviesQueryKey helper for consistent key generation", () => {
+    expect(src).toMatch(/function\s+moviesQueryKey/);
+    // Verify the helper is used in the callback
+    expect(src).toMatch(/const\s+key\s*=\s*moviesQueryKey\(/);
+  });
+});

+ 14 - 0
vitest.config.ts

@@ -0,0 +1,14 @@
+import { defineConfig } from "vitest/config";
+import path from "path";
+
+export default defineConfig({
+  test: {
+    environment: "jsdom",
+    globals: true,
+  },
+  resolve: {
+    alias: {
+      "@": path.resolve(__dirname, "src"),
+    },
+  },
+});