59 lines
2.4 KiB
JavaScript
59 lines
2.4 KiB
JavaScript
// Regression for #565: `irm .../install.ps1 | iex` crashed with
|
|
// "Cannot bind argument to parameter 'Path' because it is null."
|
|
//
|
|
// Two pipe-execution rules for install.ps1 (static checks — CI has no pwsh):
|
|
// 1. No top-level param() block. iex executes the file as a string, so a
|
|
// top-level param can never receive arguments and (depending on host)
|
|
// trips parsing. All logic lives in a function invoked at the bottom.
|
|
// 2. Script-path variables ($PSCommandPath / $MyInvocation.MyCommand.Path)
|
|
// are $null under iex — any use must be guarded, never passed straight
|
|
// into Split-Path (that was the #565 crash).
|
|
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
const REPO_ROOT = path.resolve(HERE, '..', '..');
|
|
const PS1 = fs.readFileSync(path.join(REPO_ROOT, 'install.ps1'), 'utf8');
|
|
|
|
// Strip comment lines so doc mentions of param()/path vars don't false-positive.
|
|
const code = PS1.split('\n').filter(l => !/^\s*#/.test(l)).join('\n');
|
|
|
|
test('#565 install.ps1 has no top-level param block (everything inside a function)', () => {
|
|
const beforeFunction = code.slice(0, code.indexOf('function '));
|
|
assert.ok(code.includes('function '), 'install.ps1 must wrap its logic in a function for iex piping');
|
|
assert.ok(
|
|
!/param\s*\(/i.test(beforeFunction),
|
|
'install.ps1 must not declare a top-level param() — it cannot receive args under `irm | iex` (issue #565)',
|
|
);
|
|
});
|
|
|
|
test('#565 install.ps1 never uses $MyInvocation.MyCommand.Path (null under iex)', () => {
|
|
assert.ok(
|
|
!/\$MyInvocation\.MyCommand\.Path/i.test(code),
|
|
'install.ps1 must not rely on $MyInvocation.MyCommand.Path — it is $null when piped to iex (issue #565)',
|
|
);
|
|
});
|
|
|
|
test('#565 install.ps1 guards $PSCommandPath before Split-Path', () => {
|
|
if (/\$PSCommandPath/i.test(code)) {
|
|
assert.match(
|
|
code,
|
|
/if\s*\(\s*\$PSCommandPath\s*\)/i,
|
|
'$PSCommandPath is $null under `irm | iex` — it must be truthiness-guarded before use (issue #565)',
|
|
);
|
|
}
|
|
});
|
|
|
|
test('#565 install.ps1 invokes its function at the bottom (script still does something)', () => {
|
|
const lastLines = code.trim().split('\n').slice(-3).join('\n');
|
|
assert.match(
|
|
lastLines,
|
|
/Install-Caveman/,
|
|
'install.ps1 must actually invoke Install-Caveman after defining it',
|
|
);
|
|
});
|