@@ -11,8 +11,8 @@ describe("restart-helper", () => {
1111 const originalPlatform = process . platform ;
1212 const originalGetUid = process . getuid ;
1313
14- async function prepareAndReadScript ( env : Record < string , string > ) {
15- const scriptPath = await prepareRestartScript ( env ) ;
14+ async function prepareAndReadScript ( env : Record < string , string > , gatewayPort = 18789 ) {
15+ const scriptPath = await prepareRestartScript ( env , gatewayPort ) ;
1616 expect ( scriptPath ) . toBeTruthy ( ) ;
1717 const content = await fs . readFile ( scriptPath ! , "utf-8" ) ;
1818 return { scriptPath : scriptPath ! , content } ;
@@ -22,6 +22,39 @@ describe("restart-helper", () => {
2222 await fs . unlink ( scriptPath ) ;
2323 }
2424
25+ function expectWindowsRestartWaitOrdering ( content : string , port = 18789 ) {
26+ const endCommand = 'schtasks /End /TN "' ;
27+ const pollAttemptsInit = "set /a attempts=0" ;
28+ const pollLabel = ":wait_for_port_release" ;
29+ const pollAttemptIncrement = "set /a attempts+=1" ;
30+ const pollNetstatCheck = `netstat -ano | findstr /R /C:":${ port } .*LISTENING" >nul` ;
31+ const forceKillLabel = ":force_kill_listener" ;
32+ const forceKillCommand = "taskkill /F /PID %%P >nul 2>&1" ;
33+ const portReleasedLabel = ":port_released" ;
34+ const runCommand = 'schtasks /Run /TN "' ;
35+ const endIndex = content . indexOf ( endCommand ) ;
36+ const attemptsInitIndex = content . indexOf ( pollAttemptsInit , endIndex ) ;
37+ const pollLabelIndex = content . indexOf ( pollLabel , attemptsInitIndex ) ;
38+ const pollAttemptIncrementIndex = content . indexOf ( pollAttemptIncrement , pollLabelIndex ) ;
39+ const pollNetstatCheckIndex = content . indexOf ( pollNetstatCheck , pollAttemptIncrementIndex ) ;
40+ const forceKillLabelIndex = content . indexOf ( forceKillLabel , pollNetstatCheckIndex ) ;
41+ const forceKillCommandIndex = content . indexOf ( forceKillCommand , forceKillLabelIndex ) ;
42+ const portReleasedLabelIndex = content . indexOf ( portReleasedLabel , forceKillCommandIndex ) ;
43+ const runIndex = content . indexOf ( runCommand , portReleasedLabelIndex ) ;
44+
45+ expect ( endIndex ) . toBeGreaterThanOrEqual ( 0 ) ;
46+ expect ( attemptsInitIndex ) . toBeGreaterThan ( endIndex ) ;
47+ expect ( pollLabelIndex ) . toBeGreaterThan ( attemptsInitIndex ) ;
48+ expect ( pollAttemptIncrementIndex ) . toBeGreaterThan ( pollLabelIndex ) ;
49+ expect ( pollNetstatCheckIndex ) . toBeGreaterThan ( pollAttemptIncrementIndex ) ;
50+ expect ( forceKillLabelIndex ) . toBeGreaterThan ( pollNetstatCheckIndex ) ;
51+ expect ( forceKillCommandIndex ) . toBeGreaterThan ( forceKillLabelIndex ) ;
52+ expect ( portReleasedLabelIndex ) . toBeGreaterThan ( forceKillCommandIndex ) ;
53+ expect ( runIndex ) . toBeGreaterThan ( portReleasedLabelIndex ) ;
54+
55+ expect ( content ) . not . toContain ( "timeout /t 3 /nobreak >nul" ) ;
56+ }
57+
2558 beforeEach ( ( ) => {
2659 vi . resetAllMocks ( ) ;
2760 } ) ;
@@ -91,6 +124,7 @@ describe("restart-helper", () => {
91124 expect ( content ) . toContain ( "@echo off" ) ;
92125 expect ( content ) . toContain ( 'schtasks /End /TN "OpenClaw Gateway"' ) ;
93126 expect ( content ) . toContain ( 'schtasks /Run /TN "OpenClaw Gateway"' ) ;
127+ expectWindowsRestartWaitOrdering ( content ) ;
94128 // Batch self-cleanup
95129 expect ( content ) . toContain ( 'del "%~f0"' ) ;
96130 await cleanupScript ( scriptPath ) ;
@@ -105,6 +139,25 @@ describe("restart-helper", () => {
105139 } ) ;
106140 expect ( content ) . toContain ( 'schtasks /End /TN "OpenClaw Gateway (custom)"' ) ;
107141 expect ( content ) . toContain ( 'schtasks /Run /TN "OpenClaw Gateway (custom)"' ) ;
142+ expectWindowsRestartWaitOrdering ( content ) ;
143+ await cleanupScript ( scriptPath ) ;
144+ } ) ;
145+
146+ it ( "uses passed gateway port for port polling on Windows" , async ( ) => {
147+ Object . defineProperty ( process , "platform" , { value : "win32" } ) ;
148+ const customPort = 9999 ;
149+
150+ const { scriptPath, content } = await prepareAndReadScript (
151+ {
152+ OPENCLAW_PROFILE : "default" ,
153+ } ,
154+ customPort ,
155+ ) ;
156+ expect ( content ) . toContain ( `netstat -ano | findstr /R /C:":${ customPort } .*LISTENING" >nul` ) ;
157+ expect ( content ) . toContain (
158+ `for /f "tokens=5" %%P in ('netstat -ano ^| findstr /R /C:":${ customPort } .*LISTENING"') do (` ,
159+ ) ;
160+ expectWindowsRestartWaitOrdering ( content , customPort ) ;
108161 await cleanupScript ( scriptPath ) ;
109162 } ) ;
110163
@@ -135,6 +188,7 @@ describe("restart-helper", () => {
135188 OPENCLAW_PROFILE : "production" ,
136189 } ) ;
137190 expect ( content ) . toContain ( 'schtasks /End /TN "OpenClaw Gateway (production)"' ) ;
191+ expectWindowsRestartWaitOrdering ( content ) ;
138192 await cleanupScript ( scriptPath ) ;
139193 } ) ;
140194
0 commit comments