diff --git a/test/packaging/build-context.test.ts b/test/packaging/build-context.test.ts new file mode 100644 index 0000000..9ffa5a1 --- /dev/null +++ b/test/packaging/build-context.test.ts @@ -0,0 +1,49 @@ +// The Docker build context is load-bearing in two directions, and both +// failures are silent. +// +// Excluding too little: a worktree left under `.claude/` is copied into the +// image, vitest globs its `test/` tree as well as the real one, and the +// containerised `make check` runs the whole suite twice over while reporting +// success. A compiled `bin/quak` is ~100 MB of context nobody needs. +// +// Excluding too much: Prettier 3 reads `.gitignore` as a default ignore file, +// so dropping it from the context silently changes which files +// `make fmt-check` looks at inside the image compared to the host. +// +// Neither shows up as a build failure, so they are asserted here. +import { describe, expect, it } from "vitest"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; + +const repoRoot = fileURLToPath(new URL("../../", import.meta.url)); + +const patterns = (name: string): string[] => + readFileSync(join(repoRoot, name), "utf-8") + .split("\n") + .map((line) => line.trim()) + .filter((line) => line !== "" && !line.startsWith("#")); + +const dockerignore = patterns(".dockerignore"); + +describe(".dockerignore", () => { + // Everything here is either generated, enormous, or secret. `.claude/` is + // the correctness one: see the header comment and issue #25. + it.each([ + ".claude/", + ".quak/", + "bin/quak", + "node_modules", + "coverage", + "dist", + ".vitest-cache/", + ".nyc_output/", + "*.tsbuildinfo", + ])("keeps %s out of the build context", (pattern) => { + expect(dockerignore).toContain(pattern); + }); + + it("leaves .gitignore in the build context for prettier", () => { + expect(dockerignore).not.toContain(".gitignore"); + }); +}); diff --git a/test/packaging/projectname.test.ts b/test/packaging/projectname.test.ts new file mode 100644 index 0000000..b93d1b4 --- /dev/null +++ b/test/packaging/projectname.test.ts @@ -0,0 +1,30 @@ +// `script/projectname` is the single source of the project's name for every +// script that needs one — `script/docker` builds its image tag from it, which +// is the whole reason that file exists. Nothing checked that it agreed with +// `package.json`, and after the repo was renamed it did not: the script still +// said "quack", so `make docker` produced an image tagged after a name this +// project has not used since May. +// +// The script is executed rather than read, because what matters is the string +// it prints, not the source it prints it from. +import { describe, expect, it } from "vitest"; +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { join } from "node:path"; + +const repoRoot = fileURLToPath(new URL("../../", import.meta.url)); + +const pkg = JSON.parse( + readFileSync(join(repoRoot, "package.json"), "utf-8"), +) as { name: string }; + +describe("script/projectname", () => { + it("prints the name package.json declares", () => { + const printed = execFileSync(join(repoRoot, "script/projectname"), { + cwd: repoRoot, + encoding: "utf-8", + }).trim(); + expect(printed).toBe(pkg.name); + }); +});