import { afterEach, describe, it } from 'node:test'; import { strict as assert } from 'node:assert'; import { spawnSync } from 'node:child_process'; import { createHash } from 'node:crypto'; import * as fs from 'node:fs'; import * as http from 'node:http'; import * as os from 'node:os'; import * as path from 'node:path'; import { runMxCli, parseLastJson, assertExitCode } from '../helpers/cli-harness.js'; import { createMatrixPackage } from '../helpers/package-fixtures.js'; async function startNpmRegistryFixture(params: { readonly packageName: string; readonly version: string; readonly tarballPath: string; readonly expectedToken?: string; }): Promise<{ readonly registryUrl: string; readonly metadataRequests: () => number; readonly tarballRequests: () => number; readonly authorizationHeaders: () => readonly string[]; close(): Promise; }> { const tarball = fs.readFileSync(params.tarballPath); const shasum = createHash('sha1').update(tarball).digest('hex'); const integrity = `sha512-${createHash('sha512').update(tarball).digest('base64')}`; let metadataRequestCount = 0; let tarballRequestCount = 0; const authorizationHeaders: string[] = []; const server = http.createServer((request, response) => { const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1'); const decodedPath = decodeURIComponent(requestUrl.pathname); const authorization = request.headers.authorization; if (typeof authorization === 'string') { authorizationHeaders.push(authorization); } else if (Array.isArray(authorization)) { authorizationHeaders.push(...authorization); } if (decodedPath === `/${params.packageName}`) { metadataRequestCount += 1; const registryUrl = `http://127.0.0.1:${addressPort(server)}/`; const metadata = { name: params.packageName, 'dist-tags': { latest: params.version }, versions: { [params.version]: { name: params.packageName, version: params.version, dist: { tarball: new URL('tarballs/package.tgz', registryUrl).toString(), shasum, integrity, }, }, }, }; response.writeHead(200, { 'content-type': 'application/json' }); response.end(JSON.stringify(metadata)); return; } if (decodedPath === '/tarballs/package.tgz') { tarballRequestCount += 1; response.writeHead(200, { 'content-type': 'application/octet-stream', 'content-length': String(tarball.byteLength), }); response.end(tarball); return; } response.writeHead(404, { 'content-type': 'application/json' }); response.end(JSON.stringify({ error: `Not found: ${decodedPath}` })); }); await new Promise((resolve, reject) => { server.once('error', reject); server.listen(0, '127.0.0.1', () => { server.off('error', reject); resolve(); }); }); return { registryUrl: `http://127.0.0.1:${addressPort(server)}/`, metadataRequests: () => metadataRequestCount, tarballRequests: () => tarballRequestCount, authorizationHeaders: () => [...authorizationHeaders], async close(): Promise { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); }, }; } function addressPort(server: http.Server): number { const address = server.address(); if (!address || typeof address === 'string') { throw new Error('Registry fixture is not listening on a TCP port'); } return address.port; } function npmPackPackage(packageDir: string, outDir: string): string { fs.mkdirSync(outDir, { recursive: true }); const result = spawnSync('npm', ['pack', packageDir, '--pack-destination', outDir], { encoding: 'utf8', }); assert.equal( result.status, 0, `npm pack failed\nstdout:\n${result.stdout ?? ''}\nstderr:\n${result.stderr ?? ''}`, ); const fileName = result.stdout.trim().split(/\r?\n/).filter(Boolean).pop(); assert.equal(Boolean(fileName), true); return path.join(outDir, fileName!); } describe('FP-30 mx install source resolution', () => { const tempDirs: string[] = []; const closeFns: Array<() => Promise> = []; const restoreEnvFns: Array<() => void> = []; afterEach(async () => { while (restoreEnvFns.length > 0) { const restore = restoreEnvFns.pop(); if (restore) { restore(); } } while (closeFns.length > 0) { const close = closeFns.pop(); if (close) { await close(); } } while (tempDirs.length > 0) { const dir = tempDirs.pop(); if (dir) { fs.rmSync(dir, { recursive: true, force: true }); } } }); function useMatrixHome(matrixHome: string): void { const previousMatrixHome = process.env.MATRIX_HOME; const previousCliConfig = process.env.MATRIX_CLI_CONFIG; const previousPackageRegistry = process.env.MATRIX_PACKAGE_REGISTRY; const previousRegistryUrl = process.env.MATRIX_REGISTRY_URL; process.env.MATRIX_HOME = matrixHome; delete process.env.MATRIX_CLI_CONFIG; delete process.env.MATRIX_PACKAGE_REGISTRY; delete process.env.MATRIX_REGISTRY_URL; restoreEnvFns.push(() => { restoreEnv('MATRIX_HOME', previousMatrixHome); restoreEnv('MATRIX_CLI_CONFIG', previousCliConfig); restoreEnv('MATRIX_PACKAGE_REGISTRY', previousPackageRegistry); restoreEnv('MATRIX_REGISTRY_URL', previousRegistryUrl); }); } function restoreEnv(key: string, value: string | undefined): void { if (value === undefined) { delete process.env[key]; return; } process.env[key] = value; } it('installs package from local directory, tarball, and registry name', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-install-sources-')); tempDirs.push(root); const workspaceDir = path.join(root, 'workspace'); fs.mkdirSync(workspaceDir, { recursive: true }); const packagesDir = path.join(root, '.matrix', 'packages'); const registryDir = path.join(root, '.matrix', 'registry'); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const fixture = createMatrixPackage(path.join(root, 'source'), { packageName: '@acme/install-source', version: '1.0.0', componentType: 'InstallSourceActor', mount: 'install.source', }); const installFromDirectory = await runMxCli([ 'node', 'mx', 'install', fixture.packageDir, '--packages-dir', packagesDir, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromDirectory, 0); const installedFromDir = parseLastJson(installFromDirectory.logs) as { targetDir: string }; assert.equal(fs.existsSync(installedFromDir.targetDir), true); const uninstallAfterDirectory = await runMxCli([ 'node', 'mx', 'uninstall', fixture.packageName, '--packages-dir', packagesDir, '--json', ], { cwd: workspaceDir }); assertExitCode(uninstallAfterDirectory, 0); const packResult = await runMxCli([ 'node', 'mx', 'pack', '--out-dir', path.join(root, 'packed'), '--json', ], { cwd: fixture.packageDir }); assertExitCode(packResult, 0); const packPayload = parseLastJson(packResult.logs) as { tarballPath: string }; assert.equal(fs.existsSync(packPayload.tarballPath), true); const installFromTarball = await runMxCli([ 'node', 'mx', 'install', packPayload.tarballPath, '--packages-dir', packagesDir, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromTarball, 0); const installedFromTarball = parseLastJson(installFromTarball.logs) as { targetDir: string }; assert.equal(fs.existsSync(installedFromTarball.targetDir), true); const uninstallAfterTarball = await runMxCli([ 'node', 'mx', 'uninstall', fixture.packageName, '--packages-dir', packagesDir, '--json', ], { cwd: workspaceDir }); assertExitCode(uninstallAfterTarball, 0); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-install', '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(loginResult, 0); const publishResult = await runMxCli([ 'node', 'mx', 'publish', '--registry-dir', registryDir, '--credentials-file', credentialsFile, '--json', ], { cwd: fixture.packageDir }); assertExitCode(publishResult, 0); const installFromRegistry = await runMxCli([ 'node', 'mx', 'install', `${fixture.packageName}@${fixture.version}`, '--packages-dir', packagesDir, '--registry-dir', registryDir, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromRegistry, 0); const installedFromRegistry = parseLastJson(installFromRegistry.logs) as { source: string; targetDir: string }; assert.equal(installedFromRegistry.source, `${fixture.packageName}@${fixture.version}`); assert.equal(fs.existsSync(installedFromRegistry.targetDir), true); }); it('installs package from an npm-compatible registry URL with stored registry credentials', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-install-npm-registry-')); tempDirs.push(root); const workspaceDir = path.join(root, 'workspace'); fs.mkdirSync(workspaceDir, { recursive: true }); const packagesDir = path.join(root, '.matrix', 'packages'); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const fixture = createMatrixPackage(path.join(root, 'source'), { packageName: '@acme/remote-install', version: '1.2.3', componentType: 'RemoteInstallActor', mount: 'remote.install', }); fs.mkdirSync(path.join(fixture.packageDir, 'vendor', 'runtime-helper'), { recursive: true }); fs.writeFileSync( path.join(fixture.packageDir, 'vendor', 'runtime-helper', 'package.json'), JSON.stringify({ name: '@acme/runtime-helper', version: '1.0.0', type: 'module', main: 'index.js', }, null, 2), 'utf8', ); fs.writeFileSync( path.join(fixture.packageDir, 'vendor', 'runtime-helper', 'index.js'), 'export const helper = true;\n', 'utf8', ); const packageJsonPath = path.join(fixture.packageDir, 'package.json'); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) as Record; packageJson.dependencies = { '@acme/runtime-helper': 'file:./vendor/runtime-helper', }; packageJson.devDependencies = { '@open-matrix/core': '*', }; packageJson.peerDependencies = { '@open-matrix/core': '*', }; packageJson.peerDependenciesMeta = { '@open-matrix/core': { optional: true }, }; fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf8'); const packResult = await runMxCli([ 'node', 'mx', 'pack', '--out-dir', path.join(root, 'packed'), '--json', ], { cwd: fixture.packageDir }); assertExitCode(packResult, 0); const packPayload = parseLastJson(packResult.logs) as { tarballPath: string }; const registry = await startNpmRegistryFixture({ packageName: fixture.packageName, version: fixture.version, tarballPath: packPayload.tarballPath, expectedToken: 'token-remote-install', }); closeFns.push(registry.close); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-remote-install', '--registry', registry.registryUrl, '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(loginResult, 0); const installFromRemoteRegistry = await runMxCli([ 'node', 'mx', 'install', `${fixture.packageName}@${fixture.version}`, '--packages-dir', packagesDir, '--registry', registry.registryUrl, '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromRemoteRegistry, 0); const installed = parseLastJson(installFromRemoteRegistry.logs) as { source: string; targetDir: string; version: string; }; assert.equal(installed.source, `${fixture.packageName}@${fixture.version}`); assert.equal(installed.version, fixture.version); assert.equal(fs.existsSync(installed.targetDir), true); assert.equal( fs.existsSync(path.join(installed.targetDir, 'node_modules', '@acme', 'runtime-helper', 'package.json')), true, ); assert.equal( fs.existsSync(path.join(installed.targetDir, 'node_modules', '@open-matrix', 'core', 'package.json')), false, ); assert.equal(registry.metadataRequests() > 0, true); assert.equal(registry.tarballRequests() > 0, true); assert.equal( registry.authorizationHeaders().some((header) => header.includes('token-remote-install')), true, ); }); it('installs package names from the configured npm registry without a per-command registry flag', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-install-config-registry-')); tempDirs.push(root); const workspaceDir = path.join(root, 'workspace'); fs.mkdirSync(workspaceDir, { recursive: true }); useMatrixHome(path.join(root, '.matrix')); const packagesDir = path.join(root, '.matrix', 'packages'); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const fixture = createMatrixPackage(path.join(root, 'source'), { packageName: '@acme/configured-remote-install', version: '1.2.3', componentType: 'ConfiguredRemoteInstallActor', mount: 'configured.remote.install', }); const packResult = await runMxCli([ 'node', 'mx', 'pack', '--out-dir', path.join(root, 'packed'), '--json', ], { cwd: fixture.packageDir }); assertExitCode(packResult, 0); const packPayload = parseLastJson(packResult.logs) as { tarballPath: string }; const registry = await startNpmRegistryFixture({ packageName: fixture.packageName, version: fixture.version, tarballPath: packPayload.tarballPath, expectedToken: 'token-configured-install', }); closeFns.push(registry.close); const setConfig = await runMxCli([ 'node', 'mx', 'config', 'set', 'registry', registry.registryUrl, '--json', ], { cwd: workspaceDir }); assertExitCode(setConfig, 0); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-configured-install', '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(loginResult, 0); const installFromConfiguredRegistry = await runMxCli([ 'node', 'mx', 'install', `${fixture.packageName}@${fixture.version}`, '--packages-dir', packagesDir, '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromConfiguredRegistry, 0); const installed = parseLastJson(installFromConfiguredRegistry.logs) as { source: string; targetDir: string; version: string; }; assert.equal(installed.source, `${fixture.packageName}@${fixture.version}`); assert.equal(installed.version, fixture.version); assert.equal(fs.existsSync(installed.targetDir), true); assert.equal(registry.metadataRequests() > 0, true); assert.equal(registry.tarballRequests() > 0, true); assert.equal( registry.authorizationHeaders().some((header) => header.includes('token-configured-install')), true, ); }); it('rejects npm registry packages whose matrix.json and package.json versions disagree', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-install-version-mismatch-')); tempDirs.push(root); const workspaceDir = path.join(root, 'workspace'); fs.mkdirSync(workspaceDir, { recursive: true }); const packagesDir = path.join(root, '.matrix', 'packages'); const fixture = createMatrixPackage(path.join(root, 'source'), { packageName: '@acme/version-mismatch', version: '1.0.0', componentType: 'VersionMismatchActor', mount: 'version.mismatch', }); fs.writeFileSync( path.join(fixture.packageDir, 'package.json'), JSON.stringify({ name: fixture.packageName, version: '9.9.9', type: 'module', main: 'src/index.ts', }, null, 2), 'utf8', ); const tarballPath = npmPackPackage(fixture.packageDir, path.join(root, 'packed')); const registry = await startNpmRegistryFixture({ packageName: fixture.packageName, version: '9.9.9', tarballPath, }); closeFns.push(registry.close); const installResult = await runMxCli([ 'node', 'mx', 'install', `${fixture.packageName}@9.9.9`, '--packages-dir', packagesDir, '--registry', registry.registryUrl, '--json', ], { cwd: workspaceDir }); assertExitCode(installResult, 1); assert.equal( installResult.errors.some((line) => line.includes('MX_PACKAGE_VERSION_MISMATCH')), true, ); }); it('normalizes whitespace-padded registry specifiers before install', async () => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mx-fp30-install-specifier-trim-')); tempDirs.push(root); const workspaceDir = path.join(root, 'workspace'); fs.mkdirSync(workspaceDir, { recursive: true }); const packagesDir = path.join(root, '.matrix', 'packages'); const registryDir = path.join(root, '.matrix', 'registry'); const credentialsFile = path.join(root, '.matrix', 'credentials.json'); const fixture = createMatrixPackage(path.join(root, 'source'), { packageName: '@acme/install-source', version: '1.0.0', componentType: 'InstallSourceActor', mount: 'install.source', }); const loginResult = await runMxCli([ 'node', 'mx', 'login', '--username', 'alice', '--token', 'token-install', '--credentials-file', credentialsFile, '--json', ], { cwd: workspaceDir }); assertExitCode(loginResult, 0); const publishResult = await runMxCli([ 'node', 'mx', 'publish', '--registry-dir', registryDir, '--credentials-file', credentialsFile, '--json', ], { cwd: fixture.packageDir }); assertExitCode(publishResult, 0); const installFromRegistry = await runMxCli([ 'node', 'mx', 'install', ` ${fixture.packageName}@${fixture.version} `, '--packages-dir', packagesDir, '--registry-dir', registryDir, '--json', ], { cwd: workspaceDir }); assertExitCode(installFromRegistry, 0); const installedFromRegistry = parseLastJson(installFromRegistry.logs) as { source: string; targetDir: string }; assert.equal(installedFromRegistry.source, `${fixture.packageName}@${fixture.version}`); assert.equal(fs.existsSync(installedFromRegistry.targetDir), true); }); });