mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
Extract subprocess execution and process contracts into @orca/process-host with explicit public entry points, independent checks, and one Node 18 compatible CommonJS runtime. Migrate application consumers and integrate package builds, dependency selection, import enforcement, and runtime resource staging. Preserve daemon startup ownership and profile isolation, and qualify development rebuild recovery and concurrent output publication. Remove unnecessary aliases, test forwarding exports, and a duplicate output scan. Validation: full typechecks, changed-code quality, package and artifact checks, native Windows builds/types plus 306 runtime tests, and independent native reviews. Matched full E2E controls on original main and unchanged reviewed head both pass all 17 applicable tests (nine skips). Earlier intermittent E2E failures are documented in the PR; no assertions were weakened. The final combined-tree audit preserves both branches with no import-boundary violations.
125 lines
5.3 KiB
JavaScript
125 lines
5.3 KiB
JavaScript
import { readFileSync } from 'node:fs'
|
|
import { createRequire } from 'node:module'
|
|
import { dirname, join } from 'node:path'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { runProcessSync } from '@orca/process-host'
|
|
|
|
const hooks = readFileSync(new URL('../nsis/orca-installer-hooks.nsh', import.meta.url), 'utf8')
|
|
const processCheck = readFileSync(
|
|
new URL('../nsis/orca-process-check.nsh', import.meta.url),
|
|
'utf8'
|
|
)
|
|
const require = createRequire(import.meta.url)
|
|
const builderRequire = createRequire(require.resolve('electron-builder/package.json'))
|
|
const upstreamChecks = readFileSync(
|
|
join(
|
|
dirname(builderRequire.resolve('app-builder-lib/package.json')),
|
|
'templates/nsis/include/allowOnlyOneInstallerInstance.nsh'
|
|
),
|
|
'utf8'
|
|
)
|
|
|
|
function readPowerShellProbe(source = processCheck) {
|
|
const match = source.match(/nsExec::Exec `"\$PowerShellPath"(.*?) -(?:C|Command) "([^"\n]+)"`/)
|
|
if (!match) {
|
|
throw new Error('The NSIS PowerShell invocation was not found')
|
|
}
|
|
return { args: match[1].trim().split(/\s+/).filter(Boolean), command: match[2] }
|
|
}
|
|
|
|
describe('NSIS process-check integration', () => {
|
|
it('loads the capability hook through the installer and uninstaller include', () => {
|
|
expect(hooks).toContain('!include "${__FILEDIR__}\\orca-process-check.nsh"')
|
|
expect(processCheck).toMatch(/!macro customCheckAppRunning\b/)
|
|
expect(processCheck).toContain('!include "getProcessInfo.nsh"')
|
|
expect(processCheck).toMatch(/^Var pid$/m)
|
|
expect(processCheck).toMatch(/^Var \/GLOBAL IsPowerShellAvailable$/m)
|
|
})
|
|
|
|
it('keeps upstream process selection, retries, and installation-mode handling', () => {
|
|
expect(processCheck).toContain('!insertmacro _CHECK_APP_RUNNING')
|
|
expect(processCheck).not.toMatch(/!macro (?:FIND_PROCESS|KILL_PROCESS|_CHECK_APP_RUNNING)\b/)
|
|
expect(processCheck).not.toMatch(/\b(?:Stop-Process|taskkill|Set-ExecutionPolicy)\b/)
|
|
const findProcess = upstreamChecks.match(/!macro FIND_PROCESS\b[\s\S]*?!macroend/)?.[0]
|
|
if (!findProcess) {
|
|
throw new Error('The upstream process finder was not found')
|
|
}
|
|
expect(readPowerShellProbe().args).toEqual(readPowerShellProbe(findProcess).args)
|
|
})
|
|
})
|
|
|
|
describe.runIf(process.platform === 'win32')(
|
|
'NSIS capability probe under Restricted policy',
|
|
() => {
|
|
const policyReceipt = 'orca-nsis: restricted policy verified'
|
|
const queryFailureReceipt = 'orca-nsis: injected query failure'
|
|
const policyCheck = [
|
|
'function Test-OrcaRestrictedPolicy { param([string]$Scope)',
|
|
"try { $parameters = @{ ErrorAction = 'Stop' };",
|
|
'if ($Scope) { $parameters.Scope = $Scope };',
|
|
"return ((Get-ExecutionPolicy @parameters) -eq 'Restricted')",
|
|
'} catch { return $false } };',
|
|
// A failed getter must not fall through to the query's successful exit.
|
|
"if ((Test-OrcaRestrictedPolicy '__orca_invalid_scope__') -ne $false) { exit 11 };",
|
|
"if ((Test-OrcaRestrictedPolicy 'Process') -ne $true) { exit 10 };",
|
|
'if ((Test-OrcaRestrictedPolicy) -ne $true) { exit 10 };',
|
|
`[Console]::Out.WriteLine('${policyReceipt}');`
|
|
].join(' ')
|
|
|
|
// A watchdog against a wedged probe, not a latency budget: Windows PowerShell 5.1
|
|
// cold start under the packaged-CI process load has reached the former 20s cap. Matches
|
|
// the synchronous-probe ceiling in tests/tools/win-update-e2e/powershell-runner.mjs.
|
|
const PROBE_TIMEOUT_MS = 60_000
|
|
// Vitest's 30s default would otherwise cap the blocking spawn ahead of PROBE_TIMEOUT_MS.
|
|
const PROBE_TEST_TIMEOUT_MS = PROBE_TIMEOUT_MS + 15_000
|
|
|
|
function runProbe(arch, prefix = '') {
|
|
const { args, command } = readPowerShellProbe()
|
|
if (!process.env.SystemRoot) {
|
|
throw new Error('SystemRoot is required on Windows')
|
|
}
|
|
// The pwsh runner's module path points Windows PowerShell at incompatible PS7 modules.
|
|
const env = Object.fromEntries(
|
|
Object.entries(process.env).filter(([key]) => key.toLowerCase() !== 'psmodulepath')
|
|
)
|
|
return runProcessSync({
|
|
program: join(process.env.SystemRoot, arch, 'WindowsPowerShell', 'v1.0', 'powershell.exe'),
|
|
args: [...args, '-Command', `${policyCheck} ${prefix}${command}`],
|
|
env: {
|
|
...env,
|
|
ORCA_BACKGROUND_LAUNCH: '1',
|
|
PSExecutionPolicyPreference: 'Restricted'
|
|
},
|
|
timeoutMs: PROBE_TIMEOUT_MS
|
|
})
|
|
}
|
|
|
|
it.each(['SysWOW64', 'System32'])(
|
|
'%s permits the real inline process query',
|
|
{ timeout: PROBE_TEST_TIMEOUT_MS },
|
|
(arch) => {
|
|
const result = runProbe(arch)
|
|
expect(result.code, JSON.stringify(result)).toBe(0)
|
|
expect(result.timedOut).toBe(false)
|
|
expect(result.stdout).toContain(policyReceipt)
|
|
}
|
|
)
|
|
|
|
it.each(['SysWOW64', 'System32'])(
|
|
'%s rejects a failed process query',
|
|
{ timeout: PROBE_TEST_TIMEOUT_MS },
|
|
(arch) => {
|
|
const result = runProbe(
|
|
arch,
|
|
'function Get-CimInstance { [CmdletBinding()] param([string]$ClassName); ' +
|
|
`[Console]::Out.WriteLine('${queryFailureReceipt}'); Write-Error 'CIM unavailable' }; `
|
|
)
|
|
expect(result.code, JSON.stringify(result)).toBe(1)
|
|
expect(result.timedOut).toBe(false)
|
|
expect(result.stdout).toContain(policyReceipt)
|
|
expect(result.stdout).toContain(queryFailureReceipt)
|
|
}
|
|
)
|
|
}
|
|
)
|