hivecast-sdk/packages/cli/tests/integration/fp30-install-uninstall-list-info-validate.spec.ts

168 lines
5.1 KiB
TypeScript
Raw Normal View History

import { afterEach, describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
describe('FP-30 mx lifecycle commands', () => {
const tempDirs: string[] = [];
function createLocalPackage(rootDir: string): { packageDir: string; packageName: string } {
const packageName = '@acme/weather-actor';
const packageDir = path.join(rootDir, 'weather-actor');
fs.mkdirSync(path.join(packageDir, 'src'), { recursive: true });
fs.writeFileSync(
path.join(packageDir, 'matrix.json'),
JSON.stringify(
{
name: packageName,
version: '1.0.0',
runtime: { language: 'typescript', entry: 'src/index.ts' },
components: [
{
type: 'WeatherActor',
export: 'WeatherActor',
mount: 'weather.actor',
autoStart: true,
},
],
permissions: { fsPolicy: 'none' },
},
null,
2
),
'utf8'
);
fs.writeFileSync(path.join(packageDir, 'package.json'), JSON.stringify({ name: packageName, version: '1.0.0' }, null, 2));
fs.writeFileSync(path.join(packageDir, 'src', 'index.ts'), 'export class WeatherActor {}\n', 'utf8');
return { packageDir, packageName };
}
async function loadMxCli(): Promise<{ createProgram: () => import('commander').Command }> {
const mxCliUrl = new URL('../../src/index.ts', import.meta.url).href;
return await import(`${mxCliUrl}?test=${Date.now()}-${Math.random().toString(16).slice(2)}`);
}
async function runMxCli(argv: string[]): Promise<string[]> {
const logs: string[] = [];
const originalLog = console.log;
const originalExit = process.exit;
const { createProgram } = await loadMxCli();
console.log = (...args: unknown[]) => {
logs.push(args.map((arg) => String(arg)).join(' '));
};
(process as unknown as { exit: (code?: number) => never }).exit = ((code?: number) => {
throw new Error(`process.exit(${code ?? 0})`);
}) as never;
try {
const program = createProgram();
await program.parseAsync(argv);
return logs;
} finally {
console.log = originalLog;
(process as unknown as { exit: typeof process.exit }).exit = originalExit;
}
}
function parseLastJson(logs: string[]): unknown {
for (let i = logs.length - 1; i >= 0; i--) {
const value = logs[i].trim();
if (!value) continue;
try {
return JSON.parse(value);
} catch {
// Continue scanning log lines until JSON is found.
}
}
throw new Error(`No JSON payload found in logs:\n${logs.join('\n')}`);
}
afterEach(() => {
while (tempDirs.length > 0) {
const dir = tempDirs.pop();
if (dir) {
fs.rmSync(dir, { recursive: true, force: true });
}
}
});
it('installs, lists, inspects, validates, and uninstalls local packages', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-lifecycle-'));
tempDirs.push(root);
const cwd = path.join(root, 'workspace');
const packagesDir = path.join(cwd, '.matrix', 'packages');
fs.mkdirSync(cwd, { recursive: true });
const { packageDir, packageName } = createLocalPackage(root);
await runMxCli([
'node',
'mx',
'install',
packageDir,
'--packages-dir',
packagesDir,
'--json',
]);
const installedPath = path.join(packagesDir, 'node_modules', '@acme', 'weather-actor');
assert.equal(fs.existsSync(installedPath), true);
const listedAfterInstall = parseLastJson(await runMxCli([
'node',
'mx',
'list',
'--packages-dir',
packagesDir,
'--json',
])) as string[];
assert.equal(Array.isArray(listedAfterInstall), true);
assert.equal(listedAfterInstall.includes(packageName), true);
const infoPayload = parseLastJson(await runMxCli([
'node',
'mx',
'info',
packageName,
'--packages-dir',
packagesDir,
'--json',
])) as { packageName: string; installPath: string };
assert.equal(infoPayload.packageName, packageName);
assert.equal(path.resolve(infoPayload.installPath), path.resolve(installedPath));
const validatePayload = parseLastJson(await runMxCli([
'node',
'mx',
'validate',
path.join(packageDir, 'matrix.json'),
'--json',
])) as { packageName: string; valid: boolean };
assert.equal(validatePayload.packageName, packageName);
assert.equal(validatePayload.valid, true);
await runMxCli([
'node',
'mx',
'uninstall',
packageName,
'--packages-dir',
packagesDir,
'--json',
]);
assert.equal(fs.existsSync(installedPath), false);
const listedAfterUninstall = parseLastJson(await runMxCli([
'node',
'mx',
'list',
'--packages-dir',
packagesDir,
'--json',
])) as string[];
assert.equal(Array.isArray(listedAfterUninstall), true);
assert.equal(listedAfterUninstall.includes(packageName), false);
});
});