@@ -177,6 +177,93 @@ describe("install.ps1 failure handling", () => {
177177 "" ,
178178 ] . join ( "\n" ) ,
179179 } ,
180+ {
181+ name : "scriptblock-failure" ,
182+ source : [
183+ scriptWithoutEntryPoint ,
184+ "" ,
185+ "function Write-Banner { }" ,
186+ "function Ensure-ExecutionPolicy { return $true }" ,
187+ "function Check-Node { return $false }" ,
188+ "function Install-Node { return $false }" ,
189+ "$caught = $false" ,
190+ "try {" ,
191+ ...ENTRYPOINT_LINES . map ( ( line ) => ` ${ line } ` ) ,
192+ "} catch {" ,
193+ " if ($_.Exception.Message -ne 'OpenClaw installation failed with exit code 1.') { throw }" ,
194+ " $caught = $true" ,
195+ "}" ,
196+ "if (-not $caught) { throw 'Install failure did not reach the caller' }" ,
197+ "" ,
198+ ] . join ( "\n" ) ,
199+ } ,
200+ {
201+ name : "noisy-git-failure" ,
202+ source : [
203+ scriptWithoutEntryPoint ,
204+ "" ,
205+ "function Write-Banner { }" ,
206+ "function Ensure-ExecutionPolicy { return $true }" ,
207+ "function Check-Node { return $true }" ,
208+ "function Check-ExistingOpenClaw { return $false }" ,
209+ "function Get-NpmCommandPath { return $null }" ,
210+ "function Install-OpenClawFromGit {" ,
211+ " Write-Output 'pnpm stdout before failure'" ,
212+ " return $false" ,
213+ "}" ,
214+ "function Ensure-OpenClawOnPath { throw 'should not continue after failed git install' }" ,
215+ "$InstallMethod = 'git'" ,
216+ "$GitDir = 'C:\\\\openclaw-test'" ,
217+ "$NoOnboard = $true" ,
218+ "$result = Main" ,
219+ 'if ($result -ne $false) { throw "Main returned $result" }' ,
220+ 'if ($script:InstallExitCode -ne 1) { throw "InstallExitCode=$script:InstallExitCode" }' ,
221+ "" ,
222+ ] . join ( "\n" ) ,
223+ } ,
224+ {
225+ name : "quiet-main-success" ,
226+ source : [
227+ scriptWithoutEntryPoint ,
228+ "" ,
229+ "function Write-Banner { }" ,
230+ "function Ensure-ExecutionPolicy { return $true }" ,
231+ "function Check-Node { return $true }" ,
232+ "function Check-ExistingOpenClaw { return $false }" ,
233+ "function Add-ToPath { param([string]$Path) }" ,
234+ "function Install-OpenClaw { Write-Output 'npm stdout'; return $true }" ,
235+ "function Ensure-OpenClawOnPath { return $true }" ,
236+ "function Refresh-GatewayServiceIfLoaded { }" ,
237+ "function Invoke-OpenClawCommand { return 'OpenClaw test-version' }" ,
238+ "$NoOnboard = $true" ,
239+ "$result = Main" ,
240+ "if ($result -is [array]) { throw 'Main returned an array' }" ,
241+ 'if ($result -ne $true) { throw "Main returned $result" }' ,
242+ "" ,
243+ ] . join ( "\n" ) ,
244+ } ,
245+ {
246+ name : "final-boolean-success" ,
247+ source : [
248+ scriptWithoutEntryPoint ,
249+ "" ,
250+ "function Write-Banner { }" ,
251+ "function Ensure-ExecutionPolicy { return $true }" ,
252+ "function Check-Node { return $true }" ,
253+ "function Check-ExistingOpenClaw { return $false }" ,
254+ "function Add-ToPath { param([string]$Path) }" ,
255+ "function Install-OpenClaw {" ,
256+ " Write-Output 'native chatter'" ,
257+ " return $true" ,
258+ "}" ,
259+ "function Ensure-OpenClawOnPath { return $true }" ,
260+ "function Refresh-GatewayServiceIfLoaded { }" ,
261+ "function Invoke-OpenClawCommand { return 'OpenClaw test-version' }" ,
262+ "$NoOnboard = $true" ,
263+ ...ENTRYPOINT_LINES ,
264+ "" ,
265+ ] . join ( "\n" ) ,
266+ } ,
180267 ] ;
181268 const tempDir = harness . createTempDir ( "openclaw-install-ps1-batch-" ) ;
182269 const fixtures = cases . map ( ( testCase , index ) => {
@@ -618,149 +705,22 @@ describe("install.ps1 failure handling", () => {
618705 } ) ;
619706
620707 runIfPowerShell ( "throws without killing the caller when run as a scriptblock" , ( ) => {
621- const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
622- const scriptPath = join ( tempDir , "install.ps1" ) ;
623- writeFileSync ( scriptPath , createFailingNodeFixture ( source ) ) ;
624- chmodSync ( scriptPath , 0o755 ) ;
625-
626- const command = [
627- "try {" ,
628- ` & ([scriptblock]::Create((Get-Content -LiteralPath ${ toPowerShellSingleQuotedLiteral ( scriptPath ) } -Raw)))` ,
629- "} catch {" ,
630- ' Write-Output "caught=$($_.Exception.Message)"' ,
631- "}" ,
632- 'Write-Output "alive-after-install"' ,
633- ] . join ( "\n" ) ;
634- const result = runPowerShell ( [ "-NoLogo" , "-NoProfile" , "-Command" , command ] ) ;
635-
636- expect ( result . status ) . toBe ( 0 ) ;
637- expect ( result . stdout ) . toContain ( "caught=OpenClaw installation failed with exit code 1." ) ;
638- expect ( result . stdout ) . toContain ( "alive-after-install" ) ;
708+ expectBatchedPowerShellCase ( "scriptblock-failure" ) ;
639709 } ) ;
640710
641711 runIfPowerShell ( "treats noisy Git install false as failure" , ( ) => {
642- const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
643- const scriptPath = join ( tempDir , "install.ps1" ) ;
644- const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
645- writeFileSync (
646- scriptPath ,
647- [
648- scriptWithoutEntryPoint ,
649- "" ,
650- "function Write-Banner { }" ,
651- "function Ensure-ExecutionPolicy { return $true }" ,
652- "function Check-Node { return $true }" ,
653- "function Check-ExistingOpenClaw { return $false }" ,
654- "function Get-NpmCommandPath { return $null }" ,
655- "function Install-OpenClawFromGit {" ,
656- " Write-Output 'pnpm stdout before failure'" ,
657- " return $false" ,
658- "}" ,
659- "function Ensure-OpenClawOnPath { throw 'should not continue after failed git install' }" ,
660- "$InstallMethod = 'git'" ,
661- "$GitDir = 'C:\\\\openclaw-test'" ,
662- "$NoOnboard = $true" ,
663- "$result = Main" ,
664- 'if ($result -ne $false) { throw "Main returned $result" }' ,
665- 'if ($script:InstallExitCode -ne 1) { throw "InstallExitCode=$script:InstallExitCode" }' ,
666- "" ,
667- ] . join ( "\n" ) ,
668- ) ;
669- chmodSync ( scriptPath , 0o755 ) ;
670-
671- const result = runPowerShell ( [
672- "-NoLogo" ,
673- "-NoProfile" ,
674- "-Command" ,
675- `. ${ toPowerShellSingleQuotedLiteral ( scriptPath ) } ` ,
676- ] ) ;
677-
678- expect ( result . status ) . toBe ( 0 ) ;
679- expect ( result . stderr ) . toBe ( "" ) ;
712+ expectBatchedPowerShellCase ( "noisy-git-failure" ) ;
680713 } ) ;
681714
682715 runIfPowerShell ( "preserves larger old-space NODE_OPTIONS aliases" , ( ) => {
683716 expectBatchedPowerShellCase ( "node-options" ) ;
684717 } ) ;
685718
686719 runIfPowerShell ( "keeps npm chatter out of Main's success return value" , ( ) => {
687- const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
688- const scriptPath = join ( tempDir , "install.ps1" ) ;
689- const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
690- writeFileSync (
691- scriptPath ,
692- [
693- scriptWithoutEntryPoint ,
694- "" ,
695- "function Write-Banner { }" ,
696- "function Ensure-ExecutionPolicy { return $true }" ,
697- "function Check-Node { return $true }" ,
698- "function Check-ExistingOpenClaw { return $false }" ,
699- "function Add-ToPath { param([string]$Path) }" ,
700- "function Install-OpenClaw { Write-Output 'npm stdout'; return $true }" ,
701- "function Ensure-OpenClawOnPath { return $true }" ,
702- "function Refresh-GatewayServiceIfLoaded { }" ,
703- "function Invoke-OpenClawCommand { return 'OpenClaw test-version' }" ,
704- "$NoOnboard = $true" ,
705- "$result = Main" ,
706- "if ($result -is [array]) { throw 'Main returned an array' }" ,
707- 'if ($result -ne $true) { throw "Main returned $result" }' ,
708- "" ,
709- ] . join ( "\n" ) ,
710- ) ;
711- chmodSync ( scriptPath , 0o755 ) ;
712-
713- const result = runPowerShell ( [
714- "-NoLogo" ,
715- "-NoProfile" ,
716- "-ExecutionPolicy" ,
717- "Bypass" ,
718- "-File" ,
719- scriptPath ,
720- ] ) ;
721-
722- expect ( result . status ) . toBe ( 0 ) ;
723- expect ( result . stderr ) . toBe ( "" ) ;
720+ expectBatchedPowerShellCase ( "quiet-main-success" ) ;
724721 } ) ;
725722
726723 runIfPowerShell ( "uses Main's final boolean result when helper output precedes success" , ( ) => {
727- const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
728- const scriptPath = join ( tempDir , "install.ps1" ) ;
729- const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
730- writeFileSync (
731- scriptPath ,
732- [
733- scriptWithoutEntryPoint ,
734- "" ,
735- "function Write-Banner { }" ,
736- "function Ensure-ExecutionPolicy { return $true }" ,
737- "function Check-Node { return $true }" ,
738- "function Check-ExistingOpenClaw { return $false }" ,
739- "function Add-ToPath { param([string]$Path) }" ,
740- "function Install-OpenClaw {" ,
741- " Write-Output 'native chatter'" ,
742- " return $true" ,
743- "}" ,
744- "function Ensure-OpenClawOnPath { return $true }" ,
745- "function Refresh-GatewayServiceIfLoaded { }" ,
746- "function Invoke-OpenClawCommand { return 'OpenClaw test-version' }" ,
747- "$NoOnboard = $true" ,
748- ...ENTRYPOINT_LINES ,
749- "" ,
750- ] . join ( "\n" ) ,
751- ) ;
752- chmodSync ( scriptPath , 0o755 ) ;
753-
754- const result = runPowerShell ( [
755- "-NoLogo" ,
756- "-NoProfile" ,
757- "-ExecutionPolicy" ,
758- "Bypass" ,
759- "-File" ,
760- scriptPath ,
761- ] ) ;
762-
763- expect ( result . status ) . toBe ( 0 ) ;
764- expect ( result . stderr ) . toBe ( "" ) ;
724+ expectBatchedPowerShellCase ( "final-boolean-success" ) ;
765725 } ) ;
766726} ) ;
0 commit comments