mirror of
https://github.com/affaan-m/ECC.git
synced 2026-10-14 20:25:26 +02:00
149 lines
7.1 KiB
JavaScript
149 lines
7.1 KiB
JavaScript
/**
|
|
* Tests for tests/lib/helpers/windows-test-env.js
|
|
*
|
|
* Run with: node tests/lib/windows-test-env.test.js
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const assert = require('assert');
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
const vm = require('vm');
|
|
|
|
const { test, banner, summary } = require('./helpers/mini-test-runner');
|
|
const { spawnSync } = require('child_process');
|
|
const { withoutShadowingClaude, toRelativeTarPath, resolvePython } = require('./helpers/windows-test-env');
|
|
|
|
/** Load the unchanged helper with controlled process results, keeping its real probe. */
|
|
function loadWithSpawn(spawn) {
|
|
const filename = path.join(__dirname, 'helpers', 'windows-test-env.js');
|
|
const module = { exports: {} };
|
|
vm.runInNewContext(fs.readFileSync(filename, 'utf8'), {
|
|
module,
|
|
require: name => name === 'child_process' ? { spawnSync: spawn } : require(name),
|
|
process,
|
|
}, { filename });
|
|
return module.exports;
|
|
}
|
|
|
|
banner('windows-test-env helper');
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'windows-test-env-'));
|
|
const exeDir = path.join(root, 'native-install');
|
|
const comDir = path.join(root, 'com-install');
|
|
const cmdDir = path.join(root, 'npm-global');
|
|
const plainDir = path.join(root, 'tools');
|
|
for (const dir of [exeDir, comDir, cmdDir, plainDir]) fs.mkdirSync(dir);
|
|
fs.writeFileSync(path.join(exeDir, 'claude.exe'), '');
|
|
fs.writeFileSync(path.join(comDir, 'claude.com'), '');
|
|
// A .cmd shim cannot shadow a fake .cmd placed earlier on PATH, so it stays.
|
|
fs.writeFileSync(path.join(cmdDir, 'claude.cmd'), '');
|
|
|
|
try {
|
|
if (test('drops PATH entries holding claude.exe or claude.com on Windows', () => {
|
|
const pathValue = [plainDir, exeDir, cmdDir, comDir].join(';');
|
|
assert.strictEqual(withoutShadowingClaude(pathValue, 'win32'), [plainDir, cmdDir].join(';'));
|
|
})) passed++; else failed++;
|
|
|
|
if (test('drops empty entries and tolerates a missing PATH on Windows', () => {
|
|
assert.strictEqual(withoutShadowingClaude(`;${plainDir};;`, 'win32'), plainDir);
|
|
assert.strictEqual(withoutShadowingClaude(undefined, 'win32'), '');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('leaves PATH untouched off Windows', () => {
|
|
const pathValue = [plainDir, exeDir].join(':');
|
|
assert.strictEqual(withoutShadowingClaude(pathValue, 'linux'), pathValue);
|
|
assert.strictEqual(withoutShadowingClaude(undefined, 'darwin'), '');
|
|
})) passed++; else failed++;
|
|
|
|
if (test('names an archive relatively with forward slashes and no drive letter', () => {
|
|
const cwd = path.join(root, 'extract', 'node_modules');
|
|
const archive = path.join(root, 'pkg-1.0.0.tgz');
|
|
const relative = toRelativeTarPath(cwd, archive);
|
|
assert.ok(!relative.includes(':'), `tar reads a colon as host:path: ${relative}`);
|
|
assert.ok(!relative.includes('\\'), `expected forward slashes: ${relative}`);
|
|
assert.strictEqual(path.resolve(cwd, relative), archive);
|
|
})) passed++; else failed++;
|
|
|
|
// A probe that runs only the commands listed, as Python 3.
|
|
const probeFor = working => (command, args) => working.includes([command, ...args.slice(0, args.indexOf('-c'))].join(' '));
|
|
|
|
if (test('skips a python3 that does not run Python, such as the Microsoft Store alias stub', () => {
|
|
assert.deepStrictEqual(resolvePython({ platform: 'win32', probe: probeFor(['py -3']) }), { command: 'py', prefix: ['-3'] });
|
|
assert.deepStrictEqual(resolvePython({ platform: 'win32', probe: probeFor(['python', 'py -3']) }), { command: 'python', prefix: [] });
|
|
assert.deepStrictEqual(resolvePython({ platform: 'win32', probe: probeFor(['python3']) }), { command: 'python3', prefix: [] });
|
|
})) passed++; else failed++;
|
|
|
|
if (test('prefers python3 off Windows', () => {
|
|
assert.deepStrictEqual(resolvePython({ platform: 'linux', probe: probeFor(['python3', 'python']) }), { command: 'python3', prefix: [] });
|
|
assert.deepStrictEqual(resolvePython({ platform: 'darwin', probe: probeFor(['python']) }), { command: 'python', prefix: [] });
|
|
})) passed++; else failed++;
|
|
|
|
if (test('returns null when no candidate runs Python 3', () => {
|
|
assert.strictEqual(resolvePython({ platform: 'win32', probe: () => false }), null);
|
|
assert.strictEqual(resolvePython({ platform: 'linux', probe: () => false }), null);
|
|
})) passed++; else failed++;
|
|
|
|
if (test('real probe accepts success and preserves Windows launcher arguments', () => {
|
|
const calls = [];
|
|
const helper = loadWithSpawn((command, args, options) => {
|
|
calls.push({ command, args: Array.from(args), options: { ...options } });
|
|
return { status: command === 'py' ? 0 : 9009 };
|
|
});
|
|
const python = helper.resolvePython({ platform: 'win32' });
|
|
assert.strictEqual(python.command, 'py');
|
|
assert.deepStrictEqual(Array.from(python.prefix), ['-3']);
|
|
assert.deepStrictEqual(calls.map(call => call.command), ['python', 'py']);
|
|
assert.deepStrictEqual(calls[1].args.slice(0, 2), ['-3', '-c']);
|
|
assert.deepStrictEqual(calls[1].options, { encoding: 'utf8', timeout: 15000 });
|
|
})) passed++; else failed++;
|
|
|
|
if (test('real probe rejects nonzero, missing commands and timed out processes', () => {
|
|
for (const result of [
|
|
{ status: 1 },
|
|
{ status: 9009 },
|
|
{ status: null, error: Object.assign(new Error('not found'), { code: 'ENOENT' }) },
|
|
{ status: null, error: Object.assign(new Error('timed out'), { code: 'ETIMEDOUT' }) },
|
|
]) {
|
|
const calls = [];
|
|
const helper = loadWithSpawn(command => { calls.push(command); return result; });
|
|
assert.strictEqual(helper.resolvePython({ platform: 'win32' }), null);
|
|
assert.deepStrictEqual(calls, ['python', 'py', 'python3']);
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
if (test('real probe code succeeds under known Python 3 and rejects Python 2', () => {
|
|
// Select independently so broken discovery cannot silently skip this control.
|
|
const known = [['python3'], ['python'], ['py', '-3']].find(([command, ...prefix]) => {
|
|
const result = spawnSync(command, [...prefix, '-c', 'import sys; print(sys.version_info[0])'], { encoding: 'utf8', timeout: 15000 });
|
|
return result.status === 0 && result.stdout.trim() === '3';
|
|
});
|
|
assert.ok(known, 'Python 3 is required for the Hermes test workflow');
|
|
for (const [major, expected] of [[3, true], [2, false]]) {
|
|
const helper = loadWithSpawn((_command, args, options) => {
|
|
const code = args[args.indexOf('-c') + 1];
|
|
const wrapped = `import sys; sys.version_info = (${major}, 0);\n${code}`;
|
|
return spawnSync(known[0], [...known.slice(1), '-c', wrapped], options);
|
|
});
|
|
assert.strictEqual(helper.resolvePython({ platform: 'win32' }) !== null, expected);
|
|
}
|
|
})) passed++; else failed++;
|
|
|
|
if (test('a resolved interpreter actually runs Python 3 here', () => {
|
|
const python = resolvePython();
|
|
if (!python) return;
|
|
const run = spawnSync(python.command, [...python.prefix, '-c', 'import sys; print(sys.version_info[0])'], { encoding: 'utf8' });
|
|
assert.strictEqual(run.status, 0, run.stderr);
|
|
assert.strictEqual(run.stdout.trim(), '3');
|
|
})) passed++; else failed++;
|
|
} finally {
|
|
fs.rmSync(root, { recursive: true, force: true });
|
|
}
|
|
|
|
summary(passed, failed);
|