import { describe, expect, test } from 'bun:test';
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { findMonorepoRoot } from './repo-root';
/**
* A suite that has to package a module mid-run does it with the HOST's celilo:
* `bun run apps/celilo/src/cli/index.ts package
`. `package` signs with a
* master key, and with nothing set that key is the host's own, the operator's
* on a developer or gate machine, created there if there is none.
*
* celilo now refuses to resolve its default data directory under a test run
* (celilo#1512), and a suite is one: the child inherits NODE_ENV=test. So the
* harness hands that child a key of its own. These run the real command the way
* the harness does, under a stand-in home, and look at what the home holds.
*/
const repoRoot = findMonorepoRoot(join(import.meta.dir, '..'));
const cli = repoRoot ? join(repoRoot, 'apps/celilo/src/cli/index.ts') : '';
// Small, and it has no build step, so packaging it is a copy and a signature.
const moduleDir = repoRoot ? join(repoRoot, 'modules/hello-trespass') : '';
function packageAsASuiteDoes(extraEnv: Record) {
const work = mkdtempSync(join(tmpdir(), 'celilo-suite-package-'));
const home = join(work, 'home');
const env: Record = {
...process.env,
NODE_ENV: 'test',
HOME: home,
...extraEnv,
};
for (const key of ['CELILO_DATA_DIR', 'CELILO_DB_PATH', 'EVENT_BUS_DB', 'XDG_DATA_HOME']) {
delete env[key];
}
if (!('CELILO_MASTER_KEY_PATH' in extraEnv)) env.CELILO_MASTER_KEY_PATH = undefined;
const out = join(work, 'out.netapp');
const child = Bun.spawnSync(
[process.execPath, 'run', cli, 'package', moduleDir, '--output', out],
{
env,
stdin: 'ignore',
timeout: 120_000,
},
);
const celiloStateInHome = [
'Library/Application Support/celilo',
'.local/share/celilo',
'.celilo',
].filter((path) => existsSync(join(home, path)));
const result = {
exitCode: child.exitCode,
output: `${child.stdout.toString()}${child.stderr.toString()}`,
packaged: existsSync(out),
celiloStateInHome,
work,
};
return result;
}
describe.skipIf(!repoRoot)('the key an in-run `celilo package` signs with', () => {
test('with the harness’s own key path it packages, and the home is left alone', () => {
const key = join(mkdtempSync(join(tmpdir(), 'celilo-suite-key-')), 'signing.key');
const run = packageAsASuiteDoes({ CELILO_MASTER_KEY_PATH: key });
try {
expect(run.output).not.toContain('Refusing');
expect(run.exitCode).toBe(0);
expect(run.packaged).toBe(true);
expect(existsSync(key)).toBe(true);
expect(run.celiloStateInHome).toEqual([]);
} finally {
rmSync(run.work, { recursive: true, force: true });
}
}, 150_000);
test('without one, celilo refuses rather than reach for the host’s key', () => {
// The state the harness was in. This is why the env below is not optional.
const run = packageAsASuiteDoes({});
try {
expect(run.exitCode).not.toBe(0);
expect(run.output).toContain('celilo#1512');
expect(run.packaged).toBe(false);
expect(run.celiloStateInHome).toEqual([]);
} finally {
rmSync(run.work, { recursive: true, force: true });
}
}, 150_000);
test('the harness passes that key path to the command it runs', () => {
const src = readFileSync(join(import.meta.dir, 'container-manager.ts'), 'utf-8');
const call =
/run\(\s*`bun run \$\{JSON\.stringify\(celiloCliPath\)\} package [\s\S]*?\n {12}\);/.exec(
src,
);
expect(call).not.toBeNull();
expect(call?.[0]).toContain('CELILO_MASTER_KEY_PATH: SUITE_PACKAGE_KEY_PATH');
expect(call?.[0]).toContain('...process.env');
// And the fixture these tests package really is a module.
expect(readdirSync(moduleDir)).toContain('manifest.yml');
});
});