@@ -7,12 +7,12 @@ import shadowNpmBase from './npm-base.mts'
77import type { ShadowBinOptions } from './npm-base.mts'
88
99// Mock all dependencies.
10- const mockSpawn = vi . hoisted ( ( ) => vi . fn ( ) )
10+ const mockSpawnNode = vi . hoisted ( ( ) => vi . fn ( ) )
11+ const mockFindSystemNodejs = vi . hoisted ( ( ) => vi . fn ( ) )
1112const mockInstallNpmLinks = vi . hoisted ( ( ) => vi . fn ( ) )
1213const mockInstallNpxLinks = vi . hoisted ( ( ) => vi . fn ( ) )
1314const mockGetPublicApiToken = vi . hoisted ( ( ) => vi . fn ( ) )
1415const mockFindUp = vi . hoisted ( ( ) => vi . fn ( ) )
15- const mockEnsureIpcInStdio = vi . hoisted ( ( ) => vi . fn ( ) )
1616
1717vi . mock ( 'node:fs' , async importOriginal => {
1818 const actual = ( await importOriginal ( ) ) as Record < string , any >
@@ -24,8 +24,9 @@ vi.mock('node:fs', async importOriginal => {
2424 }
2525} )
2626
27- vi . mock ( '@socketsecurity/lib/spawn' , ( ) => ( {
28- spawn : mockSpawn ,
27+ vi . mock ( '../utils/spawn/spawn-node.mjs' , ( ) => ( {
28+ spawnNode : mockSpawnNode ,
29+ findSystemNodejs : mockFindSystemNodejs ,
2930} ) )
3031
3132vi . mock ( '../utils/shadow/links.mts' , ( ) => ( {
@@ -41,9 +42,6 @@ vi.mock('../utils/fs/find-up.mts', () => ({
4142 findUp : mockFindUp ,
4243} ) )
4344
44- vi . mock ( './stdio-ipc.mts' , ( ) => ( {
45- ensureIpcInStdio : mockEnsureIpcInStdio ,
46- } ) )
4745
4846vi . mock ( '../constants/paths.mts' , async importOriginal => {
4947 const actual = ( await importOriginal ( ) ) as Record < string , any >
@@ -81,44 +79,36 @@ vi.mock('@socketsecurity/lib/constants/node', () => ({
8179} ) )
8280
8381describe ( 'shadowNpmBase' , ( ) => {
84- const mockProcess = {
85- send : vi . fn ( ) ,
86- on : vi . fn ( ) ,
87- }
88-
89- const mockSpawnResult = {
90- process : mockProcess ,
91- then : vi . fn ( ) . mockImplementation ( cb =>
92- cb ( {
93- success : true ,
94- code : 0 ,
95- stdout : '' ,
96- stderr : '' ,
97- } ) ,
98- ) ,
99- }
82+ const mockSpawnResult = Promise . resolve ( {
83+ success : true ,
84+ code : 0 ,
85+ stdout : '' ,
86+ stderr : '' ,
87+ process : {
88+ send : vi . fn ( ) ,
89+ on : vi . fn ( ) ,
90+ } ,
91+ } )
10092
10193 beforeEach ( ( ) => {
10294 vi . clearAllMocks ( )
10395
10496 // Default mock implementations.
105- mockSpawn . mockReturnValue ( mockSpawnResult )
97+ mockSpawnNode . mockReturnValue ( mockSpawnResult )
98+ mockFindSystemNodejs . mockResolvedValue ( '/usr/bin/node' )
10699 mockInstallNpmLinks . mockResolvedValue ( '/usr/bin/npm' )
107100 mockInstallNpxLinks . mockResolvedValue ( '/usr/bin/npx' )
108101 mockGetPublicApiToken . mockReturnValue ( 'test-token' )
109102 mockFindUp . mockResolvedValue ( '/mock/node_modules' )
110- mockEnsureIpcInStdio . mockReturnValue ( [ 'pipe' , 'pipe' , 'pipe' , 'ipc' ] )
111103 } )
112104
113105 it ( 'should spawn npm with default arguments' , async ( ) => {
114106 const result = await shadowNpmBase ( NPM , [ 'install' ] )
115107
116108 expect ( mockInstallNpmLinks ) . toHaveBeenCalledWith ( '/mock/shadow-bin' )
117109
118- const spawnCall = mockSpawn . mock . calls [ 0 ]
119- expect ( spawnCall [ 0 ] ) . toBe ( '/usr/bin/node' )
120-
121- const nodeArgs = spawnCall [ 1 ] as string [ ]
110+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
111+ const nodeArgs = spawnCall [ 0 ] as string [ ]
122112 expect ( nodeArgs ) . toContain ( '--no-warnings' )
123113 expect ( nodeArgs ) . toContain ( '--frozen-intrinsics' )
124114 expect ( nodeArgs ) . toContain ( '--require' )
@@ -131,9 +121,9 @@ describe('shadowNpmBase', () => {
131121 expect ( nodeArgs ) . toContain ( 'error' )
132122 expect ( nodeArgs ) . toContain ( 'install' )
133123
134- const spawnOptions = spawnCall [ 2 ]
135- expect ( spawnOptions . stdio ) . toEqual ( [ 'pipe' , 'pipe' , 'pipe' , 'ipc' ] )
124+ const spawnOptions = spawnCall [ 1 ]
136125 expect ( spawnOptions . env ) . toBeDefined ( )
126+ expect ( spawnOptions . ipc ) . toBeDefined ( )
137127
138128 expect ( result . spawnPromise ) . toBe ( mockSpawnResult )
139129 } )
@@ -143,14 +133,12 @@ describe('shadowNpmBase', () => {
143133
144134 expect ( mockInstallNpxLinks ) . toHaveBeenCalledWith ( '/mock/shadow-bin' )
145135
146- const spawnCall = mockSpawn . mock . calls [ 0 ]
147- expect ( spawnCall [ 0 ] ) . toBe ( '/usr/bin/node' )
148-
149- const nodeArgs = spawnCall [ 1 ] as string [ ]
136+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
137+ const nodeArgs = spawnCall [ 0 ] as string [ ]
150138 expect ( nodeArgs ) . toContain ( '/usr/bin/npx' )
151139 expect ( nodeArgs ) . toContain ( 'create-react-app' )
152140
153- expect ( spawnCall [ 3 ] ) . toBeUndefined ( )
141+ expect ( spawnCall [ 2 ] ) . toBeUndefined ( )
154142 } )
155143
156144 it ( 'should handle custom cwd option' , async ( ) => {
@@ -160,8 +148,7 @@ describe('shadowNpmBase', () => {
160148
161149 await shadowNpmBase ( NPM , [ 'install' ] , options )
162150
163- expect ( mockSpawn ) . toHaveBeenCalledWith (
164- expect . any ( String ) ,
151+ expect ( mockSpawnNode ) . toHaveBeenCalledWith (
165152 expect . any ( Array ) ,
166153 expect . objectContaining ( {
167154 cwd : '/custom/path' ,
@@ -182,10 +169,10 @@ describe('shadowNpmBase', () => {
182169
183170 await shadowNpmBase ( NPM , [ 'install' ] , options )
184171
185- expect ( mockSpawn ) . toHaveBeenCalled ( )
186- const spawnCall = mockSpawn . mock . calls [ 0 ]
172+ expect ( mockSpawnNode ) . toHaveBeenCalled ( )
173+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
187174 // The cwd should be converted from URL to path string.
188- const cwdArg = spawnCall ?. [ 2 ] ?. cwd
175+ const cwdArg = spawnCall ?. [ 1 ] ?. cwd
189176 // Normalized paths should use forward slashes.
190177 expect ( cwdArg ) . toContain ( 'custom' )
191178 expect ( cwdArg ) . toContain ( 'path' )
@@ -195,21 +182,13 @@ describe('shadowNpmBase', () => {
195182 const options : ShadowBinOptions = {
196183 stdio : 'inherit' ,
197184 }
198- mockEnsureIpcInStdio . mockReturnValue ( [
199- 'inherit' ,
200- 'inherit' ,
201- 'inherit' ,
202- 'ipc' ,
203- ] )
204185
205186 await shadowNpmBase ( NPM , [ 'install' ] , options )
206187
207- expect ( mockEnsureIpcInStdio ) . toHaveBeenCalledWith ( 'inherit' )
208- expect ( mockSpawn ) . toHaveBeenCalledWith (
209- expect . any ( String ) ,
188+ expect ( mockSpawnNode ) . toHaveBeenCalledWith (
210189 expect . any ( Array ) ,
211190 expect . objectContaining ( {
212- stdio : [ 'inherit' , 'inherit' , 'inherit' , 'ipc' ] ,
191+ stdio : 'inherit' ,
213192 } ) ,
214193 undefined ,
215194 )
@@ -218,8 +197,8 @@ describe('shadowNpmBase', () => {
218197 it ( 'should add permission flags for npm on supported Node.js versions' , async ( ) => {
219198 await shadowNpmBase ( NPM , [ 'install' ] )
220199
221- const spawnCall = mockSpawn . mock . calls [ 0 ]
222- const nodeArgs = spawnCall [ 1 ] as string [ ]
200+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
201+ const nodeArgs = spawnCall [ 0 ] as string [ ]
223202 const nodeOptionsArg = nodeArgs . find ( arg =>
224203 arg . startsWith ( '--node-options=' ) ,
225204 )
@@ -238,8 +217,8 @@ describe('shadowNpmBase', () => {
238217 it ( 'should not add permission flags for npx' , async ( ) => {
239218 await shadowNpmBase ( NPX , [ 'create-react-app' ] )
240219
241- const spawnCall = mockSpawn . mock . calls [ 0 ]
242- const nodeArgs = spawnCall [ 1 ] as string [ ]
220+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
221+ const nodeArgs = spawnCall [ 0 ] as string [ ]
243222 const hasPermissionFlags = nodeArgs . some ( arg =>
244223 arg . includes ( '--permission' ) ,
245224 )
@@ -250,8 +229,8 @@ describe('shadowNpmBase', () => {
250229 it ( 'should preserve existing node-options' , async ( ) => {
251230 await shadowNpmBase ( NPM , [ 'install' , '--node-options=--test-option' ] )
252231
253- const spawnCall = mockSpawn . mock . calls [ 0 ]
254- const nodeArgs = spawnCall [ 1 ] as string [ ]
232+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
233+ const nodeArgs = spawnCall [ 0 ] as string [ ]
255234 const nodeOptionsArg = nodeArgs . find ( arg =>
256235 arg . startsWith ( '--node-options=' ) ,
257236 )
@@ -267,8 +246,8 @@ describe('shadowNpmBase', () => {
267246 '--no-progress' ,
268247 ] )
269248
270- const spawnCall = mockSpawn . mock . calls [ 0 ]
271- const nodeArgs = spawnCall [ 1 ] as string [ ]
249+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
250+ const nodeArgs = spawnCall [ 0 ] as string [ ]
272251 const hasAuditFlag = nodeArgs . includes ( '--audit' )
273252 const hasProgressFlag = nodeArgs . includes ( '--progress' )
274253 const hasNoProgressFlag = nodeArgs . includes ( '--no-progress' )
@@ -282,8 +261,7 @@ describe('shadowNpmBase', () => {
282261 it ( 'should handle terminator args correctly' , async ( ) => {
283262 await shadowNpmBase ( NPM , [ 'install' , 'lodash' , '--' , '--extra' , 'args' ] )
284263
285- expect ( mockSpawn ) . toHaveBeenCalledWith (
286- expect . any ( String ) ,
264+ expect ( mockSpawnNode ) . toHaveBeenCalledWith (
287265 expect . arrayContaining ( [ 'install' , 'lodash' , '--extra' , 'args' ] ) ,
288266 expect . any ( Object ) ,
289267 undefined ,
@@ -297,33 +275,27 @@ describe('shadowNpmBase', () => {
297275
298276 await shadowNpmBase ( NPM , [ 'install' ] , options )
299277
300- expect ( mockProcess . send ) . toHaveBeenCalledWith ( {
301- SOCKET_IPC_HANDSHAKE : {
302- extra : {
303- SOCKET_CLI_SHADOW_API_TOKEN : 'test-token' ,
304- SOCKET_CLI_SHADOW_BIN : 'npm' ,
305- SOCKET_CLI_SHADOW_PROGRESS : true ,
306- customData : 'test' ,
307- } ,
308- parent_pid : expect . any ( Number ) ,
309- subprocess : true ,
310- } ,
278+ // Verify that spawnNode was called with IPC data.
279+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
280+ const spawnOptions = spawnCall [ 1 ]
281+ expect ( spawnOptions . ipc ) . toEqual ( {
282+ SOCKET_CLI_SHADOW_API_TOKEN : 'test-token' ,
283+ SOCKET_CLI_SHADOW_BIN : 'npm' ,
284+ SOCKET_CLI_SHADOW_PROGRESS : true ,
285+ customData : 'test' ,
311286 } )
312287 } )
313288
314289 it ( 'should handle progress flag in IPC message' , async ( ) => {
315290 await shadowNpmBase ( NPM , [ 'install' , '--no-progress' ] )
316291
317- expect ( mockProcess . send ) . toHaveBeenCalledWith ( {
318- SOCKET_IPC_HANDSHAKE : {
319- extra : {
320- SOCKET_CLI_SHADOW_API_TOKEN : 'test-token' ,
321- SOCKET_CLI_SHADOW_BIN : 'npm' ,
322- SOCKET_CLI_SHADOW_PROGRESS : false ,
323- } ,
324- parent_pid : expect . any ( Number ) ,
325- subprocess : true ,
326- } ,
292+ // Verify that spawnNode was called with IPC data including progress flag.
293+ const spawnCall = mockSpawnNode . mock . calls [ 0 ]
294+ const spawnOptions = spawnCall [ 1 ]
295+ expect ( spawnOptions . ipc ) . toEqual ( {
296+ SOCKET_CLI_SHADOW_API_TOKEN : 'test-token' ,
297+ SOCKET_CLI_SHADOW_BIN : 'npm' ,
298+ SOCKET_CLI_SHADOW_PROGRESS : false ,
327299 } )
328300 } )
329301} )
0 commit comments