Files
Elydora-Node-SDK/test/opencode-transaction.test.mjs
T

111 lines
4.1 KiB
JavaScript

import assert from 'node:assert/strict';
import { lstat, mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
import path from 'node:path';
import test from 'node:test';
import {
contractModuleUrl,
createFixture,
environment,
installConfig,
installationModuleUrl,
ioModuleUrl,
runNode,
} from '../test-support/opencode-test-helpers.mjs';
async function assertMissing(filePath) {
await assert.rejects(lstat(filePath), { code: 'ENOENT' });
}
async function runPreparedInstallation(fixture, { failLegacy = false, racePlugin } = {}) {
const source = `
import fsp from 'node:fs/promises';
import path from 'node:path';
import {
commitOpenCodeInstallation,
prepareOpenCodeInstallation,
} from ${JSON.stringify(installationModuleUrl)};
import { readOpenCodeSources } from ${JSON.stringify(ioModuleUrl)};
const config = JSON.parse(process.env.ELYDORA_TEST_CONFIG);
const sources = await readOpenCodeSources();
const prepared = await prepareOpenCodeInstallation(config, sources);
if (process.env.ELYDORA_TEST_RACE_PLUGIN) {
await fsp.mkdir(path.dirname(sources.paths.pluginPath), { recursive: true });
await fsp.writeFile(sources.paths.pluginPath, process.env.ELYDORA_TEST_RACE_PLUGIN);
}
let failed = false;
await commitOpenCodeInstallation(prepared, async (from, to) => {
if (!failed
&& process.env.ELYDORA_TEST_FAIL_LEGACY === 'true'
&& from === sources.paths.legacyPluginPath
&& to.endsWith('.rollback')) {
failed = true;
throw new Error('injected OpenCode legacy migration failure');
}
await fsp.rename(from, to);
});
`;
return runNode(
['--input-type=module', '--eval', source],
{
...environment(fixture),
ELYDORA_TEST_CONFIG: JSON.stringify(installConfig(fixture)),
ELYDORA_TEST_FAIL_LEGACY: String(failLegacy),
ELYDORA_TEST_RACE_PLUGIN: racePlugin,
},
fixture.projectDir,
);
}
async function assertNoTransactionFiles(...directories) {
const names = [];
for (const directory of directories) {
try {
names.push(...await readdir(directory));
} catch (error) {
if (error.code !== 'ENOENT') throw error;
}
}
assert.equal(names.some((name) => /\.(tmp|rollback)$/.test(name)), false, names.join(', '));
}
test('OpenCode rolls back plugin, legacy migration, and runtime after a late failure', async () => {
const fixture = await createFixture();
const { buildLegacyOpenCodePlugin } = await import(contractModuleUrl);
const legacy = buildLegacyOpenCodePlugin(fixture.hookScriptPath, fixture.guardScriptPath);
try {
await mkdir(path.dirname(fixture.legacyPluginPath), { recursive: true });
await writeFile(fixture.legacyPluginPath, legacy);
const result = await runPreparedInstallation(fixture, { failLegacy: true });
assert.equal(result.code, 1);
assert.match(result.stderr, /injected OpenCode legacy migration failure/);
assert.equal(await readFile(fixture.legacyPluginPath, 'utf-8'), legacy);
await assertMissing(fixture.pluginPath);
for (const filePath of [
fixture.guardScriptPath,
fixture.hookScriptPath,
path.join(fixture.agentDir, 'config.json'),
path.join(fixture.agentDir, 'private.key'),
]) await assertMissing(filePath);
await assertNoTransactionFiles(fixture.pluginsDir, fixture.agentDir);
} finally {
await fixture.close();
}
});
test('OpenCode detects concurrent plugin creation before committing runtime files', async () => {
const fixture = await createFixture();
const racePlugin = 'export const RacePlugin = async () => ({});\n';
try {
const result = await runPreparedInstallation(fixture, { racePlugin });
assert.equal(result.code, 1);
assert.match(result.stderr, /changed during OpenCode installation/i);
assert.equal(await readFile(fixture.pluginPath, 'utf-8'), racePlugin);
await assertMissing(fixture.guardScriptPath);
await assertMissing(fixture.hookScriptPath);
await assertNoTransactionFiles(fixture.pluginsDir, fixture.agentDir);
} finally {
await fixture.close();
}
});