Ctrl K
ComunidadGuías…

test/shared/database-key-links.test.ts

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

Archivo original ↗Preguntar a KiBot
/** Direct tests for the signed links that finish a database key rotation: the
 *  second each link names, and the window it opens in. */

import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import { FakeTime } from "@std/testing/time";
import {
  DATABASE_KEY_PAUSE_SECONDS,
  databaseKeyIssueUrl,
  databaseKeyLinkStartOrNull,
} from "#shared/database-key-rotation.ts";
import { setupTestEncryptionKey } from "#test-utils/env.ts";

/** A subsecond start must receive the full pause. */
const START_MS = 1_700_000_000_499;

/** The pause a link waits out before it opens. */
const PAUSE_MS = DATABASE_KEY_PAUSE_SECONDS * 1000;

/** Seconds a rotation link stays open, counted from its start. */
const LINK_SECONDS = 600;

describe("database key rotation links", () => {
  /** The signed link's query fields for one start. */
  const signedLink = async (siteId = 7, started = START_MS) => {
    const params = new URL(
      `http://x${await databaseKeyIssueUrl(siteId, started)}`,
    ).searchParams;
    return { sig: params.get("sig"), start: params.get("start") };
  };

  /** The start one link names, through the completion route's check. */
  const namedStart = (
    siteId: number,
    link: { sig: string | null; start: string | null },
  ): Promise<number | null> =>
    databaseKeyLinkStartOrNull(siteId, link.start, link.sig);

  test("signs the site's own completion route with the second the rotation began", async () => {
    setupTestEncryptionKey();
    const url = await databaseKeyIssueUrl(7, START_MS);
    expect(
      url.startsWith("/admin/built-sites/7/rotate-database-key/complete?sig="),
    ).toBe(true);
    expect((await signedLink()).start).toBe(String(START_MS));
  });

  test("names its start once the pause has passed, for its own site", async () => {
    setupTestEncryptionKey();
    using time = new FakeTime(START_MS);
    const link = await signedLink();
    time.tick(PAUSE_MS);
    expect(await namedStart(7, link)).toBe(START_MS);
    expect(await namedStart(8, link)).toBeNull();
  });

  test("names no rotation before the pause ends", async () => {
    setupTestEncryptionKey();
    using time = new FakeTime(START_MS);
    const link = await signedLink();
    time.tick(PAUSE_MS - 1000);
    expect(await namedStart(7, link)).toBeNull();
    time.tick(1000);
    expect(await namedStart(7, link)).toBe(START_MS);
  });

  test("names no rotation after ten minutes", async () => {
    setupTestEncryptionKey();
    using time = new FakeTime(START_MS);
    const link = await signedLink();
    time.tick((LINK_SECONDS + 1) * 1000);
    expect(await namedStart(7, link)).toBeNull();
  });

  test("names no rotation when a field is missing", async () => {
    setupTestEncryptionKey();
    const { sig, start } = await signedLink();
    expect(await databaseKeyLinkStartOrNull(7, null, sig)).toBeNull();
    expect(await databaseKeyLinkStartOrNull(7, start, null)).toBeNull();
  });

  test("names no rotation for an altered link", async () => {
    setupTestEncryptionKey();
    using _time = new FakeTime(START_MS);
    const { sig, start } = await signedLink();
    expect(
      await databaseKeyLinkStartOrNull(7, String(START_MS + 1), sig),
    ).toBeNull();
    expect(await databaseKeyLinkStartOrNull(7, "notanumber", sig)).toBeNull();
    expect(await databaseKeyLinkStartOrNull(7, "0", sig)).toBeNull();
    expect(
      await databaseKeyLinkStartOrNull(7, start, `${sig ?? ""}XXXX`),
    ).toBeNull();
  });
});