mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-18 14:07:33 +02:00
Preserve contributor history while applying the exact reviewed current-main repair.
Source-PR: https://github.com/affaan-m/ECC/pull/2878
Source-Parent: 57da51c74b
Review-Manifest-SHA256: a9ff9a500e307cc9031349f086a0d754c3c72fddf999bb97dfbc9c6d7a7b04bd
308 lines
14 KiB
JavaScript
308 lines
14 KiB
JavaScript
/**
|
|
* Regression tests for #2859: status counts eligible top-level instinct files.
|
|
* The loader accepts .yaml/.yml/.md; files can contain zero or many instincts,
|
|
* so this count does not establish parsed-record counts or observer health.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const { spawnSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
const repoRoot = path.resolve(__dirname, '..', '..');
|
|
const skillRoot = path.join(repoRoot, 'skills', 'continuous-learning-v2');
|
|
const observerScript = path.join(skillRoot, 'agents', 'start-observer.sh');
|
|
const instinctCli = path.join(skillRoot, 'scripts', 'instinct-cli.py');
|
|
const bashBinary = process.env.ECC_TEST_BASH || (process.platform === 'win32' ? null : '/bin/bash');
|
|
const childTimeoutMs = 3000;
|
|
const childMaxBuffer = 64 * 1024;
|
|
|
|
class SkipTest extends Error {}
|
|
|
|
function toShellPath(filePath) {
|
|
const normalized = filePath.split(path.sep).join('/');
|
|
return normalized.replace(/^([A-Za-z]):\//, (_, drive) => `/${drive.toLowerCase()}/`);
|
|
}
|
|
|
|
function readAllowedExtensions() {
|
|
const cliSource = fs.readFileSync(instinctCli, 'utf8');
|
|
const match = cliSource.match(/ALLOWED_INSTINCT_EXTENSIONS\s*=\s*\(([^)]*)\)/);
|
|
assert.ok(match, 'ALLOWED_INSTINCT_EXTENSIONS not found in instinct-cli.py');
|
|
return match[1].split(',')
|
|
.map(part => part.trim().replace(/^["']|["']$/g, ''))
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function fixtureAt(root) {
|
|
const home = path.join(root, 'home');
|
|
const temp = path.join(root, 'tmp');
|
|
const bin = path.join(root, 'bin');
|
|
const homunculus = path.join(root, 'homunculus');
|
|
const instincts = path.join(homunculus, 'instincts', 'personal');
|
|
const unexpected = path.join(root, 'unexpected-command');
|
|
for (const directory of [home, temp, bin, instincts]) fs.mkdirSync(directory, { recursive: true });
|
|
for (const command of ['python', 'python3', 'git', 'claude']) {
|
|
fs.writeFileSync(path.join(bin, command),
|
|
'#!/bin/sh\nprintf unexpected > "$FIXTURE_UNEXPECTED_COMMAND"\nexit 97\n', { mode: 0o700 });
|
|
}
|
|
fs.writeFileSync(path.join(homunculus, 'observations.jsonl'), '');
|
|
return {
|
|
root, instincts, unexpected,
|
|
env: {
|
|
PATH: [toShellPath(bin), '/usr/bin', '/bin'].join(':'),
|
|
HOME: toShellPath(home), USERPROFILE: home, TMPDIR: toShellPath(temp),
|
|
TMP: temp, TEMP: temp, LC_ALL: 'C',
|
|
...(process.platform === 'win32' ? { SystemRoot: process.env.SystemRoot } : {}),
|
|
CLV2_NO_PROJECT: '1', CLV2_HOMUNCULUS_DIR: toShellPath(homunculus),
|
|
CLV2_CONFIG: toShellPath(path.join(root, 'absent-config.json')),
|
|
CLV2_PYTHON_CMD: toShellPath(path.join(bin, 'python3')),
|
|
FIXTURE_UNEXPECTED_COMMAND: toShellPath(unexpected),
|
|
},
|
|
};
|
|
}
|
|
|
|
function withFixture(callback, remove = fs.rmSync) {
|
|
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-observer-')));
|
|
let failed = false; let primary; let result;
|
|
try {
|
|
result = callback(fixtureAt(root));
|
|
} catch (error) {
|
|
failed = true;
|
|
primary = error;
|
|
}
|
|
try { remove(root, { recursive: true, force: true }); }
|
|
catch (error) { if (!failed) throw error; }
|
|
if (failed) throw primary;
|
|
return result;
|
|
}
|
|
|
|
function checkChild(result) {
|
|
if (result.error) throw result.error;
|
|
assert.strictEqual(result.status, 0, result.stderr || result.stdout || `child signal: ${result.signal}`);
|
|
}
|
|
|
|
function runStatus(files, { run = spawnSync, setup = () => {}, remove = fs.rmSync } = {}) {
|
|
return withFixture(fixture => {
|
|
for (const name of files) {
|
|
const target = path.join(fixture.instincts, name);
|
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
fs.writeFileSync(target, 'id: fixture\n');
|
|
}
|
|
setup(fixture);
|
|
// Only this foreground shell's own PID is advertised. No observer, sleep,
|
|
// provider or other background child is started or signalled.
|
|
const program = 'printf "%s\\n" "$$" > "$CLV2_HOMUNCULUS_DIR/.observer.pid"\nexec "$BASH" "$1" status';
|
|
const wrapper = path.join(fixture.root, 'status-wrapper.sh');
|
|
fs.writeFileSync(wrapper, `${program}\n`, { flag: 'wx', mode: 0o600 });
|
|
const result = run(bashBinary, ['--noprofile', '--norc', toShellPath(wrapper),
|
|
toShellPath(observerScript)], {
|
|
cwd: fixture.root, encoding: 'utf8', env: fixture.env,
|
|
timeout: childTimeoutMs, maxBuffer: childMaxBuffer, killSignal: 'SIGKILL',
|
|
});
|
|
checkChild(result);
|
|
assert.ok(!fs.existsSync(fixture.unexpected), 'status must not invoke Git, Python or a provider');
|
|
const lines = (result.stdout || '').split('\n').filter(line => line.startsWith('Instincts:'));
|
|
assert.strictEqual(lines.length, 1, `expected one count in status output:\n${result.stdout}`);
|
|
assert.match(lines[0], /^Instincts:\s+\d+\s*$/);
|
|
return Number(lines[0].split(':')[1].trim());
|
|
}, remove);
|
|
}
|
|
|
|
function fileLink(target, link, type = 'file') {
|
|
try { fs.symlinkSync(target, link, type); }
|
|
catch (error) {
|
|
const unsupported = ['ENOSYS', 'ENOTSUP'].includes(error.code)
|
|
|| (process.platform === 'win32' && ['EPERM', 'EACCES'].includes(error.code));
|
|
if (unsupported) throw new SkipTest(`symlink capability unavailable: ${error.code}`);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function shellTest(fn) {
|
|
return () => {
|
|
if (!bashBinary) throw new SkipTest('requires bash; set ECC_TEST_BASH on Windows');
|
|
return fn();
|
|
};
|
|
}
|
|
|
|
function cleanupTests() {
|
|
return [
|
|
['status child has a private environment and bounded execution', () => {
|
|
let root;
|
|
assert.strictEqual(runStatus([], { run: (_command, args, options) => {
|
|
root = options.cwd;
|
|
assert.strictEqual(options.timeout, childTimeoutMs);
|
|
assert.strictEqual(options.maxBuffer, childMaxBuffer);
|
|
assert.strictEqual(options.killSignal, 'SIGKILL');
|
|
assert.strictEqual(options.env.CLV2_NO_PROJECT, '1');
|
|
for (const key of ['BASH_ENV', 'ENV', 'NODE_OPTIONS', 'ANTHROPIC_API_KEY', 'CLAUDE_PROJECT_DIR']) {
|
|
assert.ok(!Object.hasOwn(options.env, key), `unexpected inherited ${key}`);
|
|
}
|
|
assert.ok(options.env.CLV2_CONFIG.endsWith('/absent-config.json'));
|
|
assert.ok(!fs.existsSync(options.env.CLV2_CONFIG));
|
|
assert.strictEqual(args.at(-1), toShellPath(observerScript));
|
|
const program = args.includes('-c') ? args[3] : fs.readFileSync(path.join(root, 'status-wrapper.sh'), 'utf8');
|
|
assert.match(program, /\nexec "\$BASH" "\$1" status\n?$/);
|
|
assert.doesNotMatch(program, /sleep|kill|&/);
|
|
return { status: 0, stdout: 'Instincts: 0\n', stderr: '' };
|
|
} }), 0);
|
|
assert.ok(!fs.existsSync(root));
|
|
}],
|
|
...['timeout', 'nonzero', 'spawn error'].map(kind => [`${kind} cleans its private fixture`, () => {
|
|
let root;
|
|
const failure = Object.assign(new Error(kind), { code: kind === 'timeout' ? 'ETIMEDOUT' : 'EIO' });
|
|
assert.throws(() => runStatus([], { run: (_command, _args, options) => {
|
|
root = options.cwd;
|
|
return kind === 'nonzero' ? { status: 7, stdout: '', stderr: 'fixture rejected' } : { error: failure };
|
|
} }), error => kind === 'nonzero' ? /fixture rejected/.test(error.message) : error === failure);
|
|
assert.ok(!fs.existsSync(root));
|
|
}]),
|
|
['cleanup preserves frozen and falsy primary failures', () => {
|
|
for (const primary of [Object.freeze(new Error('primary')), null, false, 0, undefined]) {
|
|
let root; let caught = false;
|
|
try {
|
|
withFixture(value => { root = value.root; throw primary; }, (value, options) => {
|
|
fs.rmSync(value, options); throw new Error('cleanup');
|
|
});
|
|
} catch (error) { caught = true; assert.strictEqual(error, primary); }
|
|
assert.ok(caught);
|
|
assert.ok(!fs.existsSync(root));
|
|
}
|
|
}],
|
|
['cleanup failure is reported when the fixture otherwise succeeds', () => {
|
|
let root;
|
|
const failure = new Error('cleanup');
|
|
assert.throws(() => withFixture(value => { root = value.root; }, (value, options) => {
|
|
fs.rmSync(value, options); throw failure;
|
|
}), error => error === failure);
|
|
assert.ok(!fs.existsSync(root));
|
|
}],
|
|
];
|
|
}
|
|
|
|
function buildTests() {
|
|
const allowed = readAllowedExtensions();
|
|
return [
|
|
['the loader still declares several instinct extensions', () => {
|
|
assert.ok(allowed.length >= 3, `expected several extensions, got ${allowed}`);
|
|
}],
|
|
...allowed.map(ext => [`status counts ${ext} - the loader accepts it`, shellTest(() => {
|
|
assert.strictEqual(runStatus([`one${ext}`]), 1);
|
|
})]),
|
|
['status does not recurse - the loader does not', shellTest(() => {
|
|
assert.strictEqual(runStatus(['a.md', 'nested/deep.yaml']), 1);
|
|
})],
|
|
['status skips directories', shellTest(() => {
|
|
assert.strictEqual(runStatus([], { setup: value => fs.mkdirSync(path.join(value.instincts, 'directory.md')) }), 0);
|
|
})],
|
|
['status matches case-insensitively', shellTest(() => {
|
|
assert.strictEqual(runStatus(['a.YAML', 'b.YmL', 'c.MD']), 3);
|
|
})],
|
|
['start-observer.sh parses', shellTest(() => withFixture(fixture => {
|
|
checkChild(spawnSync(bashBinary, ['--noprofile', '--norc', '-n', toShellPath(observerScript)], {
|
|
cwd: fixture.root, env: fixture.env, encoding: 'utf8',
|
|
timeout: childTimeoutMs, maxBuffer: childMaxBuffer, killSignal: 'SIGKILL',
|
|
}));
|
|
}))],
|
|
['markdown instincts are counted', shellTest(() => {
|
|
assert.strictEqual(runStatus(['a.md', 'b.md', 'c.md']), 3);
|
|
})],
|
|
['the count matches the loader eligible-file rules', shellTest(() => {
|
|
assert.strictEqual(runStatus(['a.md', 'b.yaml', 'c.yml', 'd.YAML', 'notes.txt', 'nested/deep.md']), 4);
|
|
})],
|
|
['an empty instincts directory reports 0', shellTest(() => {
|
|
assert.strictEqual(runStatus([]), 0);
|
|
})],
|
|
['hidden stems count but dot-only extension names have no suffix', shellTest(() => {
|
|
assert.strictEqual(runStatus(['.note.MD', '.yaml', '.YML', '.md']), 1);
|
|
})],
|
|
['newlines and spaces in one eligible filename count once', shellTest(() => {
|
|
assert.strictEqual(runStatus(['two\nlines with spaces.MD']), 1);
|
|
})],
|
|
['shell metacharacters in a filename remain inert data', shellTest(() => {
|
|
assert.strictEqual(runStatus(['$(touch unwanted).md'], { setup: value => {
|
|
assert.ok(!fs.existsSync(path.join(value.root, 'unwanted')));
|
|
}, run: (command, args, options) => {
|
|
const result = spawnSync(command, args, options);
|
|
assert.ok(!fs.existsSync(path.join(options.cwd, 'unwanted')));
|
|
return result;
|
|
} }), 1);
|
|
})],
|
|
['status dispatches a fixed private wrapper file without inline shell code', () => {
|
|
let root;
|
|
assert.strictEqual(runStatus([], { run: (_command, args, options) => {
|
|
root = options.cwd;
|
|
assert.deepStrictEqual(args.slice(0, 2), ['--noprofile', '--norc']);
|
|
assert.ok(!args.includes('-c'), 'status must dispatch a wrapper file, not inline code');
|
|
assert.strictEqual(args.length, 4);
|
|
const wrapper = path.join(root, 'status-wrapper.sh');
|
|
assert.strictEqual(args[2], toShellPath(wrapper));
|
|
assert.strictEqual(args[3], toShellPath(observerScript));
|
|
assert.strictEqual(fs.readFileSync(wrapper, 'utf8'),
|
|
'printf "%s\\n" "$$" > "$CLV2_HOMUNCULUS_DIR/.observer.pid"\nexec "$BASH" "$1" status\n');
|
|
if (process.platform !== 'win32') assert.strictEqual(fs.statSync(wrapper).mode & 0o777, 0o600);
|
|
return { status: 0, stdout: 'Instincts: 0\n', stderr: '' };
|
|
} }), 0);
|
|
assert.ok(!fs.existsSync(root));
|
|
}],
|
|
['status target path metacharacters remain data and exec retains the advertised PID', shellTest(() => {
|
|
let target;
|
|
assert.strictEqual(runStatus([], { setup: fixture => {
|
|
target = path.join(fixture.root, 'status \' $() `literal` ; &.sh');
|
|
fs.writeFileSync(target,
|
|
'[ "$#" -eq 1 ] && [ "$1" = status ] || exit 96\n'
|
|
+ 'IFS= read -r advertised < "$CLV2_HOMUNCULUS_DIR/.observer.pid"\n'
|
|
+ '[ "$advertised" = "$$" ] || exit 95\n'
|
|
+ 'printf "Instincts: 0\\n"\n', { flag: 'wx', mode: 0o600 });
|
|
}, run: (command, args, options) => {
|
|
assert.strictEqual(args.at(-1), toShellPath(observerScript));
|
|
return spawnSync(command, [...args.slice(0, -1), toShellPath(target)], options);
|
|
} }), 0);
|
|
assert.ok(!fs.existsSync(target));
|
|
})],
|
|
['linked regular files count without traversing directory links', shellTest(() => {
|
|
assert.strictEqual(runStatus(['a.md', 'b.yaml', 'c.yml', 'd.YAML', '.note.MD',
|
|
'.md', '.yaml', '.YML', 'notes.txt', 'nested/deep.md'], { setup: value => {
|
|
const outside = path.join(value.root, 'outside');
|
|
fs.mkdirSync(outside);
|
|
const target = path.join(outside, 'regular.txt');
|
|
fs.writeFileSync(target, 'private fixture\n');
|
|
fs.writeFileSync(path.join(outside, 'deep.md'), 'not top-level\n');
|
|
fs.mkdirSync(path.join(value.instincts, 'directory.md'));
|
|
fileLink(target, path.join(value.instincts, 'linked.MD'));
|
|
fileLink(outside, path.join(value.instincts, 'directory-link.yaml'), 'dir');
|
|
fileLink(path.join(outside, 'missing'), path.join(value.instincts, 'dangling.yml'));
|
|
} }), 6);
|
|
})],
|
|
...cleanupTests(),
|
|
];
|
|
}
|
|
|
|
function main() {
|
|
console.log('\n=== Testing observer status instinct count (#2859) ===\n');
|
|
let passed = 0; let failed = 0; let skipped = 0; let tests;
|
|
try { tests = buildTests(); }
|
|
catch (error) {
|
|
console.error(` FAIL could not build the test list: ${error.message}`);
|
|
tests = []; failed += 1;
|
|
}
|
|
for (const [name, fn] of tests) {
|
|
try { fn(); console.log(` PASS ${name}`); passed += 1; }
|
|
catch (error) {
|
|
if (error instanceof SkipTest) {
|
|
console.log(` SKIP ${name}: ${error.message}`); skipped += 1;
|
|
} else {
|
|
console.error(` FAIL ${name}: ${error.message}`); failed += 1;
|
|
}
|
|
}
|
|
}
|
|
console.log(`\n Passed: ${passed}\n Failed: ${failed}\n Skipped: ${skipped}`);
|
|
// Let piped summary output drain before the aggregate runner reads it.
|
|
if (failed > 0) process.exitCode = 1;
|
|
}
|
|
|
|
main();
|