@@ -2,18 +2,23 @@ import fs from 'node:fs';
22import os from 'node:os' ;
33import path from 'node:path' ;
44
5- import { afterEach , describe , expect , it } from 'vitest' ;
5+ import { afterEach , describe , expect , it , vi } from 'vitest' ;
66
77import type { OrgManifest } from '../org-manifest.js' ;
88import {
99 cleanupStaleStagingDirs ,
10+ ensureOrgPackageExtracted ,
1011 normalizeEntryName ,
1112 parseEntryMode ,
1213 resolveBundledPath ,
1314 resolveExtractionDir ,
1415 sanitizeHostForPath ,
1516} from '../org-tarball.js' ;
1617
18+ const { mockGetVpDirs } = vi . hoisted ( ( ) => ( { mockGetVpDirs : vi . fn ( ) } ) ) ;
19+
20+ vi . mock ( '../../../binding/index.js' , ( ) => ( { getVpDirs : mockGetVpDirs } ) ) ;
21+
1722describe ( 'resolveBundledPath' , ( ) => {
1823 const scratchDirs : string [ ] = [ ] ;
1924
@@ -112,9 +117,9 @@ describe('resolveExtractionDir', () => {
112117 } ,
113118 ) ;
114119
115- // Escaping `<root>/<host>/<scope>/create` takes at least four `..` segments .
116- it . each ( [ '../../../../outside' , '../../../../../../outside-write' , '/absolute' ] ) (
117- 'rejects a version that escapes the cache root: %s' ,
120+ // Three `..` segments reach the cache root; four escape it .
121+ it . each ( [ '../../..' , '../../.. /../outside', '../../../../../../outside-write' , '/absolute' ] ) (
122+ 'rejects a version that resolves to or outside the cache root: %s' ,
118123 ( version ) => {
119124 expect ( ( ) => resolveExtractionDir ( cacheRoot , manifestFor ( version ) ) ) . toThrow (
120125 / e s c a p e s t h e c a c h e r o o t / ,
@@ -123,6 +128,29 @@ describe('resolveExtractionDir', () => {
123128 ) ;
124129} ) ;
125130
131+ describe ( 'ensureOrgPackageExtracted' , ( ) => {
132+ afterEach ( ( ) => {
133+ vi . restoreAllMocks ( ) ;
134+ } ) ;
135+
136+ it ( 'rejects the cache root before filesystem or network activity' , async ( ) => {
137+ mockGetVpDirs . mockReturnValue ( { cache : path . join ( os . tmpdir ( ) , 'vp-org-extraction-cache' ) } ) ;
138+ const exists = vi . spyOn ( fs , 'existsSync' ) . mockReturnValue ( false ) ;
139+ const mkdir = vi . spyOn ( fs . promises , 'mkdir' ) . mockResolvedValue ( undefined ) ;
140+ const readdir = vi . spyOn ( fs . promises , 'readdir' ) . mockResolvedValue ( [ ] ) ;
141+ const fetch = vi . spyOn ( globalThis , 'fetch' ) . mockRejectedValue ( new Error ( 'unexpected download' ) ) ;
142+
143+ await expect ( ensureOrgPackageExtracted ( manifestFor ( '../../..' ) ) ) . rejects . toThrow (
144+ / e s c a p e s t h e c a c h e r o o t / ,
145+ ) ;
146+
147+ expect ( exists ) . not . toHaveBeenCalled ( ) ;
148+ expect ( mkdir ) . not . toHaveBeenCalled ( ) ;
149+ expect ( readdir ) . not . toHaveBeenCalled ( ) ;
150+ expect ( fetch ) . not . toHaveBeenCalled ( ) ;
151+ } ) ;
152+ } ) ;
153+
126154describe ( 'sanitizeHostForPath' , ( ) => {
127155 it ( 'passes through plain hostnames untouched' , ( ) => {
128156 expect ( sanitizeHostForPath ( 'registry.npmjs.org' ) ) . toBe ( 'registry.npmjs.org' ) ;
0 commit comments