@@ -276,6 +276,85 @@ export function testOPFS(baseDir: string, bundle: () => duckdb.DuckDBBundle): vo
276276 } ) ;
277277 } ) ;
278278
279+ describe ( 'Open database in OPFS' , ( ) => {
280+ it ( 'should not open a non-existent DB file in read-only' , async ( ) => {
281+ const logger = new duckdb . ConsoleLogger ( LogLevel . ERROR ) ;
282+ const worker = new Worker ( bundle ( ) . mainWorker ! ) ;
283+ const db_ = new duckdb . AsyncDuckDB ( logger , worker ) ;
284+ await db_ . instantiate ( bundle ( ) . mainModule , bundle ( ) . pthreadWorker ) ;
285+
286+ await expectAsync ( db_ . open ( {
287+ path : 'opfs://non_existent.db' ,
288+ accessMode : duckdb . DuckDBAccessMode . READ_ONLY ,
289+ } ) ) . toBeRejectedWithError ( Error , / f i l e o r d i r e c t o r y c o u l d n o t b e f o u n d / ) ;
290+
291+ await db_ . terminate ( ) ;
292+ await worker . terminate ( ) ;
293+
294+ // Files should not be found with DuckDBAccessMode.READ_ONLY
295+ const opfsRoot = await navigator . storage . getDirectory ( ) ;
296+ await expectAsync ( opfsRoot . getFileHandle ( 'non_existent.db' , { create : false } ) )
297+ . toBeRejectedWithError ( Error , / f i l e o r d i r e c t o r y c o u l d n o t b e f o u n d / ) ;
298+ } ) ;
299+
300+ it ( 'should not open a non-existent DB file and mkdir in read-only' , async ( ) => {
301+ const logger = new duckdb . ConsoleLogger ( LogLevel . ERROR ) ;
302+ const worker = new Worker ( bundle ( ) . mainWorker ! ) ;
303+ const db_ = new duckdb . AsyncDuckDB ( logger , worker ) ;
304+ await db_ . instantiate ( bundle ( ) . mainModule , bundle ( ) . pthreadWorker ) ;
305+
306+ await expectAsync ( db_ . open ( {
307+ path : 'opfs://duckdb_test/path/to/non_existent.db' ,
308+ accessMode : duckdb . DuckDBAccessMode . READ_ONLY ,
309+ } ) ) . toBeRejectedWithError ( Error , / f i l e o r d i r e c t o r y c o u l d n o t b e f o u n d / ) ;
310+
311+ await db_ . terminate ( ) ;
312+ await worker . terminate ( ) ;
313+ } ) ;
314+
315+ it ( 'should open a non-existent DB file and mkdir in read-write' , async ( ) => {
316+ const logger = new duckdb . ConsoleLogger ( LogLevel . ERROR ) ;
317+ const worker = new Worker ( bundle ( ) . mainWorker ! ) ;
318+ const db_ = new duckdb . AsyncDuckDB ( logger , worker ) ;
319+ await db_ . instantiate ( bundle ( ) . mainModule , bundle ( ) . pthreadWorker ) ;
320+
321+ await expectAsync ( db_ . open ( {
322+ path : 'opfs://duckdb_test/path/to/duck.db' ,
323+ accessMode : duckdb . DuckDBAccessMode . READ_WRITE ,
324+ } ) ) . toBeResolved ( ) ;
325+
326+ await db_ . terminate ( ) ;
327+ await worker . terminate ( ) ;
328+ } ) ;
329+
330+ it ( 'should open a non-existent DB file in read-write and create files' , async ( ) => {
331+ const logger = new duckdb . ConsoleLogger ( LogLevel . ERROR ) ;
332+ const worker = new Worker ( bundle ( ) . mainWorker ! ) ;
333+ const db_ = new duckdb . AsyncDuckDB ( logger , worker ) ;
334+ await db_ . instantiate ( bundle ( ) . mainModule , bundle ( ) . pthreadWorker ) ;
335+
336+ const opfsRoot = await navigator . storage . getDirectory ( ) ;
337+
338+ // Ensure files do not exist
339+ await expectAsync ( opfsRoot . getFileHandle ( 'non_existent_2.db' , { create : false } ) )
340+ . toBeRejectedWithError ( Error , / f i l e o r d i r e c t o r y c o u l d n o t b e f o u n d / ) ;
341+ await expectAsync ( opfsRoot . getFileHandle ( 'non_existent_2.db.wal' , { create : false } ) )
342+ . toBeRejectedWithError ( Error , / f i l e o r d i r e c t o r y c o u l d n o t b e f o u n d / ) ;
343+
344+ await expectAsync ( db_ . open ( {
345+ path : 'opfs://non_existent_2.db' ,
346+ accessMode : duckdb . DuckDBAccessMode . READ_WRITE ,
347+ } ) ) . toBeResolved ( ) ;
348+
349+ await db_ . terminate ( ) ;
350+ await worker . terminate ( ) ;
351+
352+ // Files should be found with DuckDBAccessMode.READ_WRITE
353+ await expectAsync ( opfsRoot . getFileHandle ( 'non_existent_2.db' , { create : false } ) ) . toBeResolved ( ) ;
354+ await expectAsync ( opfsRoot . getFileHandle ( 'non_existent_2.db.wal' , { create : false } ) ) . toBeResolved ( ) ;
355+ } ) ;
356+ } )
357+
279358 async function removeFiles ( ) {
280359 const opfsRoot = await navigator . storage . getDirectory ( ) ;
281360 await opfsRoot . removeEntry ( 'test.db' ) . catch ( ( ) => { } ) ;
@@ -292,5 +371,12 @@ export function testOPFS(baseDir: string, bundle: () => duckdb.DuckDBBundle): vo
292371 //
293372 }
294373 await opfsRoot . removeEntry ( 'datadir' ) . catch ( ( ) => { } ) ;
374+
375+ // In case of failure caused leftovers
376+ await opfsRoot . removeEntry ( 'non_existent.db' ) . catch ( ( ) => { } ) ;
377+ await opfsRoot . removeEntry ( 'non_existent.db.wal' ) . catch ( ( ) => { } ) ;
378+ await opfsRoot . removeEntry ( 'non_existent_2.db' ) . catch ( ( ) => { } ) ;
379+ await opfsRoot . removeEntry ( 'non_existent_2.db.wal' ) . catch ( ( ) => { } ) ;
380+ await opfsRoot . removeEntry ( 'duckdb_test' , { recursive : true } ) . catch ( ( ) => { } ) ;
295381 }
296382}
0 commit comments