Skip to content

Commit c13bffa

Browse files
committed
test(shadow): update npm-base tests to use spawnNode mocks
Updated shadowNpmBase tests to properly mock the new spawnNode() abstraction instead of the raw spawn() function. Key changes: - Mock spawnNode() and findSystemNodejs() from spawn-node.mjs - Remove obsolete spawn() mock from @socketsecurity/lib/spawn - Update all test assertions to check spawnNode calls with correct parameter positions (args array first, options second) - Fix IPC test expectations to check options.ipc instead of process.send() calls, as IPC handling is now internal to spawnNode The tests now properly verify that shadowNpmBase calls spawnNode with correct arguments, Node flags, permission flags, and IPC data.
1 parent 9be3720 commit c13bffa

File tree

1 file changed

+54
-82
lines changed

1 file changed

+54
-82
lines changed

packages/cli/src/shadow/npm-base.test.mts

Lines changed: 54 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import shadowNpmBase from './npm-base.mts'
77
import 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())
1112
const mockInstallNpmLinks = vi.hoisted(() => vi.fn())
1213
const mockInstallNpxLinks = vi.hoisted(() => vi.fn())
1314
const mockGetPublicApiToken = vi.hoisted(() => vi.fn())
1415
const mockFindUp = vi.hoisted(() => vi.fn())
15-
const mockEnsureIpcInStdio = vi.hoisted(() => vi.fn())
1616

1717
vi.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

3132
vi.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

4846
vi.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

8381
describe('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

Comments
 (0)