Ctrl K
ComunidadGuías…

frontend/src/hooks/usePdfPasswordTrial.test.tsx

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { describe, it, expect, beforeEach, vi } from "vitest";
import { act, renderHook, waitFor } from "@testing-library/react";
import * as db from "@/lib/localStore/db";
import * as crypt from "@/lib/localStore/crypto";
import * as vault from "@/lib/localStore/vault";
import { usePdfPasswordTrial } from "./usePdfPasswordTrial";

beforeEach(async () => {
  await db.destroy();
  crypt._resetForTests();
});

const fileOf = (bytes = [1, 2, 3]) =>
  new File([new Uint8Array(bytes)], "doc.pdf", { type: "application/pdf" });

const passwordError = () =>
  Object.assign(new Error("password required"), { name: "PasswordException" });

describe("usePdfPasswordTrial", () => {
  it("starts idle", () => {
    const { result } = renderHook(() => usePdfPasswordTrial());
    expect(result.current.state.status).toBe("idle");
  });

  it("reports notNeeded for an unencrypted file", async () => {
    const open = vi.fn().mockResolvedValue("ok");
    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    await act(async () => {
      await result.current.run(fileOf());
    });
    await waitFor(() => expect(result.current.state.status).toBe("notNeeded"));
  });

  it("reaches unlocked and exposes the matching password", async () => {
    await vault.addPassword("a", "pw");
    const open = vi.fn(async (_d: Uint8Array, password?: string) => {
      if (password !== "pw") throw passwordError();
      return "ok";
    });
    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    await act(async () => {
      await result.current.run(fileOf());
    });
    await waitFor(() => expect(result.current.state.status).toBe("unlocked"));
    if (result.current.state.status === "unlocked") {
      expect(result.current.state.password).toBe("pw");
      expect(result.current.state.tried).toBe(1);
    }
  });

  it("passes through a visible trying state", async () => {
    await vault.addPassword("a", "pw");
    const seen: string[] = [];
    let release: (() => void) | null = null;
    const gate = new Promise<void>((r) => {
      release = r;
    });
    const open = vi.fn(async (_d: Uint8Array, password?: string) => {
      await gate;
      if (password !== "pw") throw passwordError();
      return "ok";
    });

    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    let pending!: Promise<unknown>;
    act(() => {
      pending = result.current.run(fileOf());
    });
    await waitFor(() => seen.push(result.current.state.status));
    expect(result.current.state.status).toBe("trying");
    await act(async () => {
      release!();
      await pending;
    });
    expect(result.current.state.status).toBe("unlocked");
  });

  it("reports needed when nothing fits", async () => {
    await vault.addPassword("a", "nope");
    const open = vi.fn(async () => {
      throw passwordError();
    });
    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    await act(async () => {
      await result.current.run(fileOf());
    });
    await waitFor(() => expect(result.current.state.status).toBe("needed"));
  });

  it("reports an error for a corrupt file rather than prompting", async () => {
    const open = vi.fn(async () => {
      throw Object.assign(new Error("bad xref"), { name: "InvalidPDFException" });
    });
    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    await act(async () => {
      await result.current.run(fileOf());
    });
    await waitFor(() => expect(result.current.state.status).toBe("error"));
    if (result.current.state.status === "error") {
      expect(result.current.state.message).toMatch(/bad xref/);
    }
  });

  it("resets back to idle", async () => {
    const open = vi.fn().mockResolvedValue("ok");
    const { result } = renderHook(() => usePdfPasswordTrial(async () => open));
    await act(async () => {
      await result.current.run(fileOf());
    });
    act(() => result.current.reset());
    expect(result.current.state.status).toBe("idle");
  });
});