mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-14 12:15:29 +02:00
test: cover legacy OpenCode home repair and uninstall
This commit is contained in:
@@ -786,6 +786,97 @@ function runTests() {
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('OpenCode pre-fix managed sources upgrade on repair and uninstall preserves user changes', () => {
|
||||
const tempDir = createTempDir('install-executor-opencode-upgrade-');
|
||||
try {
|
||||
const sourceRoot = path.join(tempDir, 'source');
|
||||
writeManifestSourceFixture(sourceRoot);
|
||||
const modulesPath = path.join(sourceRoot, 'manifests', 'install-modules.json');
|
||||
const manifest = JSON.parse(fs.readFileSync(modulesPath, 'utf8'));
|
||||
writeJson(sourceRoot, 'manifests/install-modules.json', {
|
||||
...manifest,
|
||||
modules: manifest.modules.map(module => ({
|
||||
...module,
|
||||
paths: ['.opencode'],
|
||||
targets: ['opencode'],
|
||||
})),
|
||||
});
|
||||
writeFile(sourceRoot, '.opencode/dist/index.js', 'export default () => ({});\n');
|
||||
writeFile(sourceRoot, '.opencode/dist/plugins/index.js', 'export {};\n');
|
||||
writeFile(sourceRoot, '.opencode/dist/tools/index.js', 'export {};\n');
|
||||
writeJson(sourceRoot, '.opencode/opencode.json', { plugin: ['./plugins'] });
|
||||
fs.cpSync(path.join(REPO_ROOT, '.opencode', 'tools'), path.join(sourceRoot, '.opencode', 'tools'), {
|
||||
recursive: true,
|
||||
});
|
||||
const currentContent = fs.readFileSync(path.join(REPO_ROOT, '.opencode', 'tools', 'index.ts'), 'utf8');
|
||||
const legacyContent = [
|
||||
'export { default as runTests } from "./run-tests.js"',
|
||||
'export { default as checkCoverage } from "./check-coverage.js"',
|
||||
'export { default as securityAudit } from "./security-audit.js"',
|
||||
'export { default as formatCode } from "./format-code.js"',
|
||||
'export { default as lintCheck } from "./lint-check.js"',
|
||||
'export { default as gitSummary } from "./git-summary.js"',
|
||||
'export { default as changedFiles } from "./changed-files.js"',
|
||||
'export { default as dependencyAnalyzer } from "./dependency-analyzer.js"',
|
||||
'',
|
||||
].join('\n');
|
||||
const digest = content => crypto.createHash('sha256').update(content).digest('hex');
|
||||
|
||||
for (const upgrade of [false, true]) {
|
||||
writeFile(sourceRoot, '.opencode/tools/index.ts', legacyContent);
|
||||
const homeDir = path.join(tempDir, upgrade ? 'repair-home' : 'uninstall-home');
|
||||
const projectRoot = path.join(tempDir, 'project');
|
||||
const plan = createManifestInstallPlan({
|
||||
sourceRoot, projectRoot, homeDir, target: 'opencode', moduleIds: ['fixture-core'],
|
||||
});
|
||||
applyInstallPlan(plan);
|
||||
const barrelPath = path.join(plan.targetRoot, 'tools', 'index.ts');
|
||||
const toolPath = path.join(plan.targetRoot, 'tools', 'run-tests.ts');
|
||||
const userPath = writeFile(plan.targetRoot, 'tools/user-note.txt', 'Keep my settings.\n');
|
||||
const installedState = JSON.parse(fs.readFileSync(plan.installStatePath, 'utf8'));
|
||||
const recordedBarrel = installedState.operations.find(operation => operation.destinationPath === barrelPath);
|
||||
assert.strictEqual(fs.readFileSync(barrelPath, 'utf8'), legacyContent);
|
||||
assert.strictEqual(recordedBarrel.contentTransform, undefined, 'TypeScript imports are copied, not transformed');
|
||||
assert.strictEqual(recordedBarrel.contentSha256, digest(legacyContent));
|
||||
assert.ok(fs.existsSync(toolPath));
|
||||
assert.ok(!fs.existsSync(path.join(plan.targetRoot, 'tools', 'run-tests.js')), 'legacy import has no adjacent JavaScript file');
|
||||
|
||||
// Upgrade the repository independently of the existing managed home.
|
||||
writeFile(sourceRoot, '.opencode/tools/index.ts', currentContent);
|
||||
const lifecycleOptions = { repoRoot: sourceRoot, projectRoot, homeDir, targets: ['opencode'] };
|
||||
if (upgrade) {
|
||||
const repaired = repairInstalledStates(lifecycleOptions);
|
||||
assert.strictEqual(repaired.results.length, 1);
|
||||
assert.strictEqual(repaired.results[0].status, 'repaired', JSON.stringify(repaired));
|
||||
assert.ok(repaired.results[0].repairedPaths.includes(barrelPath));
|
||||
const repairedContent = fs.readFileSync(barrelPath, 'utf8');
|
||||
assert.strictEqual(repairedContent, currentContent);
|
||||
assert.ok(repairedContent.includes('from "./run-tests.ts"'));
|
||||
assert.ok(!repairedContent.includes('.js"'), 'repair replaces legacy JavaScript specifiers');
|
||||
const repairedState = JSON.parse(fs.readFileSync(plan.installStatePath, 'utf8'));
|
||||
const repairedBarrel = repairedState.operations.find(operation => operation.destinationPath === barrelPath);
|
||||
assert.strictEqual(repairedBarrel.contentSha256, digest(currentContent));
|
||||
assert.notStrictEqual(repairedBarrel.contentSha256, recordedBarrel.contentSha256);
|
||||
assert.strictEqual(fs.readFileSync(userPath, 'utf8'), 'Keep my settings.\n');
|
||||
}
|
||||
|
||||
const modifiedContent = `${fs.readFileSync(toolPath, 'utf8')}\n// User test customisation.\n`;
|
||||
fs.writeFileSync(toolPath, modifiedContent, 'utf8');
|
||||
const uninstalled = uninstallInstalledStates(lifecycleOptions);
|
||||
assert.strictEqual(uninstalled.results.length, 1);
|
||||
assert.strictEqual(uninstalled.results[0].status, 'partial', JSON.stringify(uninstalled));
|
||||
assert.ok(uninstalled.results[0].removedPaths.includes(barrelPath));
|
||||
assert.ok(!fs.existsSync(barrelPath), 'both legacy and repaired managed barrels are recognised');
|
||||
assert.deepStrictEqual(uninstalled.results[0].retainedPaths, [toolPath]);
|
||||
assert.strictEqual(fs.readFileSync(toolPath, 'utf8'), modifiedContent);
|
||||
assert.strictEqual(fs.readFileSync(userPath, 'utf8'), 'Keep my settings.\n');
|
||||
assert.ok(fs.existsSync(plan.installStatePath), 'modified managed content retains its ownership record');
|
||||
}
|
||||
} finally {
|
||||
cleanup(tempDir);
|
||||
}
|
||||
})) passed++; else failed++;
|
||||
|
||||
if (test('dedupeCopyFileOperations keeps the last writer per destination (issue #2414)', () => {
|
||||
// Mirrors the OpenCode command scenario: a generic commands/<name>.md source
|
||||
// (preserve-relative-path) and an override .opencode/commands/<name>.md source
|
||||
|
||||
Reference in New Issue
Block a user