Ctrl K
ComunidadGuías…

test/scripts/stale-claim/taking.test.ts

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

Archivo original ↗Preguntar a KiBot
import { assert } from "@std/assert";
import { expect } from "@std/expect";
import { describe, it as test } from "@std/testing/bdd";
import { stub } from "@std/testing/mock";
import {
  ageFile,
  doNothing,
  expectClaimRefused,
  LONG_AGO_MS,
  withClaimDir,
  withTestClaim,
  writeClaim,
} from "#test/scripts/stale-claim/helpers.ts";

describe("taking a claim", () => {
  test("takes a free claim, writing who holds it and since when", async () => {
    await withClaimDir(async (path) => {
      const before = Date.now();
      await withTestClaim(path, async () => {
        const [owner, time] = (await Deno.readTextFile(path)).split("\n");
        // A random name and the time of the last touch, nothing else.
        expect(owner).toMatch(/^[0-9a-f-]{36}$/);
        expect(Number(time)).toBeGreaterThanOrEqual(before);
        expect(Number(time)).toBeLessThanOrEqual(Date.now());
      });
    });
  });

  test("refuses while somebody else's claim is fresh", async () => {
    await withClaimDir(async (path) => {
      await writeClaim(path, `someone-else\n${Date.now()}`);

      await expectClaimRefused(path);
    });
  });

  test("removes and takes over a claim whose owner walked away", async () => {
    await withClaimDir(async (path) => {
      await writeClaim(path, `someone-else\n${LONG_AGO_MS}`);

      await withTestClaim(path, async () => {
        assert(!(await Deno.readTextFile(path)).includes("someone-else"));
      });
    });
  });

  test("judges a claim by its file's age when it names no readable time", async () => {
    await withClaimDir(async (path) => {
      await writeClaim(path, "someone-else\nnot-a-time");
      await ageFile(path, LONG_AGO_MS);

      await withTestClaim(path, doNothing);
    });
  });

  test("keeps a fresh file's claim when its time has nothing after it", async () => {
    await withClaimDir(async (path) => {
      // A time with a newline and nothing else is not a record we ever write,
      // so the file's own age decides — and this file is new.
      await writeClaim(path, `${LONG_AGO_MS}\n`);

      await expectClaimRefused(path);
    });
  });

  test("takes over a claim naming a time from the very start of the clock", async () => {
    await withClaimDir(async (path) => {
      // The file was touched a moment ago, so only reading the time it
      // claims — 1970, however unlikely — can tell that it is abandoned.
      await writeClaim(path, "someone-else\n1");

      await withTestClaim(path, doNothing);
    });
  });

  test("leaves a freshly made claim alone while its time is unreadable", async () => {
    await withClaimDir(async (path) => {
      // A taker between creating the file and writing the record: the file is
      // moments old, so it is somebody's claim, not an abandoned one.
      await writeClaim(path, "");

      await expectClaimRefused(path);
    });
  });

  test("surfaces a claim file that cannot be made at all", async () => {
    await withClaimDir(async (path) => {
      // Only the claim file itself is unmakeable; the takers' guard beside
      // it opens fine, so the failure under test is the claim's own.
      const open = Deno.open;
      using _open = stub(Deno, "open", ((
        target: string | URL,
        options?: Deno.OpenOptions,
      ) =>
        `${target}` === path
          ? Promise.reject(new Deno.errors.PermissionDenied("no access"))
          : open(target, options)) as typeof Deno.open);

      await expect(withTestClaim(path, doNothing)).rejects.toThrow("no access");
    });
  });

  test("takes the claim when a stale one vanishes during the steal", async () => {
    await withClaimDir(async (path) => {
      await writeClaim(path, `someone-else\n${LONG_AGO_MS}`);

      const remove = Deno.remove;
      using _remove = stub(Deno, "remove", (async (
        target: string | URL,
        options?: Deno.RemoveOptions,
      ) => {
        // Another taker clears the stale claim first; ours must carry on.
        await remove(target, options).catch(() => {
          // Either taker can remove it; the throw below is the case tested.
        });
        throw new Deno.errors.NotFound("already gone");
      }) as typeof Deno.remove);

      await withTestClaim(path, doNothing);
    });
  });
});