Files
gitea-axi/test/packaging/tarball.test.ts
alexion ccc8dbe998
All checks were successful
CI / test (22) (pull_request) Successful in 1m8s
CI / test (true, 24) (pull_request) Successful in 1m12s
CI / test (22) (push) Successful in 47s
CI / test (true, 24) (push) Successful in 1m4s
ci: matrix over Node 22 and 24, add the bench and packaging tiers (task 0040)
The workflow pinned Node 20, which reached end-of-life in April 2026, while
the manifest promised support down to it — so the entire claimed range below
the single tested version went unverified and its floor was unsupported.

Matrix over the two supported long-term-support majors and narrow the engine
range to `^22 || ^24`, naming exactly what is tested. Narrowing is free now
because nothing has been published and no tags exist.

The benchmark harness tier joins every leg: it is deterministic and needs no
network, and its non-default runner configuration made it easy to believe it
was running when it was not. The end-to-end and packaging tiers run on the
highest leg only, conditioned on a flag attached to that leg through a matrix
`include` entry rather than on a version number restated at each site. The
benchmark smoke tier stays out, since it would pass by skipping.

`@types/node` follows the new floor; it was the last Node 20 reference in the
manifest, and the typecheck runs on every leg.
2026-07-19 23:45:55 -04:00

89 lines
3.6 KiB
TypeScript

import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { extractTarball, packTarball, projectRoot } from "./npm-artifact.js";
// The shape of the packed npm tarball and the manifest that ships inside it.
// These assertions are npm-specific by nature — no other distribution method
// produces a tarball or a packed manifest — so they stay separate from the
// installed-binary assertions, which any installation method can drive.
let workDir: string;
/** The extracted tarball's root — npm nests everything under `package/`. */
let packageDir: string;
/** The manifest as it ships inside the packed tarball. */
let packedManifest: Record<string, unknown>;
beforeAll(() => {
workDir = mkdtempSync(join(tmpdir(), "gitea-axi-pack-"));
const tarball = packTarball(workDir);
packageDir = extractTarball(tarball, workDir);
packedManifest = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8")) as Record<
string,
unknown
>;
}, 300_000);
afterAll(() => {
if (workDir) {
rmSync(workDir, { recursive: true, force: true });
}
});
describe("npm distribution artifact", () => {
it("bundles the built CLI, the bin entry, and the Agent Skill, and declares no postinstall", () => {
expect(existsSync(join(packageDir, "dist", "main.js"))).toBe(true);
expect(existsSync(join(packageDir, "skills", "gitea-axi", "SKILL.md"))).toBe(true);
const bin = packedManifest.bin as Record<string, string> | undefined;
expect(bin?.["gitea-axi"]).toBe("dist/main.js");
const scripts = packedManifest.scripts as Record<string, string> | undefined;
expect(scripts?.postinstall).toBeUndefined();
});
it("excludes the bench/ harness directory from the package", () => {
expect(existsSync(join(packageDir, "bench"))).toBe(false);
});
it("declares complete metadata: unscoped name, description, repo, license, engines, ESM type", () => {
const name = packedManifest.name as string;
expect(name).toBe("gitea-axi");
expect(name).not.toContain("@");
expect(name).not.toContain("/");
const description = packedManifest.description as string;
expect(typeof description).toBe("string");
expect(description.length).toBeGreaterThan(0);
const repository = packedManifest.repository as string | { url?: string } | undefined;
const repositoryUrl = typeof repository === "string" ? repository : repository?.url;
expect(repositoryUrl).toContain("gitea-axi");
expect(packedManifest.license).toBe("MIT");
// The declared range names exactly the majors continuous integration
// matrixes over, so nothing is promised that is never tested. Node 20 is
// end-of-life and deliberately no longer named.
const engines = packedManifest.engines as Record<string, string> | undefined;
expect(engines?.node).toMatch(/22/);
expect(engines?.node).toMatch(/24/);
expect(engines?.node).not.toMatch(/20/);
expect(packedManifest.type).toBe("module");
});
it("scripts and documents the publish flow so publishing is a single command", () => {
const scripts = packedManifest.scripts as Record<string, string> | undefined;
expect(scripts?.prepack).toBe("npm run build");
const publishConfig = packedManifest.publishConfig as Record<string, string> | undefined;
expect(publishConfig?.access).toBe("public");
const publishingDoc = join(projectRoot, "PUBLISHING.md");
expect(existsSync(publishingDoc)).toBe(true);
expect(readFileSync(publishingDoc, "utf8")).toMatch(/npm publish/);
});
});