11import { mkdtempSync , readFileSync , rmSync } from 'node:fs'
22import { tmpdir } from 'node:os'
33import { join } from 'node:path'
4- import { afterEach , describe , expect , it } from 'vitest'
4+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
55import {
66 createMcpShimContent ,
77 createShimContent ,
@@ -10,8 +10,18 @@ import {
1010 shimPathForPlatform ,
1111} from '../../packages/desktop/src/main/cli-shim'
1212
13+ const execFileMock = vi . hoisted ( ( ) => vi . fn ( ) )
14+
15+ vi . mock ( 'node:child_process' , ( ) => ( {
16+ execFile : execFileMock ,
17+ } ) )
18+
1319let tempDirs : string [ ] = [ ]
1420
21+ beforeEach ( ( ) => {
22+ execFileMock . mockReset ( )
23+ } )
24+
1525afterEach ( ( ) => {
1626 for ( const dir of tempDirs ) {
1727 rmSync ( dir , { recursive : true , force : true } )
@@ -119,4 +129,68 @@ describe('Hermes Studio CLI shim', () => {
119129 expect ( readFileSync ( result . shimPath , 'utf-8' ) ) . toContain ( "WEBUI_SCRIPT='/resources/webui/bin/hermes-web-ui.mjs'" )
120130 expect ( readFileSync ( join ( homeDir , '.zprofile' ) , 'utf-8' ) ) . toContain ( 'export PATH="$HOME/bin:$PATH"' )
121131 } )
132+
133+ it ( 'updates Windows user PATH through PowerShell without corrupting Unicode entries' , async ( ) => {
134+ const existingPath = 'C:\\Users\\张三\\工具;C:\\Windows\\System32'
135+ let writtenPath = ''
136+ execFileMock . mockImplementation ( ( command , args , options , callback ) => {
137+ const script = Array . isArray ( args ) ? args . join ( ' ' ) : ''
138+ if ( command !== 'powershell.exe' ) {
139+ callback ( new Error ( `unexpected command: ${ command } ` ) )
140+ return
141+ }
142+ if ( script . includes ( 'GetEnvironmentVariable' ) ) {
143+ callback ( null , { stdout : Buffer . from ( existingPath , 'utf-8' ) . toString ( 'base64' ) , stderr : '' } )
144+ return
145+ }
146+ if ( script . includes ( 'SetEnvironmentVariable' ) ) {
147+ writtenPath = Buffer . from ( options . env . HERMES_STUDIO_WINDOWS_USER_PATH_B64 , 'base64' ) . toString ( 'utf-8' )
148+ callback ( null , { stdout : '' , stderr : '' } )
149+ return
150+ }
151+ callback ( new Error ( `unexpected PowerShell script: ${ script } ` ) )
152+ } )
153+
154+ const homeDir = tempHome ( )
155+ const result = await installHermesStudioCliShim ( {
156+ homeDir,
157+ platform : 'win32' ,
158+ executablePath : 'C:\\Program Files\\Hermes Studio\\Hermes Studio.exe' ,
159+ nodePath : 'C:\\Program Files\\Hermes Studio\\node.exe' ,
160+ webUiScriptPath : 'C:\\Program Files\\Hermes Studio\\resources\\webui\\bin\\hermes-web-ui.mjs' ,
161+ env : { Path : existingPath } ,
162+ } )
163+
164+ expect ( result . status ) . toBe ( 'installed' )
165+ expect ( result . pathUpdated ) . toBe ( true )
166+ expect ( execFileMock ) . toHaveBeenCalledTimes ( 2 )
167+ expect ( execFileMock ) . not . toHaveBeenCalledWith ( 'reg.exe' , expect . anything ( ) , expect . anything ( ) , expect . anything ( ) )
168+ expect ( writtenPath ) . toBe ( `${ join ( homeDir , 'bin' ) } ;${ existingPath } ` )
169+ } )
170+
171+ it ( 'does not rewrite Windows user PATH when the shim directory is already present' , async ( ) => {
172+ const homeDir = tempHome ( )
173+ const existingPath = `${ join ( homeDir , 'bin' ) } ;C:\\Users\\张三\\工具`
174+ execFileMock . mockImplementation ( ( command , args , _options , callback ) => {
175+ const script = Array . isArray ( args ) ? args . join ( ' ' ) : ''
176+ if ( command === 'powershell.exe' && script . includes ( 'GetEnvironmentVariable' ) ) {
177+ callback ( null , { stdout : Buffer . from ( existingPath , 'utf-8' ) . toString ( 'base64' ) , stderr : '' } )
178+ return
179+ }
180+ callback ( new Error ( `unexpected command: ${ command } ` ) )
181+ } )
182+
183+ const result = await installHermesStudioCliShim ( {
184+ homeDir,
185+ platform : 'win32' ,
186+ executablePath : 'C:\\Program Files\\Hermes Studio\\Hermes Studio.exe' ,
187+ nodePath : 'C:\\Program Files\\Hermes Studio\\node.exe' ,
188+ webUiScriptPath : 'C:\\Program Files\\Hermes Studio\\resources\\webui\\bin\\hermes-web-ui.mjs' ,
189+ env : { Path : existingPath } ,
190+ } )
191+
192+ expect ( result . status ) . toBe ( 'installed' )
193+ expect ( result . pathUpdated ) . toBe ( false )
194+ expect ( execFileMock ) . toHaveBeenCalledTimes ( 1 )
195+ } )
122196} )
0 commit comments