← back to Homesonspec

apps/workers/src/snapshot-path.test.ts

79 lines

import { mkdtempSync, mkdirSync, writeFileSync, readFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { isAbsolute, join } from "node:path";
import { describe, expect, it } from "vitest";
import { resolveSnapshotPath, snapshotExt, snapshotRelativePath } from "./snapshot-path";

describe("snapshotExt", () => {
  it("json content-type → .json", () => {
    expect(snapshotExt("application/json")).toBe(".json");
    expect(snapshotExt("text/json; charset=utf-8")).toBe(".json");
  });
  it("anything else (incl. undefined) → .html", () => {
    expect(snapshotExt("text/html")).toBe(".html");
    expect(snapshotExt(undefined)).toBe(".html");
    expect(snapshotExt("")).toBe(".html");
  });
});

describe("snapshotRelativePath", () => {
  it("builds sourceKey/hash.ext for json vs html", () => {
    expect(snapshotRelativePath("dr-horton-site", "abc123", "application/json")).toBe(
      "dr-horton-site/abc123.json",
    );
    expect(snapshotRelativePath("dr-horton-site", "abc123", "text/html")).toBe(
      "dr-horton-site/abc123.html",
    );
  });
  it("is POSIX-relative (never absolute)", () => {
    expect(isAbsolute(snapshotRelativePath("k", "h", "text/html"))).toBe(false);
  });
});

describe("resolveSnapshotPath", () => {
  it("absolute storagePath passes through unchanged (legacy row)", () => {
    const abs = "/root/Projects/homesonspec/var/snapshots/k/h.html";
    expect(resolveSnapshotPath(abs)).toBe(abs);
    expect(resolveSnapshotPath(abs, "/mnt/vol/snapshots")).toBe(abs);
  });
  it("relative storagePath joins the default snapshot dir", () => {
    expect(resolveSnapshotPath("k/h.html")).toMatch(/\/var\/snapshots\/k\/h\.html$/);
  });
  it("relative storagePath joins a custom snapshot dir", () => {
    expect(resolveSnapshotPath("k/h.html", "/mnt/vol/snapshots")).toBe(
      "/mnt/vol/snapshots/k/h.html",
    );
  });
  it("strips a leading var/snapshots/ (and ./var/snapshots/) mirror-prefix tail", () => {
    expect(resolveSnapshotPath("var/snapshots/k/h.html", "/mnt/vol/snapshots")).toBe(
      "/mnt/vol/snapshots/k/h.html",
    );
    expect(resolveSnapshotPath("./var/snapshots/k/h.html", "/mnt/vol/snapshots")).toBe(
      "/mnt/vol/snapshots/k/h.html",
    );
  });
  it("rejects a relative path that escapes the configured snapshot root", () => {
    expect(() => resolveSnapshotPath("../../outside.html", "/mnt/vol/snapshots")).toThrow(
      /escapes snapshot root/,
    );
  });
});

describe("round-trip: write to disk, store relative, resolve, read back", () => {
  it("returns identical bytes", () => {
    const snapshotDir = mkdtempSync(join(tmpdir(), "hos-snap-"));
    const sourceKey = "example-source";
    const contentHash = "deadbeefcafef00d";
    const body = Buffer.from(`<html><body>${contentHash}</body></html>`);

    // write the file where the pipeline would (snapshotDir/sourceKey/hash.ext)
    mkdirSync(join(snapshotDir, sourceKey), { recursive: true });
    const rel = snapshotRelativePath(sourceKey, contentHash, "text/html");
    writeFileSync(join(snapshotDir, rel), body);

    // the DB stores the relative path; the reader resolves it back
    const resolved = resolveSnapshotPath(rel, snapshotDir);
    expect(readFileSync(resolved)).toEqual(body);
  });
});