@@ -5,6 +5,8 @@ import { describe, expect, it } from "vitest";
55import { createScriptTestHarness } from "./test-helpers" ;
66
77const SCRIPT_PATH = "scripts/install.ps1" ;
8+ const ENTRYPOINT_RE =
9+ / \r ? \n \$ m a i n R e s u l t s = @ \( M a i n \) \r ? \n \$ i n s t a l l S u c c e e d e d = \$ m a i n R e s u l t s \. C o u n t - g t 0 - a n d \$ m a i n R e s u l t s \[ - 1 \] - e q \$ t r u e \r ? \n C o m p l e t e - I n s t a l l - S u c c e e d e d : \$ i n s t a l l S u c c e e d e d \s * $ / m;
810
911function extractFunctionBody ( source : string , name : string ) : string {
1012 const match = source . match (
@@ -35,10 +37,7 @@ function toPowerShellSingleQuotedLiteral(value: string): string {
3537}
3638
3739function createFailingNodeFixture ( source : string ) : string {
38- const scriptWithoutEntryPoint = source . replace (
39- / \r ? \n \$ i n s t a l l S u c c e e d e d = M a i n \r ? \n C o m p l e t e - I n s t a l l - S u c c e e d e d : \$ i n s t a l l S u c c e e d e d \s * $ / m,
40- "" ,
41- ) ;
40+ const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
4241 expect ( scriptWithoutEntryPoint ) . not . toBe ( source ) ;
4342
4443 return [
@@ -48,7 +47,8 @@ function createFailingNodeFixture(source: string): string {
4847 "function Ensure-ExecutionPolicy { return $true }" ,
4948 "function Ensure-Node { return $false }" ,
5049 "" ,
51- "$installSucceeded = Main" ,
50+ "$mainResults = @(Main)" ,
51+ "$installSucceeded = $mainResults.Count -gt 0 -and $mainResults[-1] -eq $true" ,
5252 "Complete-Install -Succeeded:$installSucceeded" ,
5353 "" ,
5454 ] . join ( "\n" ) ;
@@ -114,10 +114,7 @@ describe("install.ps1 failure handling", () => {
114114 runIfPowerShell ( "keeps npm chatter out of Main's success return value" , ( ) => {
115115 const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
116116 const scriptPath = join ( tempDir , "install.ps1" ) ;
117- const scriptWithoutEntryPoint = source . replace (
118- / \r ? \n \$ i n s t a l l S u c c e e d e d = M a i n \r ? \n C o m p l e t e - I n s t a l l - S u c c e e d e d : \$ i n s t a l l S u c c e e d e d \s * $ / m,
119- "" ,
120- ) ;
117+ const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
121118 writeFileSync (
122119 scriptPath ,
123120 [
@@ -149,4 +146,46 @@ describe("install.ps1 failure handling", () => {
149146 expect ( result . status ) . toBe ( 0 ) ;
150147 expect ( result . stderr ) . toBe ( "" ) ;
151148 } ) ;
149+
150+ runIfPowerShell ( "uses Main's final boolean result when helper output precedes success" , ( ) => {
151+ const tempDir = harness . createTempDir ( "openclaw-install-ps1-" ) ;
152+ const scriptPath = join ( tempDir , "install.ps1" ) ;
153+ const scriptWithoutEntryPoint = source . replace ( ENTRYPOINT_RE , "" ) ;
154+ writeFileSync (
155+ scriptPath ,
156+ [
157+ scriptWithoutEntryPoint ,
158+ "" ,
159+ "function Write-Banner { }" ,
160+ "function Ensure-ExecutionPolicy { return $true }" ,
161+ "function Ensure-Node { return $true }" ,
162+ "function Ensure-Git { return $true }" ,
163+ "function Add-ToPath { param([string]$Path) }" ,
164+ "function Install-OpenClawNpm {" ,
165+ " param([string]$Target = 'latest')" ,
166+ " Write-Output 'native chatter'" ,
167+ " return $true" ,
168+ "}" ,
169+ "function Invoke-NativeCommandCapture {" ,
170+ " param([string]$FilePath, [string[]]$Arguments)" ,
171+ " return @{ ExitCode = 0; Stdout = 'npm prefix'; Stderr = '' }" ,
172+ "}" ,
173+ "$NoOnboard = $true" ,
174+ "$mainResults = @(Main)" ,
175+ "$installSucceeded = $mainResults.Count -gt 0 -and $mainResults[-1] -eq $true" ,
176+ "Complete-Install -Succeeded:$installSucceeded" ,
177+ "" ,
178+ ] . join ( "\n" ) ,
179+ ) ;
180+ chmodSync ( scriptPath , 0o755 ) ;
181+
182+ const result = spawnSync (
183+ powershell ! ,
184+ [ "-NoLogo" , "-NoProfile" , "-ExecutionPolicy" , "Bypass" , "-File" , scriptPath ] ,
185+ { encoding : "utf8" } ,
186+ ) ;
187+
188+ expect ( result . status ) . toBe ( 0 ) ;
189+ expect ( result . stderr ) . toBe ( "" ) ;
190+ } ) ;
152191} ) ;
0 commit comments