@@ -89,11 +89,15 @@ export function useTempDir(
8989 closeDatabases : ( ...dbs ) => {
9090 for ( const db of dbs ) {
9191 try {
92- if ( db && db . isOpen ) {
92+ if ( db && typeof db . close === "function" ) {
93+ // Always try to close, regardless of state
9394 db . close ( ) ;
9495 }
95- } catch {
96- // Ignore close errors during cleanup
96+ } catch ( err ) {
97+ // Log but don't throw during cleanup
98+ if ( process . platform === "win32" || process . env . DEBUG_CLEANUP ) {
99+ console . log ( `Warning: Error closing database: ${ err } ` ) ;
100+ }
97101 }
98102 }
99103 } ,
@@ -120,40 +124,140 @@ export function useTempDir(
120124 } ) ;
121125
122126 afterEach ( async ( ) => {
123- // Clean up WAL and SHM files if requested
124- if ( options ?. cleanupWalFiles && fs . existsSync ( context . tempDir ) ) {
125- try {
126- const files = fs . readdirSync ( context . tempDir ) ;
127- for ( const file of files ) {
128- if ( file . endsWith ( "-wal" ) || file . endsWith ( "-shm" ) ) {
129- try {
130- fs . unlinkSync ( path . join ( context . tempDir , file ) ) ;
131- } catch {
132- // Ignore errors
127+ const startTime = Date . now ( ) ;
128+ const logDebug = ( msg : string ) => {
129+ if ( process . env . DEBUG_CLEANUP || process . platform === "win32" ) {
130+ console . log ( `[${ Date . now ( ) - startTime } ms] ${ msg } ` ) ;
131+ }
132+ } ;
133+
134+ logDebug ( `afterEach starting for tempDir: ${ context . tempDir } ` ) ;
135+
136+ // Force a small delay to ensure databases are closed
137+ // This helps with the timing of cleanup vs database closure
138+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
139+
140+ // On Windows, we need to be more careful about cleanup timing
141+ if ( process . platform === "win32" ) {
142+ logDebug ( "Windows detected - using special cleanup procedure" ) ;
143+
144+ // First, give SQLite time to release file handles
145+ logDebug ( "Initial wait for SQLite cleanup" ) ;
146+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
147+
148+ // Try to clean up WAL and SHM files
149+ if ( options ?. cleanupWalFiles && fs . existsSync ( context . tempDir ) ) {
150+ logDebug ( "Cleaning up WAL/SHM files" ) ;
151+ let retryCount = 0 ;
152+ const maxRetries = 5 ;
153+
154+ while ( retryCount < maxRetries ) {
155+ try {
156+ const files = fs . readdirSync ( context . tempDir ) ;
157+ let walShmFound = false ;
158+
159+ for ( const file of files ) {
160+ if ( file . endsWith ( "-wal" ) || file . endsWith ( "-shm" ) ) {
161+ walShmFound = true ;
162+ try {
163+ fs . unlinkSync ( path . join ( context . tempDir , file ) ) ;
164+ logDebug ( `Deleted ${ file } on attempt ${ retryCount + 1 } ` ) ;
165+ } catch ( err : any ) {
166+ if ( err . code === 'EBUSY' || err . code === 'EPERM' ) {
167+ logDebug ( `File ${ file } still in use, will retry` ) ;
168+ } else {
169+ logDebug ( `Failed to delete ${ file } : ${ err } ` ) ;
170+ }
171+ }
172+ }
133173 }
174+
175+ if ( ! walShmFound ) {
176+ logDebug ( "No WAL/SHM files found" ) ;
177+ break ;
178+ }
179+
180+ // Check if all WAL/SHM files are gone
181+ const remainingFiles = fs . readdirSync ( context . tempDir ) ;
182+ const hasWalShm = remainingFiles . some ( f => f . endsWith ( "-wal" ) || f . endsWith ( "-shm" ) ) ;
183+
184+ if ( ! hasWalShm ) {
185+ logDebug ( "All WAL/SHM files successfully deleted" ) ;
186+ break ;
187+ }
188+
189+ retryCount ++ ;
190+ if ( retryCount < maxRetries ) {
191+ logDebug ( `Retry ${ retryCount } /${ maxRetries } - waiting 200ms` ) ;
192+ await new Promise ( ( resolve ) => setTimeout ( resolve , 200 ) ) ;
193+ }
194+ } catch ( err ) {
195+ logDebug ( `Error during WAL/SHM cleanup: ${ err } ` ) ;
196+ break ;
134197 }
135198 }
136- } catch {
137- // Ignore errors
138199 }
139- }
140-
141- // Wait a bit for Windows file handles to be released
142- if ( process . platform === "win32" ) {
143- await new Promise ( ( resolve ) => setTimeout ( resolve , 500 ) ) ;
200+
201+ // Additional wait before main cleanup
202+ logDebug ( "Final wait before directory removal" ) ;
203+ await new Promise ( ( resolve ) => setTimeout ( resolve , 300 ) ) ;
204+ } else {
205+ // Non-Windows cleanup
206+ if ( options ?. cleanupWalFiles && fs . existsSync ( context . tempDir ) ) {
207+ logDebug ( "Cleaning up WAL/SHM files" ) ;
208+ try {
209+ const files = fs . readdirSync ( context . tempDir ) ;
210+ for ( const file of files ) {
211+ if ( file . endsWith ( "-wal" ) || file . endsWith ( "-shm" ) ) {
212+ try {
213+ fs . unlinkSync ( path . join ( context . tempDir , file ) ) ;
214+ logDebug ( `Deleted ${ file } ` ) ;
215+ } catch ( err ) {
216+ logDebug ( `Failed to delete ${ file } : ${ err } ` ) ;
217+ }
218+ }
219+ }
220+ } catch ( err ) {
221+ logDebug ( `Error reading directory: ${ err } ` ) ;
222+ }
223+ }
144224 }
145225
146226 if ( fs . existsSync ( context . tempDir ) ) {
147- await fsp . rm ( context . tempDir , {
148- recursive : true ,
149- force : true ,
150- maxRetries : process . platform === "win32" ? 10 : 3 ,
151- retryDelay : process . platform === "win32" ? 1000 : 500 ,
152- } ) ;
227+ logDebug ( "Starting fsp.rm of tempDir" ) ;
228+ try {
229+ await fsp . rm ( context . tempDir , {
230+ recursive : true ,
231+ force : true ,
232+ maxRetries : process . platform === "win32" ? 3 : 3 ,
233+ retryDelay : process . platform === "win32" ? 100 : 100 ,
234+ } ) ;
235+ logDebug ( "Successfully removed tempDir" ) ;
236+ } catch ( err ) {
237+ logDebug ( `Error removing tempDir: ${ err } ` ) ;
238+ // On Windows, if the directory still exists, it's likely in use
239+ // Log but don't fail - the OS will clean it up eventually
240+ if ( process . platform === "win32" && fs . existsSync ( context . tempDir ) ) {
241+ logDebug ( `Warning: Could not remove ${ context . tempDir } - likely still in use` ) ;
242+
243+ // Try one more time with execSync, but don't wait
244+ try {
245+ const { exec } = await import ( "child_process" ) ;
246+ exec ( `rmdir /s /q "${ context . tempDir } " 2>nul` , ( err ) => {
247+ if ( err ) {
248+ logDebug ( `Background cleanup also failed: ${ err . message } ` ) ;
249+ }
250+ } ) ;
251+ } catch {
252+ // Ignore
253+ }
254+ }
255+ }
153256 }
154257
155258 // Clear additional files set
156259 additionalFiles . clear ( ) ;
260+ logDebug ( "afterEach completed" ) ;
157261 } , options ?. timeout ?? getTestTimeout ( ) ) ; // Use environment-aware timeout
158262
159263 return context ;
@@ -179,11 +283,15 @@ export function useTempDirSuite(
179283 closeDatabases : ( ...dbs ) => {
180284 for ( const db of dbs ) {
181285 try {
182- if ( db && db . isOpen ) {
286+ if ( db && typeof db . close === "function" ) {
287+ // Always try to close, regardless of state
183288 db . close ( ) ;
184289 }
185- } catch {
186- // Ignore close errors during cleanup
290+ } catch ( err ) {
291+ // Log but don't throw during cleanup
292+ if ( process . platform === "win32" || process . env . DEBUG_CLEANUP ) {
293+ console . log ( `Warning: Error closing database: ${ err } ` ) ;
294+ }
187295 }
188296 }
189297 } ,
0 commit comments