1033 lines
34 KiB
TypeScript
Raw Permalink Normal View History

import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';
import * as fs from 'node:fs';
import * as net from 'node:net';
import * as os from 'node:os';
import * as path from 'node:path';
import { resolveNatsBinary } from '../../src/utils/runner-transport.js';
import { assertExitCode, parseLastJson, runMxCli } from '../helpers/cli-harness.js';
async function getAvailablePorts(count: number): Promise<number[]> {
const servers = await Promise.all(
Array.from({ length: count }, () => new Promise<net.Server>((resolve, reject) => {
const server = net.createServer();
server.once('error', reject);
server.listen(0, '127.0.0.1', () => resolve(server));
})),
);
const ports = servers.map((server) => {
const address = server.address();
assert.ok(address && typeof address === 'object');
return address.port;
});
await Promise.all(servers.map((server) => new Promise<void>((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
})));
return ports;
}
async function createServiceFixture(rootDir: string, options?: {
readonly conflictingTopLevelFields?: boolean;
readonly topLevelSecretsFile?: boolean;
readonly withEnvironment?: boolean;
}): Promise<string> {
const packageDir = path.join(rootDir, 'sample-service');
const runtimeDir = path.join(packageDir, 'dist', 'runtime');
fs.mkdirSync(runtimeDir, { recursive: true });
const conflictingTopLevelFields = options?.conflictingTopLevelFields === true;
const topLevelSecretsFile = options?.topLevelSecretsFile === true;
const withEnvironment = options?.withEnvironment === true;
const runtimeFactory = {
kind: 'factory',
export: 'createSampleService',
...(conflictingTopLevelFields ? {
mount: 'old.mount',
config: {
file: './config/old-config.json',
},
dependsOn: ['old.identity'],
policyRef: 'policies/old',
} : {}),
...(topLevelSecretsFile ? {
secrets: {
file: './config/manifest-secrets.json',
},
} : {}),
bootstrap: {
root: 'local.root',
transport: {
mode: 'local-memory',
},
http: {
enabled: false,
},
overrides: {
config: {
source: 'manifest',
nested: { enabled: false },
},
},
},
instance: {
packageName: '@acme/sample-service',
packageRevision: '1.0.0',
class: 'service',
rootKind: 'package-root',
mount: 'sample-service.local',
configRef: './config/service-config.json',
secretRefs: [
{
name: 'apiKey',
provider: 'system.factotum',
ref: 'secret://sample/api-key',
required: true,
},
],
dependsOn: ['system.identity'],
policyRef: 'policies/standalone',
autoStart: true,
},
};
fs.writeFileSync(
path.join(packageDir, 'package.json'),
JSON.stringify({
name: '@acme/sample-service',
version: '1.0.0',
type: 'module',
main: './dist/runtime/index.js',
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'matrix.json'),
JSON.stringify({
name: '@acme/sample-service',
version: '1.0.0',
namespace: 'sample',
root: {
type: 'SampleRoot',
export: 'SampleRoot',
mount: 'sample',
},
runtime: {
language: 'typescript',
entry: './dist/runtime/index.js',
factory: runtimeFactory,
},
components: [
{
type: 'SampleService',
export: 'SampleService',
mount: 'worker',
accepts: ['sample.run', 'sample.run'],
autoStart: true,
},
],
permissions: {
fsPolicy: 'none',
},
}, null, 2),
'utf8',
);
fs.mkdirSync(path.join(packageDir, 'config'), { recursive: true });
fs.writeFileSync(
path.join(packageDir, 'config', 'old-config.json'),
JSON.stringify({
config: {
old: true,
},
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'config', 'service-config.json'),
JSON.stringify({
config: {
fromFile: true,
nested: { file: true },
},
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'config', 'cli-config.json'),
JSON.stringify({
config: {
fromCliFile: true,
nested: { file: 'cli' },
},
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'config', 'cli-secrets.json'),
JSON.stringify([
{
name: 'overrideKey',
provider: 'system.factotum',
ref: 'secret://sample/override-key',
},
], null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'config', 'manifest-secrets.json'),
JSON.stringify([
{
name: 'manifestKey',
provider: 'system.factotum',
ref: 'secret://sample/manifest-key',
},
], null, 2),
'utf8',
);
fs.writeFileSync(
path.join(runtimeDir, 'index.js'),
`export async function createSampleService(overrides = {}, services = {}, _configResolution, bootstrapContext) {
return {
ok: true,
effectiveConfig: overrides,
bootContext: bootstrapContext,
runnerRuntimeProvided: Boolean(services.runtime),
runnerRuntimeRoot: services.runtime?.transport?.root ?? null,
runtime: {
async shutdown() {
return undefined;
}
}
};
}
`,
'utf8',
);
if (withEnvironment) {
const [natsPort, wsPort] = await getAvailablePorts(2);
fs.mkdirSync(path.join(packageDir, '.matrix'), { recursive: true });
fs.writeFileSync(
path.join(packageDir, '.matrix', 'dev.environment.json'),
JSON.stringify({
name: 'dev',
runtime: {
root: 'COM.TEST.PACKAGE-ENV',
runtimeId: 'sample-service-dev',
},
package: {
publicRoot: 'sample',
},
nats: {
mode: 'embedded',
port: natsPort,
wsPort,
binaryPath: resolveNatsBinary(process.cwd()),
},
}, null, 2),
'utf8',
);
}
return packageDir;
}
function createPackageRootFixture(rootDir: string): string {
const packageDir = path.join(rootDir, 'package-root-service');
const runtimeDir = path.join(packageDir, 'dist', 'runtime');
fs.mkdirSync(runtimeDir, { recursive: true });
fs.writeFileSync(
path.join(packageDir, 'package.json'),
JSON.stringify({
name: '@acme/package-root-service',
version: '2.0.0',
type: 'module',
main: './dist/runtime/index.js',
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'matrix.json'),
JSON.stringify({
name: '@acme/package-root-service',
version: '2.0.0',
runtime: {
language: 'typescript',
entry: './dist/runtime/index.js',
factory: {
kind: 'factory',
export: 'createPackageRootService',
bootstrap: {
overrides: {
mode: 'package-root',
},
},
instance: {
rootKind: 'package-root',
},
},
},
components: [
{
type: 'NestedService',
export: 'NestedService',
mount: 'nested-component-mount',
autoStart: true,
},
],
permissions: {
fsPolicy: 'none',
},
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(runtimeDir, 'index.js'),
`export async function createPackageRootService(overrides = {}, _services, _configResolution, bootstrapContext) {
return {
ok: true,
effectiveConfig: overrides,
bootContext: bootstrapContext,
runtime: {
async shutdown() {
return undefined;
}
}
};
}
`,
'utf8',
);
return packageDir;
}
function createComponentRootFixture(
rootDir: string,
options: {
name: string;
components: Array<{ type: string; mount?: string }>;
instance?: Record<string, unknown>;
},
): string {
const packageDir = path.join(rootDir, options.name);
const runtimeDir = path.join(packageDir, 'dist', 'runtime');
fs.mkdirSync(runtimeDir, { recursive: true });
fs.writeFileSync(
path.join(packageDir, 'package.json'),
JSON.stringify({
name: `@acme/${options.name}`,
version: '1.0.0',
type: 'module',
main: './dist/runtime/index.js',
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(packageDir, 'matrix.json'),
JSON.stringify({
name: `@acme/${options.name}`,
version: '1.0.0',
runtime: {
language: 'typescript',
entry: './dist/runtime/index.js',
factory: {
kind: 'factory',
export: 'createComponentRootService',
instance: {
rootKind: 'component',
...(options.instance ?? {}),
},
},
},
components: options.components.map((component) => ({
type: component.type,
export: component.type,
...(component.mount ? { mount: component.mount } : {}),
autoStart: false,
})),
permissions: {
fsPolicy: 'none',
},
}, null, 2),
'utf8',
);
fs.writeFileSync(
path.join(runtimeDir, 'index.js'),
`export async function createComponentRootService(overrides = {}, _services, _configResolution, bootstrapContext) {
return {
ok: true,
effectiveConfig: overrides,
bootContext: bootstrapContext,
runtime: {
async shutdown() {
return undefined;
}
}
};
}
`,
'utf8',
);
return packageDir;
}
describe('mx service command', () => {
it('marks runtime control ready before registry registration', () => {
const source = fs.readFileSync(path.join(process.cwd(), 'src', 'commands', 'service.ts'), 'utf8');
const controlIndex = source.indexOf('const mountedControlMount = mounted.controlMount;');
const readyIndex = source.indexOf("lifecycleState = 'ready';");
const registerIndex = source.indexOf('const runtimeRegistration = await registerRunnerRuntime(');
const liveIndex = source.indexOf("lifecycleState = 'live';");
assert.notEqual(controlIndex, -1);
assert.notEqual(readyIndex, -1);
assert.notEqual(registerIndex, -1);
assert.notEqual(liveIndex, -1);
assert.ok(controlIndex < readyIndex, 'control actor mount result must precede ready state');
assert.ok(readyIndex < registerIndex, 'runtime.ready must not wait on self-registration');
assert.ok(registerIndex < liveIndex, 'live state remains after registry registration');
});
it('loads matrix.json runtime.factory and invokes the declared bootstrap export in check mode', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-'));
try {
const packageDir = await createServiceFixture(tmpRoot);
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
'--root',
'cli.root',
'--mount',
'cli.mount',
'--config',
'./config/cli-config.json',
'--secrets',
'./config/cli-secrets.json',
'--overrides',
JSON.stringify({
config: {
nested: { enabled: true },
cli: 'override',
},
}),
]);
assertExitCode(result, 0);
const payload = parseLastJson(result.logs) as {
packageName: string;
exportName: string;
checked: boolean;
root?: string;
mount?: string;
configRef?: string;
secretsRef?: string;
instance?: {
id?: string;
class?: string;
mount?: string;
rootKind?: string;
configRef?: string;
policyRef?: string;
dependsOn?: string[];
secretRefs?: Array<{
name?: string;
provider?: string;
ref?: string;
required?: boolean;
}>;
registrationSource?: string;
};
serviceInstance?: {
id?: string;
class?: string;
mount?: string;
rootKind?: string;
configRef?: string;
policyRef?: string;
dependsOn?: string[];
secretRefs?: Array<{
name?: string;
provider?: string;
ref?: string;
required?: boolean;
}>;
registrationSource?: string;
};
runtimeRegistration?: {
runtimeId?: string;
runtimeRoot?: string;
registryMount?: string;
created?: boolean;
actorCount?: number;
runtimeCount?: number;
};
bindingRegistration?: {
runtimeId?: string;
registryMount?: string;
registeredCount?: number;
bindings?: Array<{
binding?: string;
localMount?: string;
packageName?: string;
ops?: string[];
}>;
};
runtimeDeregistration?: {
runtimeId?: string;
registryMount?: string;
removed?: boolean;
runtimeCount?: number;
};
bindingDeregistration?: {
runtimeId?: string;
registryMount?: string;
removedCount?: number;
};
result?: {
effectiveConfig?: {
config?: {
source?: string;
cli?: string;
nested?: { enabled?: boolean };
fromCliFile?: boolean;
};
};
runnerRuntimeProvided?: boolean;
runnerRuntimeRoot?: string | null;
bootContext?: {
root?: string;
mount?: string;
configRef?: string;
secretsRef?: string;
transport?: { mode?: string };
http?: { enabled?: boolean };
instance?: {
mount?: string;
configRef?: string;
dependsOn?: string[];
policyRef?: string;
secretRefs?: Array<{
name?: string;
provider?: string;
ref?: string;
}>;
registrationSource?: string;
};
serviceInstance?: {
mount?: string;
configRef?: string;
dependsOn?: string[];
policyRef?: string;
secretRefs?: Array<{
name?: string;
provider?: string;
ref?: string;
}>;
registrationSource?: string;
};
};
};
};
assert.equal(payload.packageName, '@acme/sample-service');
assert.equal(payload.exportName, 'createSampleService');
assert.equal(payload.checked, true);
assert.equal(payload.root, 'cli.root');
assert.equal(payload.mount, 'cli.mount');
assert.equal(payload.configRef, './config/cli-config.json');
assert.equal(payload.secretsRef, './config/cli-secrets.json');
assert.equal(payload.result?.effectiveConfig?.config?.source, 'manifest');
assert.equal(payload.result?.effectiveConfig?.config?.fromCliFile, true);
assert.equal(payload.result?.effectiveConfig?.config?.cli, 'override');
assert.equal(payload.result?.effectiveConfig?.config?.nested?.enabled, true);
assert.equal(payload.result?.runnerRuntimeProvided, true);
assert.equal(payload.result?.runnerRuntimeRoot, 'cli.root');
assert.equal(payload.runtimeRegistration?.runtimeId, '@acme/sample-service:sample-service.local');
assert.equal(payload.runtimeRegistration?.runtimeRoot, 'cli.root');
assert.equal(payload.runtimeRegistration?.registryMount, 'system.runtimes');
assert.equal(payload.runtimeRegistration?.created, true);
// Runtime control now has one RuntimeControlActor at
// system.runtimes.<runtimeId>. LoadedPackageActor adds one mount while
// the separate .control mount is absent, so actorCount stays at 5.
assert.equal(payload.runtimeRegistration?.actorCount, 5);
assert.equal(payload.runtimeRegistration?.runtimeCount, 1);
assert.equal(payload.bindingRegistration?.runtimeId, '@acme/sample-service:sample-service.local');
assert.equal(payload.bindingRegistration?.registryMount, 'system.bindings');
assert.equal(payload.bindingRegistration?.registeredCount, 2);
assert.deepEqual(payload.bindingRegistration?.bindings, [
{
binding: 'sample',
localMount: 'cli.mount',
packageName: '@acme/sample-service',
ops: [],
},
{
binding: 'sample.worker',
localMount: 'cli.mount.worker',
packageName: '@acme/sample-service',
ops: ['sample.run'],
},
]);
assert.equal(payload.runtimeDeregistration?.runtimeId, '@acme/sample-service:sample-service.local');
assert.equal(payload.runtimeDeregistration?.registryMount, 'system.runtimes');
assert.equal(payload.runtimeDeregistration?.removed, true);
assert.equal(payload.runtimeDeregistration?.runtimeCount, 0);
assert.equal(payload.bindingDeregistration?.runtimeId, '@acme/sample-service:sample-service.local');
assert.equal(payload.bindingDeregistration?.registryMount, 'system.bindings');
assert.equal(payload.bindingDeregistration?.removedCount, 2);
assert.equal(payload.instance?.autoStart, true);
assert.equal(payload.instance?.mount, 'cli.mount');
assert.equal(payload.instance?.configRef, './config/cli-config.json');
assert.equal(payload.instance?.rootKind, 'package-root');
assert.equal(payload.instance?.policyRef, 'policies/standalone');
assert.deepEqual(payload.instance?.dependsOn, ['system.identity']);
assert.equal(payload.instance?.registrationSource, 'matrix-service');
assert.equal(payload.instance?.secretRefs?.[0]?.name, 'overrideKey');
assert.equal(payload.instance?.secretRefs?.[0]?.provider, 'system.factotum');
assert.equal(payload.instance?.secretRefs?.[0]?.ref, 'secret://sample/override-key');
assert.equal(payload.serviceInstance?.mount, 'cli.mount');
assert.equal(payload.serviceInstance?.configRef, './config/cli-config.json');
assert.equal(payload.serviceInstance?.rootKind, 'package-root');
assert.equal(payload.serviceInstance?.autoStart, true);
assert.equal(payload.serviceInstance?.policyRef, 'policies/standalone');
assert.deepEqual(payload.serviceInstance?.dependsOn, ['system.identity']);
assert.equal(payload.serviceInstance?.registrationSource, 'matrix-service');
assert.equal(payload.serviceInstance?.secretRefs?.[0]?.name, 'overrideKey');
assert.equal(payload.serviceInstance?.secretRefs?.[0]?.provider, 'system.factotum');
assert.equal(payload.serviceInstance?.secretRefs?.[0]?.ref, 'secret://sample/override-key');
assert.equal(payload.result?.bootContext?.root, 'cli.root');
assert.equal(payload.result?.bootContext?.mount, 'cli.mount');
assert.equal(payload.result?.bootContext?.configRef, './config/cli-config.json');
assert.equal(payload.result?.bootContext?.secretsRef, './config/cli-secrets.json');
assert.equal(payload.result?.bootContext?.transport?.mode, 'local-memory');
assert.equal(payload.result?.bootContext?.http?.enabled, false);
assert.equal(payload.result?.bootContext?.instance?.mount, 'cli.mount');
assert.equal(payload.result?.bootContext?.instance?.configRef, './config/cli-config.json');
assert.equal(payload.result?.bootContext?.instance?.autoStart, true);
assert.deepEqual(payload.result?.bootContext?.instance?.dependsOn, ['system.identity']);
assert.equal(payload.result?.bootContext?.instance?.policyRef, 'policies/standalone');
assert.equal(payload.result?.bootContext?.instance?.registrationSource, 'matrix-service');
assert.equal(payload.result?.bootContext?.instance?.secretRefs?.[0]?.name, 'overrideKey');
assert.equal(payload.result?.bootContext?.serviceInstance?.mount, 'cli.mount');
assert.equal(payload.result?.bootContext?.serviceInstance?.configRef, './config/cli-config.json');
assert.equal(payload.result?.bootContext?.serviceInstance?.autoStart, true);
assert.deepEqual(payload.result?.bootContext?.serviceInstance?.dependsOn, ['system.identity']);
assert.equal(payload.result?.bootContext?.serviceInstance?.policyRef, 'policies/standalone');
assert.equal(payload.result?.bootContext?.serviceInstance?.registrationSource, 'matrix-service');
assert.equal(payload.result?.bootContext?.serviceInstance?.secretRefs?.[0]?.name, 'overrideKey');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('loads package-local environment manifests and threads them into standalone bootstrap', async () => {
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-env-'));
const packageDir = await createServiceFixture(tempRoot, { withEnvironment: true });
const result = await runMxCli(['node', 'mx', 'service', packageDir, '--check', '--json', '--env', 'dev']);
assertExitCode(result, 0);
const payload = parseLastJson(result.logs) as {
environmentName?: string;
root?: string;
environmentPath?: string;
runnerTransport?: {
mode?: string;
root?: string;
url?: string;
wsUrl?: string;
dataDir?: string;
pidFile?: string;
};
runtimeRegistration?: {
runtimeId?: string;
runtimeRoot?: string;
runtimeCount?: number;
};
bindingRegistration?: {
runtimeId?: string;
registryMount?: string;
registeredCount?: number;
bindings?: Array<{
binding?: string;
localMount?: string;
packageName?: string;
ops?: string[];
}>;
};
runtimeDeregistration?: {
runtimeId?: string;
removed?: boolean;
runtimeCount?: number;
};
bindingDeregistration?: {
runtimeId?: string;
removedCount?: number;
};
result?: {
bootContext?: {
environment?: {
name?: string;
runtime?: { root?: string };
};
runnerTransport?: {
mode?: string;
url?: string;
wsUrl?: string;
};
};
};
};
assert.equal(payload.environmentName, 'dev');
assert.equal(payload.root, 'COM.TEST.PACKAGE-ENV');
assert.equal(
payload.environmentPath,
path.join(packageDir, '.matrix', 'dev.environment.json'),
);
assert.equal(payload.runnerTransport?.mode, 'embedded');
assert.equal(payload.runnerTransport?.root, 'COM.TEST.PACKAGE-ENV');
assert.match(String(payload.runnerTransport?.url), /^nats:\/\/127\.0\.0\.1:\d+$/);
assert.match(String(payload.runnerTransport?.wsUrl), /^ws:\/\/127\.0\.0\.1:\d+$/);
assert.equal(payload.runnerTransport?.dataDir, path.join(packageDir, '.matrix', 'state', 'dev', 'nats'));
assert.equal(payload.runnerTransport?.pidFile, path.join(packageDir, '.matrix', 'state', 'dev', 'nats-server.pid'));
assert.equal(payload.result?.bootContext?.environment?.name, 'dev');
assert.equal(payload.result?.bootContext?.environment?.runtime?.root, 'COM.TEST.PACKAGE-ENV');
assert.equal(payload.result?.bootContext?.runnerTransport?.mode, 'embedded');
assert.match(String(payload.result?.bootContext?.runnerTransport?.url), /^nats:\/\/127\.0\.0\.1:\d+$/);
assert.match(String(payload.result?.bootContext?.runnerTransport?.wsUrl), /^ws:\/\/127\.0\.0\.1:\d+$/);
assert.equal(payload.runtimeRegistration?.runtimeId, 'sample-service-dev');
assert.equal(payload.runtimeRegistration?.runtimeRoot, 'COM.TEST.PACKAGE-ENV');
assert.equal(payload.runtimeRegistration?.runtimeCount, 1);
assert.equal(payload.bindingRegistration?.runtimeId, 'sample-service-dev');
assert.equal(payload.bindingRegistration?.registryMount, 'system.bindings');
assert.equal(payload.bindingRegistration?.registeredCount, 2);
assert.deepEqual(payload.bindingRegistration?.bindings, [
{
binding: 'sample',
localMount: 'sample-service.local',
packageName: '@acme/sample-service',
ops: [],
},
{
binding: 'sample.worker',
localMount: 'sample-service.local.worker',
packageName: '@acme/sample-service',
ops: ['sample.run'],
},
]);
assert.equal(payload.runtimeDeregistration?.runtimeId, 'sample-service-dev');
assert.equal(payload.runtimeDeregistration?.removed, true);
assert.equal(payload.runtimeDeregistration?.runtimeCount, 0);
assert.equal(payload.bindingDeregistration?.runtimeId, 'sample-service-dev');
assert.equal(payload.bindingDeregistration?.removedCount, 2);
});
it('fails clearly when standalone top-level fields are present', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-instance-conflict-'));
try {
const packageDir = await createServiceFixture(tmpRoot, { conflictingTopLevelFields: true });
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 1);
assert.match(result.errors.join('\n'), /top-level "mount" is no longer supported; use "instance\.mount"/i);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('fails clearly when matrix.json runtime.factory uses top-level secrets.file', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-secrets-field-'));
try {
const packageDir = await createServiceFixture(tmpRoot, { topLevelSecretsFile: true });
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 1);
assert.match(result.errors.join('\n'), /top-level "secrets\.file" is no longer supported; use "instance\.secretRefs or CLI --secrets"/i);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('fails clearly when matrix.json runtime.factory uses top-level standalone bootstrap fields', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-bootstrap-field-'));
try {
const packageDir = await createServiceFixture(tmpRoot);
const manifestPath = path.join(packageDir, 'matrix.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {
runtime?: { factory?: Record<string, unknown> };
};
fs.writeFileSync(
manifestPath,
JSON.stringify({
...manifest,
runtime: {
...(manifest.runtime ?? {}),
factory: {
...(manifest.runtime?.factory ?? {}),
root: 'old.root',
},
},
}, null, 2),
'utf8',
);
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 1);
assert.match(result.errors.join('\n'), /top-level "root" is no longer supported; use "bootstrap\.root"/i);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('fails clearly when matrix.json runtime.factory is missing', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-missing-'));
try {
const packageDir = path.join(tmpRoot, 'broken-service');
fs.mkdirSync(packageDir, { recursive: true });
fs.writeFileSync(
path.join(packageDir, 'matrix.json'),
JSON.stringify({
name: '@acme/broken-service',
version: '1.0.0',
runtime: { language: 'typescript', entry: './dist/runtime/index.js' },
components: [],
permissions: { fsPolicy: 'none' },
}, null, 2),
'utf8',
);
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
]);
assertExitCode(result, 1);
assert.match(result.errors.join('\n'), /matrix\.json runtime\.factory must be a JSON object/i);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('fails clearly when matrix.json runtime.factory instance identity disagrees with matrix.json', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-package-identity-mismatch-'));
try {
const packageDir = await createServiceFixture(tmpRoot);
const manifestPath = path.join(packageDir, 'matrix.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as {
runtime?: { factory?: { instance?: Record<string, unknown> } };
};
fs.writeFileSync(
manifestPath,
JSON.stringify({
...manifest,
runtime: {
...(manifest.runtime ?? {}),
factory: {
...(manifest.runtime?.factory ?? {}),
instance: {
...(manifest.runtime?.factory?.instance ?? {}),
packageName: '@acme/not-the-same-service',
},
},
},
}, null, 2),
'utf8',
);
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 1);
assert.match(
result.errors.join('\n'),
/instance\.packageName "@acme\/not-the-same-service" does not match matrix\.json name "@acme\/sample-service"/,
);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('does not invent a component mount for package-root standalone services', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-package-root-'));
try {
const packageDir = createPackageRootFixture(tmpRoot);
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 0);
const payload = parseLastJson(result.logs) as {
mount?: string;
instance?: {
id?: string;
mount?: string;
rootKind?: string;
};
serviceInstance?: {
id?: string;
mount?: string;
rootKind?: string;
};
result?: {
bootContext?: {
mount?: string;
instance?: {
mount?: string;
rootKind?: string;
};
serviceInstance?: {
mount?: string;
rootKind?: string;
};
};
};
};
assert.equal(payload.mount, undefined);
assert.equal(payload.instance?.id, '@acme/package-root-service');
assert.equal(payload.instance?.mount, undefined);
assert.equal(payload.instance?.rootKind, 'package-root');
assert.equal(payload.serviceInstance?.id, '@acme/package-root-service');
assert.equal(payload.serviceInstance?.mount, undefined);
assert.equal(payload.serviceInstance?.rootKind, 'package-root');
assert.equal(payload.result?.bootContext?.mount, undefined);
assert.equal(payload.result?.bootContext?.instance?.mount, undefined);
assert.equal(payload.result?.bootContext?.instance?.rootKind, 'package-root');
assert.equal(payload.result?.bootContext?.serviceInstance?.mount, undefined);
assert.equal(payload.result?.bootContext?.serviceInstance?.rootKind, 'package-root');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('resolves component-root standalone services by declared componentType', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-component-root-'));
try {
const packageDir = createComponentRootFixture(tmpRoot, {
name: 'component-root-service',
components: [
{ type: 'PrimaryService', mount: 'primary.mount' },
{ type: 'SecondaryService', mount: 'secondary.mount' },
],
instance: {
componentType: 'SecondaryService',
},
});
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
'--json',
]);
assertExitCode(result, 0);
const payload = parseLastJson(result.logs) as {
mount?: string;
instance?: {
componentType?: string;
mount?: string;
rootKind?: string;
};
result?: {
bootContext?: {
mount?: string;
instance?: {
componentType?: string;
mount?: string;
rootKind?: string;
};
};
};
};
assert.equal(payload.mount, 'secondary.mount');
assert.equal(payload.instance?.componentType, 'SecondaryService');
assert.equal(payload.instance?.mount, 'secondary.mount');
assert.equal(payload.instance?.rootKind, 'component');
assert.equal(payload.result?.bootContext?.mount, 'secondary.mount');
assert.equal(payload.result?.bootContext?.instance?.componentType, 'SecondaryService');
assert.equal(payload.result?.bootContext?.instance?.mount, 'secondary.mount');
assert.equal(payload.result?.bootContext?.instance?.rootKind, 'component');
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
it('fails clearly when component-root standalone bootstrap is ambiguous', async () => {
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-service-component-ambiguous-'));
try {
const packageDir = createComponentRootFixture(tmpRoot, {
name: 'ambiguous-component-root-service',
components: [
{ type: 'PrimaryService', mount: 'primary.mount' },
{ type: 'SecondaryService', mount: 'secondary.mount' },
],
});
const result = await runMxCli([
'node',
'mx',
'service',
packageDir,
'--check',
]);
assertExitCode(result, 1);
assert.match(result.errors.join('\n'), /component-root bootstrap is ambiguous/i);
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
});
});