216 lines
4.3 KiB
TypeScript
216 lines
4.3 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
import { mkdtempSync, writeFileSync, mkdirSync, rmSync } from "fs";
|
|
import { join } from "path";
|
|
import { tmpdir } from "os";
|
|
import { execSync } from "child_process";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const projectRoot = fileURLToPath(new URL("..", import.meta.url));
|
|
|
|
function runCli(
|
|
args: string,
|
|
cwd: string,
|
|
): { stdout: string; stderr: string; exitCode: number } {
|
|
try {
|
|
const stdout = execSync(
|
|
`npx tsx ${join(projectRoot, "src/cli/cli.ts")} ${args}`,
|
|
{
|
|
cwd,
|
|
encoding: "utf8",
|
|
stdio: ["pipe", "pipe", "pipe"],
|
|
},
|
|
);
|
|
return { stdout: stdout.trim(), stderr: "", exitCode: 0 };
|
|
} catch (err: any) {
|
|
return {
|
|
stdout: err.stdout?.toString().trim() || "",
|
|
stderr: err.stderr?.toString().trim() || "",
|
|
exitCode: err.status || 1,
|
|
};
|
|
}
|
|
}
|
|
|
|
describe("cli context", () => {
|
|
let dir: string;
|
|
|
|
beforeEach(() => {
|
|
dir = mkdtempSync(join(tmpdir(), "pi-map-cli-"));
|
|
});
|
|
|
|
afterEach(() => {
|
|
rmSync(dir, { recursive: true });
|
|
});
|
|
|
|
it("context command returns a bundle for a matching query", () => {
|
|
mkdirSync(join(dir, "src"));
|
|
mkdirSync(join(dir, "src", "auth"));
|
|
writeFileSync(
|
|
join(dir, "src", "auth", "tokens.ts"),
|
|
`export function validateToken() {}\n`,
|
|
);
|
|
writeFileSync(
|
|
join(dir, "src", "auth", ".pi-map.md"),
|
|
`# src/auth
|
|
dir: src/auth
|
|
index: src/auth/.pi-map.index.md
|
|
## role
|
|
Authentication and token validation.
|
|
## files
|
|
- tokens.ts | Token validation | exp: validateToken | dep: -
|
|
## arch
|
|
Guard pattern.
|
|
## tags
|
|
auth, token, validate
|
|
## symbols
|
|
validateToken
|
|
## workflows
|
|
- validate token
|
|
files: tokens.ts
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
writeFileSync(
|
|
join(dir, "src", "auth", ".pi-map.index.md"),
|
|
`# src/auth (index)
|
|
dir: src/auth
|
|
## role
|
|
Auth layer.
|
|
## parent
|
|
index: ./.pi-map.index.md
|
|
map: ./.pi-map.md
|
|
## children
|
|
-
|
|
## files
|
|
- tokens.ts
|
|
## links
|
|
index: src/auth/.pi-map.index.md
|
|
map: src/auth/.pi-map.md
|
|
## workflows
|
|
- validate token
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
|
|
const { stdout, exitCode } = runCli('context "token validation"', dir);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toContain("# Context bundle: token validation");
|
|
expect(stdout).toContain("## relevant indexes");
|
|
expect(stdout).toContain("src/auth/.pi-map.index.md");
|
|
expect(stdout).toContain("## relevant maps");
|
|
expect(stdout).toContain("src/auth/.pi-map.md");
|
|
expect(stdout).toContain("## likely files");
|
|
expect(stdout).toContain("src/auth/tokens.ts");
|
|
expect(stdout).toContain("## instructions");
|
|
});
|
|
|
|
it("context command omits symbols section when no symbols exist", () => {
|
|
mkdirSync(join(dir, "src"));
|
|
writeFileSync(
|
|
join(dir, "src", ".pi-map.md"),
|
|
`# src
|
|
dir: src
|
|
index: src/.pi-map.index.md
|
|
## role
|
|
Core source.
|
|
## files
|
|
- index.ts | Entry | exp: main | dep: -
|
|
## arch
|
|
Entrypoint.
|
|
## tags
|
|
-
|
|
## symbols
|
|
-
|
|
## workflows
|
|
-
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
writeFileSync(
|
|
join(dir, "src", ".pi-map.index.md"),
|
|
`# src (index)
|
|
dir: src
|
|
## role
|
|
Core source.
|
|
## parent
|
|
-
|
|
## children
|
|
-
|
|
## files
|
|
- index.ts
|
|
## links
|
|
index: src/.pi-map.index.md
|
|
map: src/.pi-map.md
|
|
## workflows
|
|
-
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
|
|
const { stdout, exitCode } = runCli('context "core source"', dir);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toContain("# Context bundle: core source");
|
|
expect(stdout).not.toContain("## relevant symbols");
|
|
});
|
|
|
|
it("context command returns no-results bundle when nothing matches", () => {
|
|
mkdirSync(join(dir, "src"));
|
|
writeFileSync(
|
|
join(dir, "src", ".pi-map.md"),
|
|
`# src
|
|
dir: src
|
|
index: src/.pi-map.index.md
|
|
## role
|
|
Core source.
|
|
## files
|
|
- index.ts | Entry | exp: main | dep: -
|
|
## arch
|
|
Entrypoint.
|
|
## tags
|
|
-
|
|
## symbols
|
|
-
|
|
## workflows
|
|
-
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
writeFileSync(
|
|
join(dir, "src", ".pi-map.index.md"),
|
|
`# src (index)
|
|
dir: src
|
|
## role
|
|
Core source.
|
|
## parent
|
|
-
|
|
## children
|
|
-
|
|
## files
|
|
- index.ts
|
|
## links
|
|
index: src/.pi-map.index.md
|
|
map: src/.pi-map.md
|
|
## workflows
|
|
-
|
|
## dirty
|
|
-
|
|
`,
|
|
);
|
|
|
|
const { stdout, exitCode } = runCli('context "zzzzzzzz"', dir);
|
|
expect(exitCode).toBe(0);
|
|
expect(stdout).toContain("# Context bundle: zzzzzzzz");
|
|
expect(stdout).toContain("No relevant directories found");
|
|
});
|
|
|
|
it("context command errors when query is missing", () => {
|
|
const { stderr, exitCode } = runCli("context", dir);
|
|
expect(exitCode).toBe(1);
|
|
expect(stderr).toContain("Missing query");
|
|
});
|
|
});
|