@@ -172,76 +172,13 @@ If anything breaks:
172172- ** Coverage** : MANDATORY - never decrease, always maintain/increase
173173- ** c8 ignore** : Must include reason ending with period
174174
175- #### Test Helpers ( ` test/utils/ ` )
175+ #### Test Helpers
176176
177- ** NPM Package Helper** (` npm-package-helper.mts ` )
178- ``` typescript
179- import { setupNpmPackageTest } from ' ../utils/npm-package-helper.mts'
180-
181- // Replaces ~15-20 lines of boilerplate per test
182- const { module : assert, pkgPath, skip, eco, sockRegPkgName } =
183- await setupNpmPackageTest (__filename )
184-
185- describe (` ${eco } > ${sockRegPkgName } ` , { skip }, () => {
186- it (' should work' , () => {
187- expect (assert ).toBeDefined ()
188- })
189- })
190- ```
191-
192- ** Temp File Helper** (` temp-file-helper.mts ` )
193- ``` typescript
194- import { withTempDir , withTempFile , runWithTempDir } from ' ../utils/temp-file-helper.mts'
195-
196- // Temp directory with cleanup
197- const { path : tmpDir, cleanup } = await withTempDir (' test-prefix-' )
198- try {
199- // Use tmpDir...
200- } finally {
201- await cleanup ()
202- }
203-
204- // Or with callback (auto cleanup):
205- await runWithTempDir (async (tmpDir ) => {
206- // Use tmpDir... cleanup happens automatically
207- }, ' test-prefix-' )
208-
209- // Temp file
210- const { path : tmpFile, cleanup } = await withTempFile (' content' , {
211- extension: ' .json' ,
212- prefix: ' config-'
213- })
214- ```
215-
216- ** Platform Test Helpers** (` platform-test-helpers.mts ` )
217- ``` typescript
218- import { platform , itOnWindows , itOnUnix , normalizePath } from ' ../utils/platform-test-helpers.mts'
219-
220- describe (' cross-platform tests' , () => {
221- itOnWindows (' should handle Windows paths' , () => {
222- expect (path .sep ).toBe (' \\ ' )
223- })
224-
225- itOnUnix (' should handle Unix paths' , () => {
226- expect (path .sep ).toBe (' /' )
227- })
228-
229- it (' should compare paths cross-platform' , () => {
230- expectNormalizedPath (' C:\\ Users\\ test' , ' /c/Users/test' )
231- })
232- })
233- ```
234-
235- ** Assertion Helpers** (` assertion-helpers.mts ` )
236- ``` typescript
237- import { expectString , expectFrozen , expectHasProperties } from ' ../utils/assertion-helpers.mts'
238-
239- it (' should validate config' , () => {
240- expectString (config .apiKey )
241- expectFrozen (config )
242- expectHasProperties (config , [' apiKey' , ' baseUrl' , ' timeout' ])
243- })
244- ```
177+ Test helpers available in ` test/utils/ ` :
178+ - ` setupNpmPackageTest() ` - NPM package test boilerplate (npm-package-helper.mts)
179+ - ` withTempDir/withTempFile/runWithTempDir() ` - Temp file management (temp-file-helper.mts)
180+ - ` itOnWindows/itOnUnix/normalizePath() ` - Platform-specific tests (platform-test-helpers.mts)
181+ - ` expectString/expectFrozen/expectHasProperties() ` - Assertions (assertion-helpers.mts)
245182
246183#### Running Tests
247184- ** All tests** : ` pnpm test `
@@ -250,20 +187,6 @@ it('should validate config', () => {
250187- ** Coverage** : ` pnpm run cover `
251188- ** NPM packages** : ` node scripts/test-npm-packages.mjs ` (long-running)
252189
253- #### Migration Guide
254- See ` test/utils/TEST_HELPERS_README.md ` for:
255- - Detailed usage examples (before/after patterns)
256- - Migration strategy and phases
257- - Potential line savings per helper
258- - Best practices and patterns
259-
260- #### Best Practices
261- - ** Use helpers** : setupNpmPackageTest(), withTempDir(), itOnWindows(), etc.
262- - ** Auto cleanup** : Always use cleanup functions for temp resources
263- - ** Platform-aware** : Use platform helpers for cross-platform tests
264- - ** Descriptive names** : Clear test names for coverage reports
265- - ** Combine helpers** : Mix helpers for maximum impact
266-
267190### Vitest Memory Optimization
268191- ** Pool** : ` pool: 'forks' ` , ` singleFork: true ` , ` maxForks: 1 ` , ` isolate: true `
269192- ** Timeouts** : ` testTimeout: 60_000, hookTimeout: 60_000 `
@@ -451,6 +374,18 @@ Decision tree:
451374- ** Order** : Alphabetical; private first, then exported
452375- ** Await in loops** : Add ` // eslint-disable-next-line no-await-in-loop ` when intentional
453376- ** Process spawning** : Use ` @socketsecurity/registry/lib/spawn ` not ` child_process.spawn `
377+ - ** spawn() with shell: WIN32** : 🚨 NEVER change ` shell: WIN32 ` to ` shell: true `
378+ - ` shell: WIN32 ` is the correct cross-platform pattern (enables shell on Windows, disables on Unix)
379+ - If spawn fails with ENOENT, the issue is NOT the shell parameter
380+ - Fix by properly separating command and arguments instead:
381+ ``` javascript
382+ // WRONG - passing full command as string
383+ spawn (' python3 -m module arg1 arg2' , [], { shell: WIN32 })
384+
385+ // CORRECT - separate command and args
386+ spawn (' python3' , [' -m' , ' module' , ' arg1' , ' arg2' ], { shell: WIN32 })
387+ ```
388+ - This pattern is canonical across all Socket Security codebases
454389- ** Working directory** : 🚨 NEVER use ` process.chdir()` - use ` { cwd }` options and absolute paths instead
455390 - Breaks tests, worker threads, and causes race conditions
456391 - Always pass ` { cwd: absolutePath }` to spawn/ exec/ fs operations
0 commit comments