SPB Git

spb/autoscholar

Public
2.4 KB · 56 lines typescript
1import { mkdtemp, rm } from "node:fs/promises";2import { tmpdir } from "node:os";3import { join } from "node:path";4import { afterEach, beforeEach, describe, expect, it } from "vitest";5import { assertSafeKey, FilesystemStorage, sha256Hex } from "./index.js";67describe("FilesystemStorage", () => {8  let root: string;9  let store: FilesystemStorage;10  beforeEach(async () => {11    root = await mkdtemp(join(tmpdir(), "as-store-"));12    store = new FilesystemStorage(root);13  });14  afterEach(() => rm(root, { recursive: true, force: true }));1516  it("stores, hashes and reads back", async () => {17    const obj = await store.put("runs/x/a1/manuscript/main.tex", Buffer.from("hello"));18    expect(obj.sha256).toBe(sha256Hex("hello"));19    expect((await store.get("runs/x/a1/manuscript/main.tex")).toString()).toBe("hello");20    expect(await store.head("runs/x/a1/manuscript/main.tex")).toMatchObject({ bytes: 5 });21  });2223  it("is write-once and rejects different content under the same key", async () => {24    await store.put("k/a", Buffer.from("one"));25    await store.put("k/a", Buffer.from("one")); // idempotent26    await expect(store.put("k/a", Buffer.from("two"), sha256Hex("two"))).rejects.toThrow(/different digest/);27  });2829  it("rejects digest mismatch", async () => {30    await expect(store.put("k/b", Buffer.from("x"), "0".repeat(64))).rejects.toThrow(/digest mismatch/);31    expect(await store.exists("k/b")).toBe(false);32  });3334  it("rejects path traversal", () => {35    expect(() => assertSafeKey("../etc/passwd")).toThrow();36    expect(() => assertSafeKey("runs/../../x")).toThrow();37    expect(() => assertSafeKey("/abs")).toThrow();38    expect(() => store.absolutePath("runs/x/../../../y")).toThrow();39  });4041  it("accepts artifacts named *.sha256 next to their siblings (no sidecar collision)", async () => {42    await store.put("c/tool.c", Buffer.from("int main(){}"));43    const obj = await store.put("c/tool.c.sha256", Buffer.from("abc  tool.c\n"));44    expect(obj.bytes).toBe(12);45    expect((await store.get("c/tool.c")).toString()).toBe("int main(){}");46    expect((await store.head("c/tool.c"))?.sha256).toBe(sha256Hex("int main(){}"));47    expect(await store.list("c")).toEqual(["c/tool.c", "c/tool.c.sha256"]);48  });4950  it("lists keys under a prefix", async () => {51    await store.put("p/a.txt", Buffer.from("a"));52    await store.put("p/sub/b.txt", Buffer.from("b"));53    expect(await store.list("p")).toEqual(["p/a.txt", "p/sub/b.txt"]);54  });55});56
Files · main
Menu

Theme