chore: strengthen package test task
This commit is contained in:
parent
d9348f43a0
commit
632c34d120
15
package.json
15
package.json
@ -18,17 +18,22 @@
|
||||
"bin/",
|
||||
"dist/",
|
||||
"skills/",
|
||||
"scripts/build.mjs",
|
||||
"scripts/check-package-grammars.mjs",
|
||||
"scripts/package-smoke.mjs",
|
||||
"scripts/test-all.mjs",
|
||||
"LICENSE",
|
||||
"CHANGELOG.md"
|
||||
],
|
||||
"scripts": {
|
||||
"prepare": "[ -d .git ] && ./scripts/install-hooks.sh || true",
|
||||
"build": "tsc -p tsconfig.build.json && printf '#!/usr/bin/env node\n' | cat - dist/cli/qmd.js > dist/cli/qmd.tmp && mv dist/cli/qmd.tmp dist/cli/qmd.js && chmod +x dist/cli/qmd.js",
|
||||
"test": "bun run test:unit",
|
||||
"test:node": "node ./node_modules/vitest/vitest.mjs run --reporter=verbose",
|
||||
"test:bun": "bun test --preload ./src/test-preload.ts",
|
||||
"test:unit": "bun run test:node -- test/ && bun run test:bun -- test/",
|
||||
"build": "node scripts/build.mjs",
|
||||
"test": "node scripts/test-all.mjs",
|
||||
"test:types": "node ./node_modules/typescript/bin/tsc -p tsconfig.build.json --noEmit",
|
||||
"test:node": "node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000",
|
||||
"test:bun": "bun test --timeout 60000 --preload ./src/test-preload.ts",
|
||||
"test:unit": "CI=true node ./node_modules/vitest/vitest.mjs run --reporter=verbose --testTimeout 60000 test/ && CI=true bun test --timeout 60000 --preload ./src/test-preload.ts test/",
|
||||
"test:package": "node scripts/package-smoke.mjs",
|
||||
"qmd": "tsx src/cli/qmd.ts",
|
||||
"index": "tsx src/cli/qmd.ts index",
|
||||
"vector": "tsx src/cli/qmd.ts vector",
|
||||
|
||||
29
scripts/build.mjs
Normal file
29
scripts/build.mjs
Normal file
@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { chmodSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = join(fileURLToPath(new URL("..", import.meta.url)));
|
||||
|
||||
function run(command, args, options = {}) {
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: root,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
...options,
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
run(process.execPath, [join(root, "node_modules", "typescript", "bin", "tsc"), "-p", "tsconfig.build.json"]);
|
||||
|
||||
const cliPath = join(root, "dist", "cli", "qmd.js");
|
||||
const tmpPath = `${cliPath}.tmp`;
|
||||
const built = readFileSync(cliPath, "utf8");
|
||||
const withoutExistingShebang = built.startsWith("#!") ? built.slice(built.indexOf("\n") + 1) : built;
|
||||
writeFileSync(tmpPath, `#!/usr/bin/env node\n${withoutExistingShebang}`);
|
||||
renameSync(tmpPath, cliPath);
|
||||
chmodSync(cliPath, 0o755);
|
||||
65
scripts/package-smoke.mjs
Normal file
65
scripts/package-smoke.mjs
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = fileURLToPath(new URL("..", import.meta.url));
|
||||
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
||||
|
||||
function run(label, command, args, options = {}) {
|
||||
console.log(`==> ${label}`);
|
||||
const { quiet, ...spawnOptions } = options;
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: root,
|
||||
stdio: quiet ? "pipe" : "inherit",
|
||||
shell: process.platform === "win32",
|
||||
...spawnOptions,
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
console.error(`Package smoke failed: ${label}`);
|
||||
if (quiet) {
|
||||
if (result.stdout) process.stderr.write(result.stdout);
|
||||
if (result.stderr) process.stderr.write(result.stderr);
|
||||
}
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
function assertPath(path, label = path) {
|
||||
const full = join(root, path);
|
||||
if (!existsSync(full)) {
|
||||
console.error(`Package smoke failed: missing ${label} (${path})`);
|
||||
process.exit(1);
|
||||
}
|
||||
return full;
|
||||
}
|
||||
|
||||
run("build compiled package", process.execPath, ["scripts/build.mjs"]);
|
||||
run("AST grammar runtime packages", process.execPath, ["scripts/check-package-grammars.mjs"]);
|
||||
|
||||
for (const entry of pkg.files ?? []) {
|
||||
assertPath(entry.replace(/\/$/, ""), `package.json files[] entry ${entry}`);
|
||||
}
|
||||
|
||||
for (const [name, binPath] of Object.entries(pkg.bin ?? {})) {
|
||||
const full = assertPath(binPath, `bin ${name}`);
|
||||
const mode = statSync(full).mode;
|
||||
if ((mode & 0o111) === 0) {
|
||||
console.error(`Package smoke failed: bin ${name} is not executable (${binPath})`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
assertPath("dist/index.js", "compiled main export");
|
||||
assertPath("dist/index.d.ts", "compiled type export");
|
||||
assertPath("dist/cli/qmd.js", "compiled CLI");
|
||||
|
||||
run("compiled CLI under Node", process.execPath, ["dist/cli/qmd.js", "--help"], { quiet: true });
|
||||
run("package wrapper", "sh", ["bin/qmd", "--help"], { quiet: true });
|
||||
|
||||
if (process.env.QMD_SKIP_BUN_SMOKE === "1") {
|
||||
console.log("==> compiled CLI under Bun (skipped by QMD_SKIP_BUN_SMOKE=1)");
|
||||
} else {
|
||||
run("compiled CLI under Bun", "bun", ["dist/cli/qmd.js", "--help"], { quiet: true });
|
||||
}
|
||||
27
scripts/test-all.mjs
Normal file
27
scripts/test-all.mjs
Normal file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const root = fileURLToPath(new URL("..", import.meta.url));
|
||||
|
||||
function run(label, command, args, options = {}) {
|
||||
console.log(`==> ${label}`);
|
||||
const { env: extraEnv, ...spawnOptions } = options;
|
||||
const result = spawnSync(command, args, {
|
||||
cwd: root,
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
env: { ...process.env, ...(extraEnv ?? {}) },
|
||||
...spawnOptions,
|
||||
});
|
||||
if (result.status !== 0) {
|
||||
console.error(`Test task failed: ${label}`);
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
}
|
||||
|
||||
run("TypeScript build typecheck", process.execPath, [join(root, "node_modules", "typescript", "bin", "tsc"), "-p", "tsconfig.build.json", "--noEmit"]);
|
||||
run("Vitest suite under Node", process.execPath, [join(root, "node_modules", "vitest", "vitest.mjs"), "run", "--reporter=verbose", "--testTimeout", "60000", "test/"], { env: { CI: "true" } });
|
||||
run("Bun test suite", "bun", ["test", "--timeout", "60000", "--preload", "./src/test-preload.ts", "test/"], { env: { CI: "true" } });
|
||||
run("Package smoke", process.execPath, ["scripts/package-smoke.mjs"]);
|
||||
@ -9,14 +9,14 @@ const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||
|
||||
describe("Node ESM entrypoints", () => {
|
||||
test("CLI --index path normalizes via setIndexName/setConfigIndexName under Node 22+", () => {
|
||||
execFileSync("npm", ["run", "build"], {
|
||||
execFileSync(process.execPath, ["scripts/build.mjs"], {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
});
|
||||
|
||||
const indexPath = join(mkdtempSync(join(tmpdir(), "qmd-index-")), "nested", "idx");
|
||||
const output = execFileSync("node", ["dist/cli/qmd.js", "--index", indexPath, "--version"], {
|
||||
const output = execFileSync(process.execPath, ["dist/cli/qmd.js", "--index", indexPath, "--version"], {
|
||||
cwd: repoRoot,
|
||||
encoding: "utf-8",
|
||||
stdio: "pipe",
|
||||
|
||||
@ -5,6 +5,32 @@ import { join } from "node:path";
|
||||
const root = new URL("..", import.meta.url);
|
||||
const pkg = JSON.parse(readFileSync(new URL("package.json", root), "utf8"));
|
||||
|
||||
describe("package test task", () => {
|
||||
test("runs typecheck, unit tests, and package smoke checks", () => {
|
||||
expect(pkg.scripts.test).toContain("scripts/test-all.mjs");
|
||||
|
||||
expect(pkg.scripts["test:types"]).toContain("tsconfig.build.json --noEmit");
|
||||
expect(pkg.scripts["test:unit"]).toContain("vitest.mjs");
|
||||
expect(pkg.scripts["test:unit"]).toContain("bun test");
|
||||
expect(pkg.scripts["test:unit"]).toContain("CI=true");
|
||||
|
||||
expect(pkg.scripts["test:package"]).toContain("scripts/package-smoke.mjs");
|
||||
|
||||
const testAllScript = readFileSync(new URL("scripts/test-all.mjs", root), "utf8");
|
||||
expect(testAllScript).toContain("TypeScript build typecheck");
|
||||
expect(testAllScript).toContain("Vitest suite under Node");
|
||||
expect(testAllScript).toContain("Bun test suite");
|
||||
expect(testAllScript).toContain("Package smoke");
|
||||
|
||||
const packageSmokeScript = readFileSync(new URL("scripts/package-smoke.mjs", root), "utf8");
|
||||
expect(packageSmokeScript).toContain("scripts/build.mjs");
|
||||
expect(packageSmokeScript).toContain("scripts/check-package-grammars.mjs");
|
||||
expect(packageSmokeScript).toContain("compiled CLI under Node");
|
||||
expect(packageSmokeScript).toContain("compiled CLI under Bun");
|
||||
expect(packageSmokeScript).toContain("package wrapper");
|
||||
});
|
||||
});
|
||||
|
||||
describe("package grammar distribution", () => {
|
||||
test("installs AST grammar wasm packages as required runtime dependencies", () => {
|
||||
for (const dep of ["tree-sitter-typescript", "tree-sitter-python", "tree-sitter-go", "tree-sitter-rust"]) {
|
||||
@ -17,7 +43,10 @@ describe("package grammar distribution", () => {
|
||||
expect(pkg.scripts, "package.json scripts").toHaveProperty("smoke:package-grammars");
|
||||
expect(String(pkg.scripts["smoke:package-grammars"])).toContain("check-package-grammars");
|
||||
|
||||
expect(pkg.files, "published package files").toContain("scripts/build.mjs");
|
||||
expect(pkg.files, "published package files").toContain("scripts/check-package-grammars.mjs");
|
||||
expect(pkg.files, "published package files").toContain("scripts/package-smoke.mjs");
|
||||
expect(pkg.files, "published package files").toContain("scripts/test-all.mjs");
|
||||
expect(pkg.files, "published package files").toContain("skills/");
|
||||
const qmdSkill = readFileSync(new URL("skills/qmd/SKILL.md", root), "utf8");
|
||||
expect(qmdSkill).toContain("# QMD - Query Markdown Documents");
|
||||
|
||||
Loading…
Reference in New Issue
Block a user