@@ -2,7 +2,7 @@ import {zip, brotliCompress} from './archiver.js'
22import { fileExists , inTemporaryDirectory , mkdir , touchFile } from './fs.js'
33import { joinPath , dirname } from './path.js'
44import { exec } from './system.js'
5- import { describe , expect , test } from 'vitest'
5+ import { describe , expect , test , vi } from 'vitest'
66import StreamZip from 'node-stream-zip'
77import brotli from 'brotli'
88import fs from 'fs'
@@ -57,6 +57,51 @@ describe('zip', () => {
5757 expect ( expectedEntries . sort ( ) ) . toEqual ( archiveEntries . sort ( ) )
5858 } )
5959 } )
60+
61+ test ( 'gracefully handles files deleted during archiving' , async ( ) => {
62+ await inTemporaryDirectory ( async ( tmpDir ) => {
63+ // Given
64+ const zipPath = joinPath ( tmpDir , 'output.zip' )
65+ const outputDirectoryName = 'output'
66+ const outputDirectoryPath = joinPath ( tmpDir , outputDirectoryName )
67+ const structure = [ 'file1.js' , 'file2.js' , 'file3.js' ]
68+
69+ await createFiles ( structure , outputDirectoryPath )
70+
71+ const file2Path = joinPath ( outputDirectoryPath , 'file2.js' )
72+
73+ // Spy on readFile to simulate a file being deleted during archiving
74+ // We'll make readFile throw ENOENT for file2.js specifically
75+ const fsModule = await import ( './fs.js' )
76+ const originalReadFile = fsModule . readFile
77+ const readFileSpy = vi . spyOn ( fsModule , 'readFile' )
78+ readFileSpy . mockImplementation ( async ( path : string ) => {
79+ if ( path === file2Path ) {
80+ const error : NodeJS . ErrnoException = new Error ( `ENOENT: no such file or directory, open '${ path } '` )
81+ error . code = 'ENOENT'
82+ throw error
83+ }
84+ return originalReadFile ( path )
85+ } )
86+
87+ // When - should not throw even though file2.js throws ENOENT
88+ await expect (
89+ zip ( {
90+ inputDirectory : outputDirectoryPath ,
91+ outputZipPath : zipPath ,
92+ } ) ,
93+ ) . resolves . not . toThrow ( )
94+
95+ // Then - archive should contain remaining files only
96+ const archiveEntries = await readArchiveFiles ( zipPath )
97+ expect ( archiveEntries ) . toContain ( 'file1.js' )
98+ expect ( archiveEntries ) . toContain ( 'file3.js' )
99+ // file2.js should not be in archive since readFile failed
100+ expect ( archiveEntries ) . not . toContain ( 'file2.js' )
101+
102+ readFileSpy . mockRestore ( )
103+ } )
104+ } )
60105} )
61106
62107describe ( 'brotliCompress' , ( ) => {
@@ -141,6 +186,48 @@ describe('brotliCompress', () => {
141186 expect ( fs . existsSync ( joinPath ( extractPath , 'test.json' ) ) ) . toBeFalsy ( )
142187 } )
143188 } )
189+
190+ test ( 'gracefully handles files deleted during compression' , async ( ) => {
191+ await inTemporaryDirectory ( async ( tmpDir ) => {
192+ // Given
193+ const brotliPath = joinPath ( tmpDir , 'output.br' )
194+ const outputDirectoryName = 'output'
195+ const outputDirectoryPath = joinPath ( tmpDir , outputDirectoryName )
196+ const structure = [ 'file1.js' , 'file2.js' , 'file3.js' ]
197+
198+ await createFiles ( structure , outputDirectoryPath )
199+
200+ const file2Path = joinPath ( outputDirectoryPath , 'file2.js' )
201+
202+ // Spy on readFile to simulate a file being deleted during compression
203+ // We'll make readFile throw ENOENT for file2.js specifically
204+ const fsModule = await import ( './fs.js' )
205+ const originalReadFile = fsModule . readFile
206+ const readFileSpy = vi . spyOn ( fsModule , 'readFile' )
207+ readFileSpy . mockImplementation ( async ( path : string ) => {
208+ if ( path === file2Path ) {
209+ const error : NodeJS . ErrnoException = new Error ( `ENOENT: no such file or directory, open '${ path } '` )
210+ error . code = 'ENOENT'
211+ throw error
212+ }
213+ return originalReadFile ( path )
214+ } )
215+
216+ // When - should not throw even though file2.js throws ENOENT
217+ await expect (
218+ brotliCompress ( {
219+ inputDirectory : outputDirectoryPath ,
220+ outputPath : brotliPath ,
221+ } ) ,
222+ ) . resolves . not . toThrow ( )
223+
224+ // Then - compressed file should exist and be valid
225+ const exists = await fileExists ( brotliPath )
226+ expect ( exists ) . toBeTruthy ( )
227+
228+ readFileSpy . mockRestore ( )
229+ } )
230+ } )
144231} )
145232
146233async function createFiles ( structure : string [ ] , directory : string ) {
0 commit comments