74 lines
3.1 KiB
TypeScript
74 lines
3.1 KiB
TypeScript
/**
|
|
* Regression test for #1496 (bug 2): `omp install ./my-extension` used to be
|
|
* silently rewritten to `launch install ./my-extension` and forwarded to the
|
|
* LLM as an initial prompt because no top-level `install` subcommand existed.
|
|
*
|
|
* These tests pin two invariants:
|
|
*
|
|
* 1. `install` is registered in the CLI command table, so the runner does
|
|
* not prepend `launch` and the args never reach the model.
|
|
* 2. The local-path heuristic that routes `install ./foo` to `plugin link`
|
|
* while routing remote specs to `plugin install` is correct for the
|
|
* shapes users actually type.
|
|
*/
|
|
import { describe, expect, test } from "bun:test";
|
|
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { commands, isSubcommand, resolveCliArgv } from "@oh-my-pi/pi-coding-agent/cli-commands";
|
|
import { looksLikeLocalPath } from "@oh-my-pi/pi-coding-agent/commands/install";
|
|
import { removeSyncWithRetries } from "@oh-my-pi/pi-utils";
|
|
|
|
describe("install command is registered as a top-level subcommand", () => {
|
|
test("CLI runner sees `install` as a known command", () => {
|
|
expect(commands.some(c => c.name === "install")).toBe(true);
|
|
expect(isSubcommand("install")).toBe(true);
|
|
});
|
|
|
|
test("CLI runner rejects only bare reserved management words", () => {
|
|
expect(resolveCliArgv(["extensions"])).toEqual({
|
|
error: '`omp extensions` is not a management command. Use `omp plugin list` / `omp plugin install`, or run `omp launch extensions` if you meant to send "extensions" as a prompt.',
|
|
});
|
|
expect(resolveCliArgv(["extensions", "are", "not", "loading"])).toEqual({
|
|
argv: ["launch", "extensions", "are", "not", "loading"],
|
|
});
|
|
expect(resolveCliArgv(["launch", "extensions"])).toEqual({ argv: ["launch", "extensions"] });
|
|
});
|
|
});
|
|
|
|
describe("looksLikeLocalPath", () => {
|
|
test("explicit relative paths are local", () => {
|
|
expect(looksLikeLocalPath("./my-extension")).toBe(true);
|
|
expect(looksLikeLocalPath("../sibling")).toBe(true);
|
|
expect(looksLikeLocalPath(".")).toBe(true);
|
|
});
|
|
|
|
test("absolute and home-relative paths are local", () => {
|
|
expect(looksLikeLocalPath("/usr/local/share/ext")).toBe(true);
|
|
expect(looksLikeLocalPath("~/extensions/foo")).toBe(true);
|
|
});
|
|
|
|
test("Windows drive-prefixed paths are local", () => {
|
|
expect(looksLikeLocalPath("C:\\extensions\\foo")).toBe(true);
|
|
expect(looksLikeLocalPath("D:/extensions/foo")).toBe(true);
|
|
});
|
|
|
|
test("npm specs and marketplace refs are remote", () => {
|
|
expect(looksLikeLocalPath("@oh-my-pi/exa")).toBe(false);
|
|
expect(looksLikeLocalPath("my-pkg")).toBe(false);
|
|
expect(looksLikeLocalPath("my-pkg@1.2.3")).toBe(false);
|
|
expect(looksLikeLocalPath("name@marketplace")).toBe(false);
|
|
});
|
|
|
|
test("bare names that exist as a local directory are treated as local", () => {
|
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "omp-install-test-"));
|
|
try {
|
|
fs.mkdirSync(path.join(tempDir, "vendored-ext"));
|
|
expect(looksLikeLocalPath("vendored-ext", tempDir)).toBe(true);
|
|
expect(looksLikeLocalPath("missing-pkg", tempDir)).toBe(false);
|
|
} finally {
|
|
removeSyncWithRetries(tempDir);
|
|
}
|
|
});
|
|
});
|