blob: b9095b837827d8a0ada607d115be1ebc2ce04655 [file]
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { spawnSync } from 'node:child_process';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
const workflowUrl = new URL('../.github/workflows/windows-baseline.yml', import.meta.url);
const processIdentityScriptUrl = new URL('./windows-process-identity.ps1', import.meta.url);
function powerShellArgs(command) {
return ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', command];
}
function resolvePowerShellExecutable(spawn = spawnSync) {
const failures = [];
for (const executable of ['pwsh.exe', 'powershell.exe']) {
const probe = spawn(executable, powerShellArgs('exit 0'), {
encoding: 'utf8',
});
if (probe.status === 0) return executable;
failures.push(
`${executable}: ${probe.error?.message || probe.stderr || probe.stdout || `exit ${probe.status}`}`,
);
}
assert.fail(`No usable PowerShell executable (${failures.join('; ')})`);
}
test('PowerShell resolution covers the documented Windows PowerShell fallback', () => {
for (const pwshFailure of [
{
status: null,
error: Object.assign(new Error('spawnSync pwsh.exe ENOENT'), { code: 'ENOENT' }),
},
{ status: 1, error: undefined },
]) {
const calls = [];
const executable = resolvePowerShellExecutable((command, args, options) => {
calls.push({ command, args, options });
return command === 'pwsh.exe'
? { ...pwshFailure, stdout: '', stderr: '' }
: { status: 0, error: undefined, stdout: '', stderr: '' };
});
assert.equal(executable, 'powershell.exe');
assert.deepEqual(
calls.map(({ command }) => command),
['pwsh.exe', 'powershell.exe'],
);
assert.deepEqual(calls[0].args, powerShellArgs('exit 0'));
assert.equal(calls[0].options.encoding, 'utf8');
}
});
test('Windows baseline workflow keeps its non-blocking evidence contract', async () => {
const workflow = await readFile(workflowUrl, 'utf8');
assert.match(workflow, /^\s+runs-on: windows-latest$/mu);
assert.match(workflow, /^\s+continue-on-error: true$/mu);
assert.match(workflow, /^\s+timeout-minutes: 45$/mu);
const stepIds = [...workflow.matchAll(/^\s+- id: ([a-z]+)$/gmu)].map((match) => match[1]);
assert.deepEqual(stepIds, [
'install',
'build',
'inventory',
'scripts',
'smoke',
'storage',
'processes',
]);
for (const stepId of stepIds) {
assert.match(workflow, new RegExp(`\\$\\{\\{ steps\\.${stepId}\\.outcome \\}\\}`, 'u'));
}
for (const command of [
'npm.cmd ci',
'npm.cmd run build:test',
'npm.cmd run windows:inventory',
'npm.cmd run test:scripts',
'npm.cmd run smoke:windows',
'node.exe scripts/run-workspace-tests-parallel.mjs --concurrency=1 --workspaces=packages/storage',
]) {
assert.ok(workflow.includes(command), command);
}
assert.match(workflow, /Get-CimInstance Win32_Process/u);
assert.match(workflow, /name: Capture process baseline/u);
assert.match(workflow, /process-baseline\.json/u);
assert.match(workflow, /CreationDate/u);
assert.match(workflow, /\. \.\/scripts\/windows-process-identity\.ps1/u);
assert.equal(workflow.match(/Get-WindowsProcessIdentityKey/gmu)?.length, 4);
assert.match(workflow, /HashSet\[string\]/u);
assert.match(workflow, /HashSet\[int\]/u);
assert.doesNotMatch(workflow, /CommandLine -match/u);
assert.match(workflow, /\$treeProcessIds\.Contains\(\$process\.ParentProcessId\)/u);
assert.match(workflow, /residual-process-tree\.json/u);
assert.match(workflow, /taskkill\.exe \/PID \$process\.ProcessId \/T \/F/u);
assert.match(workflow, /residual-processes-after-cleanup\.json/u);
assert.match(workflow, /\$unreaped\.Count -gt 0/u);
assert.match(workflow, /\$exitCode = \$LASTEXITCODE/u);
assert.match(
workflow,
/--workspaces=packages\/storage \*> "\$env:WINDOWS_BASELINE_LOG_DIR\/storage\.log"/u,
);
assert.match(workflow, /Get-Content "\$env:WINDOWS_BASELINE_LOG_DIR\/storage\.log"/u);
// Pin-short-comment contract: the workflow must pin upload-artifact to a
// full SHA and annotate it with the exact version it resolves to. The major
// is intentionally not asserted — dependabot may bump it — but the
// annotation must stay truthful.
assert.match(workflow, /actions\/upload-artifact@[0-9a-f]{40} # v\d+\.\d+\.\d+/u);
assert.match(workflow, /name: windows-baseline/u);
assert.match(workflow, /retention-days: 14/u);
});
test('Windows process identity matches a non-empty JSON baseline to a live process object', {
skip: process.platform !== 'win32',
}, () => {
const fixture = String.raw`
. '${fileURLToPath(processIdentityScriptUrl).replaceAll("'", "''")}'
$captured = [pscustomobject]@{
Processes = @([pscustomobject]@{
ProcessId = 4242
CreationDate = [DateTimeOffset]::Parse('2026-08-05T08:09:10.1234567+08:00').ToUniversalTime().ToString('o')
})
} | ConvertTo-Json -Depth 3 | ConvertFrom-Json
$live = [pscustomobject]@{
ProcessId = 4242
CreationDate = [DateTime]::Parse('2026-08-05T00:09:10.1234567Z').ToUniversalTime()
}
if ((Get-WindowsProcessIdentityKey $captured.Processes) -ne (Get-WindowsProcessIdentityKey $live)) {
exit 1
}
`;
const result = spawnSync(resolvePowerShellExecutable(), powerShellArgs(fixture), {
encoding: 'utf8',
});
assert.equal(result.status, 0, result.error?.message || result.stderr || result.stdout);
});