mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-28 12:35:13 +02:00
115 lines
5.0 KiB
JavaScript
115 lines
5.0 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const { spawnSync } = require('child_process');
|
|
const test = require('node:test');
|
|
|
|
const ROOT = path.resolve(__dirname, '../..');
|
|
|
|
function withReadOnlyCli(fn) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-carrier-cli-'));
|
|
const user = path.join(root, 'user');
|
|
const workspace = path.join(root, 'workspace');
|
|
fs.mkdirSync(user);
|
|
fs.mkdirSync(workspace);
|
|
fs.writeFileSync(path.join(user, 'settings.json'), '{"existing":true}\n');
|
|
try {
|
|
fn(args => spawnSync(process.execPath, [path.join(ROOT, 'scripts/ecc.js'), 'profile', ...args], {
|
|
cwd: workspace, encoding: 'utf8', timeout: 30000, maxBuffer: 4 * 1024 * 1024,
|
|
env: {
|
|
PATH: process.env.PATH, SystemRoot: process.env.SystemRoot,
|
|
HOME: user, USERPROFILE: user, CODEX_HOME: path.join(user, '.codex'),
|
|
CLAUDE_CONFIG_DIR: path.join(user, '.claude'),
|
|
XDG_CONFIG_HOME: path.join(user, 'config'), XDG_STATE_HOME: path.join(user, 'state'),
|
|
...(process.env.NODE_V8_COVERAGE ? { NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE } : {}),
|
|
},
|
|
}));
|
|
} finally {
|
|
try {
|
|
assert.deepEqual(fs.readdirSync(root).sort(), ['user', 'workspace']);
|
|
assert.deepEqual(fs.readdirSync(user), ['settings.json']);
|
|
assert.equal(fs.readFileSync(path.join(user, 'settings.json'), 'utf8'), '{"existing":true}\n');
|
|
assert.deepEqual(fs.readdirSync(workspace), []);
|
|
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
|
}
|
|
}
|
|
|
|
function payload(result) {
|
|
assert.equal(result.status, 0, result.stderr || result.stdout);
|
|
const value = JSON.parse(result.stdout);
|
|
assert.equal(value.status, 'warning');
|
|
assert.equal(value.activation, 'unobserved');
|
|
assert.equal(value.carrier.active, false);
|
|
assert.equal(value.carrier.nativeSupport, 'unobserved');
|
|
return value;
|
|
}
|
|
|
|
test('profile help exposes carrier planning without a write command', () => withReadOnlyCli(run => {
|
|
const result = run(['--help']);
|
|
assert.equal(result.status, 0);
|
|
assert.match(result.stdout, /ecc profile carrier/);
|
|
assert.match(result.stdout, /read-only/i);
|
|
}));
|
|
|
|
test('default carrier preview is a deterministic Lean Codex proposal', () => withReadOnlyCli(run => {
|
|
const first = payload(run(['carrier', '--json']));
|
|
assert.deepEqual(payload(run(['carrier', '--json'])), first);
|
|
assert.equal(first.carrier.target, 'codex');
|
|
assert.equal(first.carrier.profileId, 'lean@1');
|
|
assert.equal(first.carrier.selectionMode, 'auto');
|
|
assert.equal(first.carrier.selectedIds.length, 3);
|
|
assert.equal(first.carrier.status, 'planned');
|
|
assert.equal(first.artifacts[0].digest, first.carrier.carrierDigest);
|
|
assert.ok(!JSON.stringify(first).includes(ROOT));
|
|
}));
|
|
|
|
test('Full carrier keeps explicit exclusions and manual intent', () => withReadOnlyCli(run => {
|
|
const { carrier } = payload(run(['carrier', 'full@1', '--selection', 'manual',
|
|
'--exclude', 'skill:python-patterns', '--json']));
|
|
assert.equal(carrier.selectionMode, 'manual');
|
|
assert.ok(carrier.excludedIds.includes('skill:python-patterns'));
|
|
assert.ok(carrier.files.every(file => file.skillId !== 'skill:python-patterns'));
|
|
}));
|
|
|
|
test('every implemented layout remains explicitly native-unobserved', () => withReadOnlyCli(run => {
|
|
for (const target of ['claude', 'codex', 'cursor', 'opencode', 'pi']) {
|
|
const { carrier } = payload(run(['carrier', '--target', target, '--json']));
|
|
assert.equal(carrier.target, target);
|
|
assert.equal(carrier.status, 'planned');
|
|
assert.ok(carrier.files.length > 0);
|
|
}
|
|
}));
|
|
|
|
test('recognized unsupported target returns inventory without generated files', () => withReadOnlyCli(run => {
|
|
const { carrier } = payload(run(['carrier', '--target', 'kimi', '--json']));
|
|
assert.equal(carrier.status, 'unsupported');
|
|
assert.deepEqual(carrier.files, []);
|
|
assert.equal(carrier.selectedIds.length, 3);
|
|
}));
|
|
|
|
test('carrier text and dry-run output preserve read-only and unobserved boundaries', () => withReadOnlyCli(run => {
|
|
const result = run(['carrier', '--dry-run']);
|
|
assert.equal(result.status, 0, result.stderr);
|
|
assert.match(result.stdout, /carrier/i);
|
|
assert.match(result.stdout, /unobserved/i);
|
|
assert.match(result.stdout, /planned|proposed/i);
|
|
const expected = payload(run(['carrier', 'lean@1', '--target', 'codex', '--json']));
|
|
for (const args of [
|
|
['--dry-run', 'carrier', 'lean@1', '--target', 'codex', '--json'],
|
|
['carrier', 'lean@1', '--target', '--dry-run', 'codex', '--json'],
|
|
]) assert.deepEqual(payload(run(args)), expected);
|
|
}));
|
|
|
|
test('carrier rejects unknown targets, write destinations, and hook flags', () => withReadOnlyCli(run => {
|
|
for (const args of [['--target', 'unknown'], ['--output', 'user'], ['--hooks', 'strict']]) {
|
|
const result = run(['carrier', ...args, '--json']);
|
|
assert.equal(result.status, 1);
|
|
const error = JSON.parse(result.stdout);
|
|
assert.equal(error.status, 'error');
|
|
assert.doesNotMatch(error.summary, /Cannot find module|Require stack/);
|
|
}
|
|
}));
|