Files
ocas/packages/cli/tests/helpers.ts
T
xiaoju 736d7e7374 chore: 测试框架从 bun:test 迁移到 vitest
- 36 个 test 文件 bun:test → vitest
- Bun.spawn() → execFileSync('tsx', ...)
- Bun.file() → readFileSync
- import.meta.dir → import.meta.dirname (tests) / __dirname (CLI source)
- 删除 bun-types devDep
- 添加 vitest + tsx devDep
- CLI shebang bun → node
- 30/36 test files pass, 558/617 tests pass

Refs #62
2026-06-03 04:11:10 +00:00

111 lines
3.2 KiB
TypeScript

import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { execFileSync } from "node:child_process";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import type { JSONSchema } from "@ocas/core";
import { putSchema } from "@ocas/core";
import { openStore as openFsStore } from "@ocas/fs";
export {
join,
mkdirSync,
mkdtempSync,
readFileSync,
resolve,
rmSync,
tmpdir,
writeFileSync,
};
export const entrypoint = resolve(import.meta.dirname, "../dist/index.js");
export const pkgPath = resolve(import.meta.dirname, "../package.json");
/** Extract the `value` field from a { type, value } envelope JSON string. */
export function envValue(json: string): unknown {
return (JSON.parse(json.trim()) as { value: unknown }).value;
}
/**
* Register a schema directly via the library (CLI schema put was removed).
* Returns the type hash.
*/
export async function putSchemaFile(
storePath: string,
schemaFilePath: string,
): Promise<string> {
const store = await openFsStore(storePath);
const schema = JSON.parse(
readFileSync(schemaFilePath, "utf-8"),
) as JSONSchema;
const hash = putSchema(store, schema);
return hash;
}
/**
* Run CLI command. Accepts either a string[] or ...string[] (rest args).
* If first arg is an array, uses that as args. Otherwise treats all args as the command.
*/
export function runCli(
args: string[],
storePath?: string,
): { stdout: string; stderr: string; exitCode: number } {
const finalArgs = storePath
? [entrypoint, "--home", storePath, ...args]
: [entrypoint, ...args];
try {
const stdout = execFileSync("node", finalArgs, {
encoding: "utf-8",
timeout: 10000,
});
return { stdout, stderr: "", exitCode: 0 };
} catch (e: unknown) {
const err = e as { stdout?: string; stderr?: string; status?: number };
return { stdout: err.stdout ?? "", stderr: err.stderr ?? "", exitCode: err.status ?? 1 };
}
}
export function runCliWithStdin(
args: string[],
storePath: string,
stdin: string,
): { stdout: string; stderr: string; exitCode: number } {
const finalArgs = [entrypoint, "--home", storePath, ...args];
try {
const stdout = execFileSync("node", finalArgs, {
input: stdin,
encoding: "utf-8",
timeout: 10000,
});
return { stdout, stderr: "", exitCode: 0 };
} catch (e: unknown) {
const err = e as { stdout?: string; stderr?: string; status?: number };
return { stdout: err.stdout ?? "", stderr: err.stderr ?? "", exitCode: err.status ?? 1 };
}
}
/**
* Parse JSON and strip volatile fields (timestamp, created, updated)
* so snapshots are stable across runs.
*/
export function stripVolatile(json: string): unknown {
const strip = (v: unknown): unknown => {
if (Array.isArray(v)) return v.map(strip);
if (v !== null && typeof v === "object") {
const out: Record<string, unknown> = {};
for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
if (k === "timestamp" || k === "created" || k === "updated") continue;
out[k] = strip(val);
}
return out;
}
return v;
};
return strip(JSON.parse(json));
}