mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-16 11:52:26 +03:00
Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.
One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.
Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.
343 lines
11 KiB
TypeScript
343 lines
11 KiB
TypeScript
import test from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import os from "node:os";
|
|
|
|
// Dynamic import to pick up ESM module
|
|
const { parseCliRegistry, getCommandsForFamily } =
|
|
await import("../../src/lib/agentSkills/cliRegistryParser.ts");
|
|
|
|
// ─── Fixture helpers ──────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Creates a temporary directory mirroring bin/cli/commands/,
|
|
* writes fixture .mjs files, changes CWD, returns cleanup fn.
|
|
*/
|
|
function withFixtureCli(files: Record<string, string>): { cleanup: () => void } {
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-cli-test-"));
|
|
const commandsDir = path.join(tmpDir, "bin", "cli", "commands");
|
|
fs.mkdirSync(commandsDir, { recursive: true });
|
|
|
|
for (const [filename, content] of Object.entries(files)) {
|
|
fs.writeFileSync(path.join(commandsDir, filename), content, "utf-8");
|
|
}
|
|
|
|
const originalCwd = process.cwd();
|
|
process.chdir(tmpDir);
|
|
|
|
return {
|
|
cleanup() {
|
|
process.chdir(originalCwd);
|
|
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
},
|
|
};
|
|
}
|
|
|
|
// ─── Fixture content ──────────────────────────────────────────────────────────
|
|
|
|
const FIXTURE_PROVIDERS_MJS = `
|
|
export function registerProviders(program) {
|
|
const providers = program.command('providers').description('Manage provider connections');
|
|
|
|
providers
|
|
.command('list')
|
|
.description('List configured provider connections')
|
|
.option('--json', 'Print machine-readable JSON')
|
|
.action(async (opts) => {});
|
|
|
|
providers
|
|
.command('available')
|
|
.description('Show available providers in the catalog')
|
|
.option('--search <query>', 'Filter by id or name')
|
|
.option('--category <category>', 'Filter by category')
|
|
.action(async (opts) => {});
|
|
|
|
providers
|
|
.command('test <idOrName>')
|
|
.description('Test a configured provider connection')
|
|
.action(async (idOrName, opts) => {});
|
|
|
|
providers
|
|
.command('test-all')
|
|
.description('Test all active provider connections')
|
|
.action(async (opts) => {});
|
|
|
|
providers
|
|
.command('validate')
|
|
.description('Validate local provider configuration')
|
|
.action(async (opts) => {});
|
|
|
|
providers
|
|
.command('rotate <idOrName>')
|
|
.description('Rotate API key for a provider connection')
|
|
.option('--new-key <key>', 'New API key value')
|
|
.option('--dry-run', 'Preview without writing')
|
|
.action(async (idOrName, opts) => {});
|
|
|
|
providers
|
|
.command('status')
|
|
.description('Show provider connection status and expiry')
|
|
.option('--json', 'JSON output')
|
|
.action(async (opts) => {});
|
|
}
|
|
`;
|
|
|
|
const FIXTURE_HEALTH_MJS = `
|
|
export function registerHealth(program) {
|
|
const health = program
|
|
.command('health')
|
|
.description('Check server health status')
|
|
.option('-v, --verbose', 'Show extended info')
|
|
.option('--json', 'Output as JSON')
|
|
.action(async (opts) => {});
|
|
|
|
health
|
|
.command('components')
|
|
.description('List health components and status')
|
|
.action(async (opts) => {});
|
|
|
|
health
|
|
.command('watch')
|
|
.description('Live dashboard — refresh every N seconds')
|
|
.option('--interval <s>', 'Refresh interval in seconds')
|
|
.action(async (opts) => {});
|
|
}
|
|
`;
|
|
|
|
const FIXTURE_KEYS_MJS = `
|
|
export function registerKeys(program) {
|
|
const keys = program.command('keys').description('Manage OmniRoute API keys');
|
|
|
|
keys
|
|
.command('list')
|
|
.description('List all API keys')
|
|
.option('--json', 'JSON output')
|
|
.action(async (opts) => {});
|
|
|
|
keys
|
|
.command('create')
|
|
.description('Create a new API key')
|
|
.option('--name <name>', 'Key name')
|
|
.action(async (opts) => {});
|
|
|
|
keys
|
|
.command('revoke <id>')
|
|
.description('Revoke an API key')
|
|
.action(async (id, opts) => {});
|
|
}
|
|
`;
|
|
|
|
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
|
|
test("parseCliRegistry() returns commands Map and families Map", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
"health.mjs": FIXTURE_HEALTH_MJS,
|
|
});
|
|
try {
|
|
const result = parseCliRegistry();
|
|
assert.ok(result.commands instanceof Map, "commands should be a Map");
|
|
assert.ok(result.families instanceof Map, "families should be a Map");
|
|
assert.ok(result.commands.size > 0, "commands should not be empty");
|
|
assert.ok(result.families.size > 0, "families should not be empty");
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() recognises providers family with ≥5 subcommands", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
});
|
|
try {
|
|
const { families } = parseCliRegistry();
|
|
const providerCmds = families.get("cli-providers");
|
|
assert.ok(providerCmds, "Expected 'cli-providers' family to exist");
|
|
assert.ok(
|
|
providerCmds!.length >= 5,
|
|
`Expected ≥5 provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`
|
|
);
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() recognises health family commands", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"health.mjs": FIXTURE_HEALTH_MJS,
|
|
});
|
|
try {
|
|
const { families } = parseCliRegistry();
|
|
const healthCmds = families.get("cli-health");
|
|
assert.ok(healthCmds, "Expected 'cli-health' family to exist");
|
|
assert.ok(healthCmds!.length >= 2, `Expected ≥2 health commands, got ${healthCmds!.length}`);
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() extracts description for each command", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
"keys.mjs": FIXTURE_KEYS_MJS,
|
|
});
|
|
try {
|
|
const { commands } = parseCliRegistry();
|
|
// Top-level providers command should have description
|
|
const providers = [...commands.values()].find((c) => c.name === "providers");
|
|
assert.ok(providers, "Expected providers command");
|
|
assert.ok(
|
|
providers!.description.length > 0,
|
|
`Expected non-empty description for providers, got: "${providers!.description}"`
|
|
);
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() marks subcommands with isSubcommand=true (after first)", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
});
|
|
try {
|
|
const { families } = parseCliRegistry();
|
|
const providerCmds = families.get("cli-providers")!;
|
|
// After the first (top-level) entry, rest should be subcommands
|
|
const subCmds = providerCmds.filter((c) => c.isSubcommand);
|
|
assert.ok(
|
|
subCmds.length >= 4,
|
|
`Expected ≥4 subcommands (list, available, test, etc.), got ${subCmds.length}`
|
|
);
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() extracts flags from .option() calls", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
});
|
|
try {
|
|
const { commands } = parseCliRegistry();
|
|
// Find a command that has options
|
|
const rotate = [...commands.values()].find((c) => c.name.includes("rotate"));
|
|
// Flags might be present if parsing found them
|
|
if (rotate) {
|
|
// If rotate exists and has flags, verify format
|
|
for (const flag of rotate.flags) {
|
|
assert.ok(typeof flag === "string" && flag.length > 0, `Invalid flag: "${flag}"`);
|
|
}
|
|
}
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() does not mis-attribute child options to parent command when subcommand variable is created inline", () => {
|
|
const fixture = `
|
|
export function registerBackup(program) {
|
|
const backup = program.command("backup").description("Manage backups");
|
|
|
|
backup
|
|
.command("create")
|
|
.description("Create a backup")
|
|
.option("--name <name>", "Name")
|
|
.option("--cloud", "Cloud backup");
|
|
|
|
const auto = backup.command("auto").description("Auto backup");
|
|
|
|
auto
|
|
.command("enable")
|
|
.description("Enable auto backup")
|
|
.option("--cron <expr>", "Cron schedule");
|
|
|
|
backup
|
|
.command("status")
|
|
.description("Show status");
|
|
}
|
|
`;
|
|
const { cleanup } = withFixtureCli({
|
|
"backup.mjs": fixture,
|
|
});
|
|
try {
|
|
const { commands } = parseCliRegistry();
|
|
const backupCmd = commands.get("backup");
|
|
assert.ok(backupCmd, "backup command should exist");
|
|
assert.deepEqual(
|
|
backupCmd.flags,
|
|
[],
|
|
"parent backup command should not collect flags from nested subcommands"
|
|
);
|
|
|
|
const statusCmd = commands.get("backup status");
|
|
assert.ok(statusCmd, "backup status command should exist");
|
|
assert.deepEqual(
|
|
statusCmd.flags,
|
|
[],
|
|
"backup status should not collect flags from other subcommands"
|
|
);
|
|
|
|
const createCmd = commands.get("backup create");
|
|
assert.ok(createCmd, "backup create command should exist");
|
|
assert.deepEqual(createCmd.flags, ["--name <name>", "--cloud"]);
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() skips unrecognised .mjs files", () => {
|
|
const { cleanup } = withFixtureCli({
|
|
"unknown-custom.mjs": `export function register(p) {}`,
|
|
"providers.mjs": FIXTURE_PROVIDERS_MJS,
|
|
});
|
|
try {
|
|
const { families } = parseCliRegistry();
|
|
// No family should be mapped from unknown-custom
|
|
const hasUnknown = [...families.keys()].some((k) => String(k).includes("unknown-custom"));
|
|
assert.equal(hasUnknown, false, "unknown-custom.mjs should not create a family");
|
|
// providers.mjs should still be parsed
|
|
assert.ok(families.has("cli-providers"), "Expected cli-providers family from providers.mjs");
|
|
} finally {
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
test("parseCliRegistry() throws if commands directory is missing", () => {
|
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "omni-cli-missing-"));
|
|
const originalCwd = process.cwd();
|
|
process.chdir(tmpDir);
|
|
try {
|
|
assert.throws(
|
|
() => parseCliRegistry(),
|
|
/cliRegistryParser: could not read/,
|
|
"Expected error when commands dir is missing"
|
|
);
|
|
} finally {
|
|
process.chdir(originalCwd);
|
|
fs.rmSync(tmpDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
}
|
|
});
|
|
|
|
// ─── Integration test: real providers.mjs (always runs — it's in the repo) ───
|
|
|
|
test("parseCliRegistry() with real providers.mjs: providers family has ≥5 commands", () => {
|
|
// This test uses the actual project files (not a fixture).
|
|
// We rely on the CWD being the worktree root during `npm run test:unit`.
|
|
const result = parseCliRegistry();
|
|
const providerCmds = result.families.get("cli-providers");
|
|
assert.ok(providerCmds, "Expected cli-providers family from real providers.mjs");
|
|
assert.ok(
|
|
providerCmds!.length >= 5,
|
|
`Expected ≥5 real provider commands, got ${providerCmds!.length}: ${providerCmds!.map((c) => c.name).join(", ")}`
|
|
);
|
|
});
|
|
|
|
test("getCommandsForFamily('cli-providers') with real files: returns ≥5 strings", () => {
|
|
const commands = getCommandsForFamily("cli-providers");
|
|
assert.ok(commands.length >= 5, `Expected ≥5 cli-providers commands, got ${commands.length}`);
|
|
for (const cmd of commands) {
|
|
assert.ok(typeof cmd === "string" && cmd.length > 0, `Invalid command name: "${cmd}"`);
|
|
}
|
|
});
|