Skip to content

Commit 5ea8f71

Browse files
committed
test(coverage): cover spawn-cdxgen + spawn-socket-patch bespoke Dlx flows
Adds 11 tests across two new test files for the bespoke Dlx logic in cdxgen and socket-patch (Vfs and auto-dispatch are tested via the shared define-tool-spawn factory): - spawn-cdxgen.mts: local-binary path, local script via node, spawnDlx fallback, unexpected-resolution-type throw, custom stdio - spawn-socket-patch.mts: local-binary path, local script via node, GitHub-release path, legacy npm dlx fallback, custom stdio, options.env merge
1 parent 03f83c8 commit 5ea8f71

2 files changed

Lines changed: 260 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/**
2+
* Unit tests for the bespoke `spawnCdxgenDlx` flow.
3+
*
4+
* The Vfs / auto-dispatch code is tested via define-tool-spawn.test.mts.
5+
* This file targets the cdxgen-specific paths: local-override execution
6+
* (binary or JS via node) and the `spawnDlx` fallback for the npm dlx route.
7+
*/
8+
9+
import { beforeEach, describe, expect, it, vi } from 'vitest'
10+
11+
const mockSpawn = vi.hoisted(() => vi.fn())
12+
const mockSpawnDlx = vi.hoisted(() => vi.fn())
13+
const mockResolveCdxgen = vi.hoisted(() => vi.fn())
14+
const mockDetectExecutableType = vi.hoisted(() => vi.fn())
15+
16+
vi.mock('@socketsecurity/lib/spawn', () => ({
17+
spawn: mockSpawn,
18+
}))
19+
20+
vi.mock('@socketsecurity/lib/dlx/detect', () => ({
21+
detectExecutableType: mockDetectExecutableType,
22+
}))
23+
24+
vi.mock('../../../../src/utils/dlx/spawn.mts', () => ({
25+
spawnDlx: mockSpawnDlx,
26+
}))
27+
28+
vi.mock('../../../../src/utils/dlx/resolve-binary.mts', () => ({
29+
resolveCdxgen: mockResolveCdxgen,
30+
}))
31+
32+
import { spawnCdxgenDlx } from '../../../../src/utils/dlx/spawn-cdxgen.mts'
33+
34+
describe('spawnCdxgenDlx', () => {
35+
beforeEach(() => {
36+
vi.clearAllMocks()
37+
})
38+
39+
it('runs a local cdxgen binary when SOCKET_CLI_CDXGEN_LOCAL_PATH is set', async () => {
40+
mockResolveCdxgen.mockReturnValue({
41+
type: 'local',
42+
path: '/local/cdxgen',
43+
})
44+
mockDetectExecutableType.mockReturnValue({ type: 'binary' })
45+
mockSpawn.mockReturnValue('p')
46+
47+
const result = await spawnCdxgenDlx(
48+
['-r', '.'],
49+
undefined,
50+
undefined,
51+
)
52+
53+
expect(mockSpawn).toHaveBeenCalledWith(
54+
'/local/cdxgen',
55+
['-r', '.'],
56+
expect.objectContaining({ stdio: 'inherit' }),
57+
)
58+
expect(result).toEqual({ spawnPromise: 'p' })
59+
})
60+
61+
it('runs the local cdxgen.js via node when not a binary', async () => {
62+
mockResolveCdxgen.mockReturnValue({
63+
type: 'local',
64+
path: '/local/cdxgen.js',
65+
})
66+
mockDetectExecutableType.mockReturnValue({ type: 'script' })
67+
mockSpawn.mockReturnValue('p')
68+
69+
await spawnCdxgenDlx([], undefined, undefined)
70+
71+
expect(mockSpawn).toHaveBeenCalledWith(
72+
'node',
73+
['/local/cdxgen.js'],
74+
expect.any(Object),
75+
)
76+
})
77+
78+
it('falls back to spawnDlx when resolution.type is "dlx"', async () => {
79+
mockResolveCdxgen.mockReturnValue({
80+
type: 'dlx',
81+
details: { name: '@cyclonedx/cdxgen', version: '11.0.0' },
82+
})
83+
mockSpawnDlx.mockResolvedValue({ spawnPromise: 'p' })
84+
85+
const result = await spawnCdxgenDlx([], undefined, undefined)
86+
87+
expect(mockSpawnDlx).toHaveBeenCalled()
88+
expect(result).toEqual({ spawnPromise: 'p' })
89+
})
90+
91+
it('throws when resolveCdxgen returns an unexpected type', async () => {
92+
mockResolveCdxgen.mockReturnValue({
93+
type: 'github-release',
94+
details: {} as any,
95+
})
96+
97+
await expect(spawnCdxgenDlx([], undefined, undefined)).rejects.toThrow(
98+
/resolveCdxgen returned resolution\.type="github-release"/,
99+
)
100+
})
101+
102+
it('honors a custom stdio passed via spawnExtra', async () => {
103+
mockResolveCdxgen.mockReturnValue({
104+
type: 'local',
105+
path: '/local/cdxgen',
106+
})
107+
mockDetectExecutableType.mockReturnValue({ type: 'binary' })
108+
mockSpawn.mockReturnValue('p')
109+
110+
await spawnCdxgenDlx([], undefined, { stdio: 'pipe' } as any)
111+
112+
expect(mockSpawn).toHaveBeenCalledWith(
113+
'/local/cdxgen',
114+
[],
115+
expect.objectContaining({ stdio: 'pipe' }),
116+
)
117+
})
118+
})
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/**
2+
* Unit tests for the bespoke `spawnSocketPatchDlx` flow.
3+
*
4+
* The Vfs / auto-dispatch code is tested via define-tool-spawn.test.mts.
5+
* This file targets the three-way Dlx dispatch (local override / GitHub
6+
* release / legacy npm fallback).
7+
*/
8+
9+
import { beforeEach, describe, expect, it, vi } from 'vitest'
10+
11+
const mockSpawn = vi.hoisted(() => vi.fn())
12+
const mockSpawnDlx = vi.hoisted(() => vi.fn())
13+
const mockDownloadGitHubReleaseBinary = vi.hoisted(() => vi.fn())
14+
const mockResolveSocketPatch = vi.hoisted(() => vi.fn())
15+
const mockDetectExecutableType = vi.hoisted(() => vi.fn())
16+
17+
vi.mock('@socketsecurity/lib/spawn', () => ({
18+
spawn: mockSpawn,
19+
}))
20+
21+
vi.mock('@socketsecurity/lib/dlx/detect', () => ({
22+
detectExecutableType: mockDetectExecutableType,
23+
}))
24+
25+
vi.mock('../../../../src/utils/dlx/spawn.mts', () => ({
26+
downloadGitHubReleaseBinary: mockDownloadGitHubReleaseBinary,
27+
spawnDlx: mockSpawnDlx,
28+
}))
29+
30+
vi.mock('../../../../src/utils/dlx/resolve-binary.mts', () => ({
31+
resolveSocketPatch: mockResolveSocketPatch,
32+
}))
33+
34+
import { spawnSocketPatchDlx } from '../../../../src/utils/dlx/spawn-socket-patch.mts'
35+
36+
describe('spawnSocketPatchDlx', () => {
37+
beforeEach(() => {
38+
vi.clearAllMocks()
39+
})
40+
41+
it('runs a local socket-patch binary when SOCKET_CLI_SOCKET_PATCH_LOCAL_PATH is set', async () => {
42+
mockResolveSocketPatch.mockReturnValue({
43+
type: 'local',
44+
path: '/local/socket-patch',
45+
})
46+
mockDetectExecutableType.mockReturnValue({ type: 'binary' })
47+
mockSpawn.mockReturnValue('p')
48+
49+
const result = await spawnSocketPatchDlx(
50+
['apply'],
51+
undefined,
52+
undefined,
53+
)
54+
55+
expect(mockSpawn).toHaveBeenCalledWith(
56+
'/local/socket-patch',
57+
['apply'],
58+
expect.objectContaining({ stdio: 'inherit' }),
59+
)
60+
expect(result).toEqual({ spawnPromise: 'p' })
61+
})
62+
63+
it('runs the local script via node when not a binary', async () => {
64+
mockResolveSocketPatch.mockReturnValue({
65+
type: 'local',
66+
path: '/local/socket-patch.js',
67+
})
68+
mockDetectExecutableType.mockReturnValue({ type: 'script' })
69+
mockSpawn.mockReturnValue('p')
70+
71+
await spawnSocketPatchDlx([], undefined, undefined)
72+
73+
expect(mockSpawn).toHaveBeenCalledWith(
74+
'node',
75+
['/local/socket-patch.js'],
76+
expect.any(Object),
77+
)
78+
})
79+
80+
it('downloads from GitHub releases when resolution.type is "github-release"', async () => {
81+
mockResolveSocketPatch.mockReturnValue({
82+
type: 'github-release',
83+
details: { name: 'socket-patch', version: '2.0.0' },
84+
})
85+
mockDownloadGitHubReleaseBinary.mockResolvedValue('/cache/socket-patch')
86+
mockSpawn.mockReturnValue('p')
87+
88+
const result = await spawnSocketPatchDlx(['apply'], undefined, undefined)
89+
90+
expect(mockDownloadGitHubReleaseBinary).toHaveBeenCalled()
91+
expect(mockSpawn).toHaveBeenCalledWith(
92+
'/cache/socket-patch',
93+
['apply'],
94+
expect.objectContaining({ stdio: 'inherit' }),
95+
)
96+
expect(result).toEqual({ spawnPromise: 'p' })
97+
})
98+
99+
it('falls back to spawnDlx for legacy npm-package resolutions', async () => {
100+
mockResolveSocketPatch.mockReturnValue({
101+
type: 'dlx',
102+
details: { name: 'socket-patch', version: '1.0.0' },
103+
})
104+
mockSpawnDlx.mockResolvedValue({ spawnPromise: 'p' })
105+
106+
const result = await spawnSocketPatchDlx([], undefined, undefined)
107+
108+
expect(mockSpawnDlx).toHaveBeenCalled()
109+
expect(result).toEqual({ spawnPromise: 'p' })
110+
})
111+
112+
it('honors a custom stdio passed via spawnExtra', async () => {
113+
mockResolveSocketPatch.mockReturnValue({
114+
type: 'github-release',
115+
details: { name: 'socket-patch', version: '2.0.0' },
116+
})
117+
mockDownloadGitHubReleaseBinary.mockResolvedValue('/cache/socket-patch')
118+
mockSpawn.mockReturnValue('p')
119+
120+
await spawnSocketPatchDlx([], undefined, { stdio: 'pipe' } as any)
121+
122+
expect(mockSpawn).toHaveBeenCalledWith(
123+
'/cache/socket-patch',
124+
[],
125+
expect.objectContaining({ stdio: 'pipe' }),
126+
)
127+
})
128+
129+
it('merges options.env into the child env (local path)', async () => {
130+
mockResolveSocketPatch.mockReturnValue({
131+
type: 'local',
132+
path: '/local/socket-patch',
133+
})
134+
mockDetectExecutableType.mockReturnValue({ type: 'binary' })
135+
mockSpawn.mockReturnValue('p')
136+
137+
await spawnSocketPatchDlx([], { env: { FOO: 'bar' } } as any, undefined)
138+
139+
const callEnv = mockSpawn.mock.calls[0][2].env
140+
expect(callEnv.FOO).toBe('bar')
141+
})
142+
})

0 commit comments

Comments
 (0)