From ad8a371be25acc5bd4f1875961a4f210c945baf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobi=20L=C3=BCtke?= Date: Sat, 16 May 2026 23:20:47 +0000 Subject: [PATCH] Fix QMD CI test runtime assumptions --- test/cli.test.ts | 47 +++++++++++++++++++++------------------ test/local-config.test.ts | 16 ++++++++++--- test/mcp.test.ts | 2 +- test/package.test.ts | 4 ++-- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/test/cli.test.ts b/test/cli.test.ts index ee9d5f5..4cbb7fb 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -27,14 +27,15 @@ let testCounter = 0; // Unique counter for each test run const thisDir = dirname(fileURLToPath(import.meta.url)); const projectRoot = join(thisDir, ".."); const qmdScript = join(projectRoot, "src", "cli", "qmd.ts"); -// Resolve tsx binary from project's node_modules (not cwd-dependent) -const tsxBin = (() => { - const candidate = join(projectRoot, "node_modules", ".bin", "tsx"); - if (existsSync(candidate)) { - return candidate; - } - return join(process.cwd(), "node_modules", ".bin", "tsx"); -})(); +const isBunRuntime = typeof (globalThis as { Bun?: unknown }).Bun !== "undefined"; +const tsxCli = join(projectRoot, "node_modules", "tsx", "dist", "cli.mjs"); +const qmdCommand = isBunRuntime + ? { command: process.execPath, args: [qmdScript] } + : { command: process.execPath, args: [tsxCli, qmdScript] }; + +function qmdRunnerArgs(args: string[]): { command: string; args: string[] } { + return { command: qmdCommand.command, args: [...qmdCommand.args, ...args] }; +} // Helper to run qmd command with test database async function runQmd( @@ -44,7 +45,8 @@ async function runQmd( const workingDir = options.cwd || fixturesDir; const dbPath = options.dbPath || testDbPath; const configDir = options.configDir || testConfigDir; - const proc = spawn(tsxBin, [qmdScript, ...args], { + const runner = qmdRunnerArgs(args); + const proc = spawn(runner.command, runner.args, { cwd: workingDir, env: { ...process.env, @@ -252,15 +254,15 @@ describe("CLI Skills", () => { expect(stderr).toBe(""); expect(exitCode).toBe(0); expect(stdout).toContain("qmd"); - expect(stdout).toContain("Search markdown knowledge bases"); + expect(stdout).toContain("Search local markdown knowledge bases"); }); test("gets version-matched runtime skill content", async () => { const { stdout, stderr, exitCode } = await runQmd(["skills", "get", "qmd"]); expect(stderr).toBe(""); expect(exitCode).toBe(0); - expect(stdout).toContain("# QMD - Quick Markdown Search"); - expect(stdout).toContain("## MCP: `query`"); + expect(stdout).toContain("# QMD - Query Markdown Documents"); + expect(stdout).toContain("## MCP Tool: `query`"); expect(stdout).not.toContain("This file is a discovery stub"); }); @@ -268,7 +270,7 @@ describe("CLI Skills", () => { const { stdout, stderr, exitCode } = await runQmd(["skills", "get", "qmd", "--full"]); expect(stderr).toBe(""); expect(exitCode).toBe(0); - expect(stdout).toContain("# QMD - Quick Markdown Search"); + expect(stdout).toContain("# QMD - Query Markdown Documents"); expect(stdout).toContain("--- references/mcp-setup.md ---"); expect(stdout).toContain("# QMD MCP Server Setup"); }); @@ -284,8 +286,8 @@ describe("CLI Skills", () => { const { stdout, stderr, exitCode } = await runQmd(["skill", "show"]); expect(stderr).toBe(""); expect(exitCode).toBe(0); - expect(stdout).toContain("# QMD - Quick Markdown Search"); - expect(stdout).toContain("## MCP: `query`"); + expect(stdout).toContain("# QMD - Query Markdown Documents"); + expect(stdout).toContain("## MCP Tool: `query`"); expect(stdout).not.toContain("This file is a discovery stub"); }); @@ -300,8 +302,8 @@ describe("CLI Skills", () => { const installedSkillDir = join(installDir, ".agents", "skills", "qmd"); const installed = readFileSync(join(installedSkillDir, "SKILL.md"), "utf8"); - expect(installed).toContain("# QMD - Quick Markdown Search"); - expect(installed).toContain("## MCP: `query`"); + expect(installed).toContain("# QMD - Query Markdown Documents"); + expect(installed).toContain("## MCP Tool: `query`"); expect(installed).not.toContain("This file is a discovery stub"); expect(readFileSync(join(installedSkillDir, "references", "mcp-setup.md"), "utf8")).toContain("# QMD MCP Server Setup"); }); @@ -370,7 +372,7 @@ describe("CLI Skill Commands", () => { expect(exitCode).toBe(0); const skillDir = join(projectDir, ".agents", "skills", "qmd"); - expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Quick Markdown Search"); + expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Query Markdown Documents"); expect(existsSync(join(projectDir, ".claude", "skills", "qmd"))).toBe(false); expect(stdout).toContain(`✓ Installed QMD skill to ${skillDir}`); expect(stdout).toContain("Tip: create a Claude symlink manually"); @@ -388,9 +390,9 @@ describe("CLI Skill Commands", () => { const skillDir = join(fakeHome, ".agents", "skills", "qmd"); const claudeLink = join(fakeHome, ".claude", "skills", "qmd"); - expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Quick Markdown Search"); + expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Query Markdown Documents"); expect(lstatSync(claudeLink).isSymbolicLink()).toBe(true); - expect(readFileSync(join(claudeLink, "SKILL.md"), "utf-8")).toContain("# QMD - Quick Markdown Search"); + expect(readFileSync(join(claudeLink, "SKILL.md"), "utf-8")).toContain("# QMD - Query Markdown Documents"); expect(stdout).toContain(`✓ Installed QMD skill to ${skillDir}`); expect(stdout).toContain(`✓ Linked Claude skill at ${claudeLink}`); }); @@ -408,7 +410,7 @@ describe("CLI Skill Commands", () => { const skillDir = join(fakeHome, ".agents", "skills", "qmd"); expect(lstatSync(skillDir).isSymbolicLink()).toBe(false); - expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Quick Markdown Search"); + expect(readFileSync(join(skillDir, "SKILL.md"), "utf-8")).toContain("# QMD - Query Markdown Documents"); expect(stdout).toContain(`✓ Claude already sees the skill via ${join(fakeHome, ".claude", "skills")}`); }); @@ -1580,7 +1582,8 @@ describe("mcp http daemon", () => { port: number, options: { args?: string[]; env?: Record } = {}, ): import("child_process").ChildProcess { - const proc = spawn(tsxBin, [qmdScript, ...(options.args ?? []), "mcp", "--http", "--port", String(port)], { + const runner = qmdRunnerArgs([...(options.args ?? []), "mcp", "--http", "--port", String(port)]); + const proc = spawn(runner.command, runner.args, { cwd: fixturesDir, env: { ...process.env, diff --git a/test/local-config.test.ts b/test/local-config.test.ts index ea2295f..8bc6bf0 100644 --- a/test/local-config.test.ts +++ b/test/local-config.test.ts @@ -5,6 +5,17 @@ import { tmpdir } from "node:os"; import { afterEach, describe, expect, test } from "vitest"; import { findLocalConfigPath, getLocalDbPath } from "../src/collections.js"; +function cliCommandArgs(command: string): { bin: string; args: string[] } { + const cliPath = join(process.cwd(), "src/cli/qmd.ts"); + if (process.versions.bun) { + return { bin: process.execPath, args: [cliPath, command] }; + } + return { + bin: process.execPath, + args: [join(process.cwd(), "node_modules/tsx/dist/cli.mjs"), cliPath, command], + }; +} + const roots: string[] = []; function tempProject(): string { @@ -59,9 +70,8 @@ describe("local .qmd project config", () => { writeFileSync(join(root, ".qmd", "index.yaml"), `collections:\n docs:\n path: ${JSON.stringify(join(root, "docs"))}\n pattern: "**/*.md"\n context:\n /: Local test docs\nmodels:\n embed: local-embed-model\n rerank: local-rerank-model\n generate: local-generate-model\n`); const home = join(root, "home"); - const tsxBin = join(process.cwd(), "node_modules", ".bin", "tsx"); - const runner = existsSync(tsxBin) ? tsxBin : "bun"; - const output = execFileSync(runner, [join(process.cwd(), "src/cli/qmd.ts"), "status"], { + const { bin, args } = cliCommandArgs("status"); + const output = execFileSync(bin, args, { cwd: root, encoding: "utf-8", env: { diff --git a/test/mcp.test.ts b/test/mcp.test.ts index 495c624..d0abeb7 100644 --- a/test/mcp.test.ts +++ b/test/mcp.test.ts @@ -186,7 +186,7 @@ function seedTestData(db: Database): void { for (let i = 0; i < 768; i++) embedding[i] = Math.random(); for (const doc of docs.slice(0, 4)) { // Skip large file for embeddings - db.prepare(`INSERT INTO content_vectors (hash, seq, pos, model, embedded_at) VALUES (?, 0, 0, 'embeddinggemma', ?)`).run(doc.hash, now); + db.prepare(`INSERT INTO content_vectors (hash, seq, pos, model, embedded_at) VALUES (?, 0, 0, ?, ?)`).run(doc.hash, DEFAULT_EMBED_MODEL, now); db.prepare(`INSERT INTO vectors_vec (hash_seq, embedding) VALUES (?, ?)`).run(`${doc.hash}_0`, embedding); } } diff --git a/test/package.test.ts b/test/package.test.ts index 7ba41ac..030d1aa 100644 --- a/test/package.test.ts +++ b/test/package.test.ts @@ -20,8 +20,8 @@ describe("package grammar distribution", () => { expect(pkg.files, "published package files").toContain("scripts/check-package-grammars.mjs"); expect(pkg.files, "published package files").toContain("skills/"); const qmdSkill = readFileSync(new URL("skills/qmd/SKILL.md", root), "utf8"); - expect(qmdSkill).toContain("# QMD - Quick Markdown Search"); - expect(qmdSkill).toContain("## MCP: `query`"); + expect(qmdSkill).toContain("# QMD - Query Markdown Documents"); + expect(qmdSkill).toContain("## MCP Tool: `query`"); expect(qmdSkill).not.toContain("This file is a discovery stub"); const scriptPath = join(root.pathname, "scripts", "check-package-grammars.mjs");