Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

.pi/skills/modernize/scripts/audit.test.mjs

Raw
import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import test from "node:test";
import { checkedSpawn } from "./audit.bench.mjs";
import {
	assertLatest,
	collectPackageVersions,
	compareVersions,
	createReport,
	extractCandidates,
	extractCapabilities,
	parseChangelog,
	resolvePiInstallation,
} from "./audit.mjs";

const changelog = `# Changelog

## [2.0.0] - 2026-07-13

### Added

- Added harmless sparkle output.

### Changed

- Renamed \`oldCall()\` to \`newCall()\`.

### Fixed

- Removed the obsolete \`legacyOption\` request field.
- Fixed ordinary rendering noise.

## [1.5.0] - 2026-07-01

### Breaking Changes

- Replace \`alpha()\` with \`beta()\`.

## [1.0.0] - 2026-06-01

### Added

- Initial release.
`;

test("parses release sections newest first", () => {
	assert.deepEqual(
		parseChangelog(changelog).map(({ version }) => version),
		["2.0.0", "1.5.0", "1.0.0"],
	);
});

test("extracts migration sections and keyword bullets in the version interval", () => {
	assert.deepEqual(
		extractCandidates(changelog, {
			afterVersion: "1.5.0",
			throughVersion: "2.0.0",
		}).map(({ section, text }) => [section, text]),
		[
			["Changed", "Renamed `oldCall()` to `newCall()`."],
			["Fixed", "Removed the obsolete `legacyOption` request field."],
		],
	);
});

const capabilitiesChangelog = `# Changelog

## [1.2.0]

### Added

- Added a future capability.

## [1.1.0]

### New Features

- Program status reporting through OSC 7501: working, blocked, done, or failed.

### Added

- Added \`+name\` and \`-name\` entries to \`--tools\`.

### Changed

- Status reports never contain prompts or model output; desktop notifications remain separate.

### Fixed

- Fixed ordinary rendering noise.

## [1.0.4]

### Fixed

- Fixed system prompt rules naming hidden tools; \`BuildSystemPromptOptions\` gains \`hiddenTools\`.

## [1.0.2]

### Added

- Added \`samplingParamsByThinkingLevel\`.

## [1.0.1]

### Added

- Added an older capability.
`;

test("collects native features and Fixed API additions without migration keywords", () => {
	const capabilities = extractCapabilities(capabilitiesChangelog, {
		afterVersion: "1.0.2",
		throughVersion: "1.1.0",
	});
	assert.deepEqual(
		capabilities.map(({ section }) => section),
		["New Features", "Added", "Changed", "Fixed", "Fixed", "Added"],
	);
	for (const shape of [
		"OSC 7501",
		"+name",
		"hiddenTools",
		"samplingParamsByThinkingLevel",
	])
		assert.ok(
			capabilities.some(({ text }) => text.includes(shape)),
			shape,
		);
	assert.ok(
		capabilities.some(({ text }) => text.includes("ordinary rendering noise")),
	);
});

test("includes the repository-resolved release but excludes older and future capabilities", () => {
	const capabilities = extractCapabilities(capabilitiesChangelog, {
		afterVersion: "1.0.2",
		throughVersion: "1.1.0",
	});
	assert.deepEqual(
		[...new Set(capabilities.map(({ version }) => version))],
		["1.1.0", "1.0.4", "1.0.2"],
	);
	assert.deepEqual(
		extractCapabilities(capabilitiesChangelog, {
			afterVersion: "1.1.0",
			throughVersion: "1.1.0",
		}).map(({ section }) => section),
		["New Features", "Added", "Changed", "Fixed"],
	);
});

test("keeps partial-replacement evidence despite observable behavior differences", () => {
	const capabilities = extractCapabilities(capabilitiesChangelog, {
		afterVersion: "1.0.2",
		throughVersion: "1.1.0",
	});
	assert.ok(
		capabilities.some(({ text }) =>
			text.includes("desktop notifications remain separate"),
		),
	);
});

test("rejects changelogs without release sections", () => {
	assert.throws(() => parseChangelog("# Changelog\n\nNothing yet."), /release/);
});

test("compares semantic versions numerically", () => {
	assert.equal(compareVersions("0.80.10", "0.80.6"), 1);
	assert.equal(compareVersions("0.80.6", "0.80.6"), 0);
	assert.equal(compareVersions("0.79.9", "0.80.1"), -1);
});

test("fails when installed Pi is not latest", () => {
	assert.doesNotThrow(() => assertLatest("0.80.6", "0.80.6"));
	assert.throws(() => assertLatest("0.80.5", "0.80.6"), /not latest/);
});

test("resolves the installed Pi package from launcher and CLI paths", (context) => {
	const directory = mkdtempSync(path.join(tmpdir(), "pi-modernize-"));
	context.after(() => rmSync(directory, { recursive: true, force: true }));
	const packageRoot = path.join(
		directory,
		"node_modules",
		"@earendil-works",
		"pi-coding-agent",
	);
	const cli = path.join(packageRoot, "dist", "cli.js");
	const changelog = path.join(packageRoot, "CHANGELOG.md");
	const launcher = path.join(directory, "pi.CMD");
	mkdirSync(path.dirname(cli), { recursive: true });
	writeFileSync(
		path.join(packageRoot, "package.json"),
		JSON.stringify({
			name: "@earendil-works/pi-coding-agent",
			version: "0.85.0",
			bin: { pi: "dist/cli.js" },
		}),
	);
	writeFileSync(cli, "");
	writeFileSync(changelog, "");
	writeFileSync(launcher, "");

	const expected = { cli, changelog, version: "0.85.0" };
	assert.deepEqual(resolvePiInstallation(launcher, ""), expected);
	assert.deepEqual(resolvePiInstallation(cli, ""), expected);
});

test("fails when no installed Pi package is near the launcher", (context) => {
	const directory = mkdtempSync(path.join(tmpdir(), "pi-modernize-"));
	context.after(() => rmSync(directory, { recursive: true, force: true }));
	const launcher = path.join(directory, "pi");
	writeFileSync(launcher, "");

	assert.throws(
		() => resolvePiInstallation(launcher, ""),
		/installed @earendil-works\/pi-coding-agent package not found/,
	);
});

function managedInstallation(context) {
	const directory = mkdtempSync(path.join(tmpdir(), "pi-modernize-"));
	context.after(() => rmSync(directory, { recursive: true, force: true }));
	const installRoot = path.join(directory, "install");
	const launcher = path.join(directory, "bin", "pi.CMD");
	const packageRoot = path.join(
		installRoot,
		"releases",
		"0.85.0",
		"node_modules",
		"@earendil-works",
		"pi-coding-agent",
	);
	const cli = path.join(packageRoot, "dist", "cli.js");
	const changelog = path.join(packageRoot, "CHANGELOG.md");
	mkdirSync(path.dirname(launcher), { recursive: true });
	mkdirSync(path.dirname(cli), { recursive: true });
	writeFileSync(launcher, "");
	writeFileSync(path.join(installRoot, "current-version"), "0.85.0\n");
	writeFileSync(
		path.join(packageRoot, "package.json"),
		JSON.stringify({
			name: "@earendil-works/pi-coding-agent",
			version: "0.85.0",
			bin: { pi: "dist/cli.js" },
		}),
	);
	writeFileSync(cli, "");
	writeFileSync(changelog, "");
	return {
		installRoot,
		launcher,
		packageRoot,
		expected: { cli, changelog, version: "0.85.0" },
	};
}

test("resolves the selected managed release from the launcher layout", (context) => {
	const { launcher, expected } = managedInstallation(context);
	assert.deepEqual(resolvePiInstallation(launcher, ""), expected);
});

test("explicit managed root takes precedence over executable proximity", (context) => {
	const { installRoot, expected } = managedInstallation(context);
	assert.deepEqual(
		resolvePiInstallation(process.execPath, installRoot),
		expected,
	);
});

test("rejects invalid managed version pointers without falling back", (context) => {
	const { installRoot, launcher } = managedInstallation(context);
	for (const version of ["", ".", "..", "../outside", "a/b", "a\\b"]) {
		writeFileSync(path.join(installRoot, "current-version"), version);
		assert.throws(
			() => resolvePiInstallation(launcher, installRoot),
			/version file is invalid/,
		);
	}
});

test("fails for missing managed pointers and releases", (context) => {
	const { installRoot, launcher } = managedInstallation(context);
	writeFileSync(path.join(installRoot, "current-version"), "0.86.0");
	assert.throws(
		() => resolvePiInstallation(launcher, installRoot),
		/package not found in managed release/,
	);
	rmSync(path.join(installRoot, "current-version"));
	assert.throws(() => resolvePiInstallation(launcher, installRoot), /ENOENT/);
});

test("rejects managed release metadata mismatching the pointer", (context) => {
	const { installRoot, launcher, packageRoot } = managedInstallation(context);
	writeFileSync(
		path.join(packageRoot, "package.json"),
		JSON.stringify({
			name: "@earendil-works/pi-coding-agent",
			version: "0.84.0",
		}),
	);
	assert.throws(
		() => resolvePiInstallation(launcher, installRoot),
		/does not match package version/,
	);
});

test("reports inherited-stdio child failures by exit code", () => {
	assert.throws(
		() =>
			checkedSpawn(process.execPath, ["-e", "process.exit(7)"], {
				stdio: "inherit",
			}),
		/exited with code 7/,
	);
});

test("collects resolved earendil Pi package versions", () => {
	assert.deepEqual(
		collectPackageVersions({
			packages: {
				"node_modules/@earendil-works/pi-ai": { version: "0.80.4" },
				"extensions/x/node_modules/@earendil-works/pi-ai": {
					version: "0.80.4",
				},
				"node_modules/@earendil-works/pi-coding-agent": {
					version: "0.80.6",
				},
				"node_modules/@earendil-works/pi-coding-agent/node_modules/partial-json":
					{
						version: "0.1.7",
					},
				"node_modules/vitest": { version: "4.1.9" },
			},
		}),
		[
			{ name: "@earendil-works/pi-ai", version: "0.80.4" },
			{ name: "@earendil-works/pi-coding-agent", version: "0.80.6" },
		],
	);
});

test("reports only evidence needed before tool-driven inspection", () => {
	assert.deepEqual(
		createReport({
			piVersion: "2.0.0",
			candidatesAfterVersion: "1.5.0",
			localChangelogPath: "/pi/CHANGELOG.md",
			remoteChangelogUrl: "https://example.test/CHANGELOG.md",
			candidates: [{ version: "2.0.0", text: "Replace alpha." }],
			capabilities: [{ version: "1.5.0", text: "Added native status." }],
		}),
		{
			schemaVersion: 3,
			status: "ok",
			piVersion: "2.0.0",
			candidatesAfterVersion: "1.5.0",
			changelogs: {
				installed: "/pi/CHANGELOG.md",
				remote: "https://example.test/CHANGELOG.md",
			},
			candidates: [{ version: "2.0.0", text: "Replace alpha." }],
			capabilities: [{ version: "1.5.0", text: "Added native status." }],
		},
	);
});