@@ -4,6 +4,14 @@ import { describe, it, expect, afterEach } from 'vitest';
44import { createMultiTenantRouter , type MultiTenantRouter } from '../src/multi-tenant.js' ;
55import { TursoDriver } from '../src/turso-driver.js' ;
66import { SqlDriver } from '@objectstack/driver-sql' ;
7+ import { tmpdir } from 'node:os' ;
8+ import { join } from 'node:path' ;
9+
10+ /** Build a unique temp path template for test isolation. */
11+ function tmpTemplate ( label : string ) : string {
12+ const rand = Math . random ( ) . toString ( 36 ) . slice ( 2 , 8 ) ;
13+ return `file:${ join ( tmpdir ( ) , `test-turso-${ label } -${ rand } -{tenant}.db` ) } ` ;
14+ }
715
816describe ( 'Multi-Tenant Router' , ( ) => {
917 let router : MultiTenantRouter | null = null ;
@@ -29,7 +37,7 @@ describe('Multi-Tenant Router', () => {
2937
3038 it ( 'should create a TursoDriver for a tenant' , async ( ) => {
3139 router = createMultiTenantRouter ( {
32- urlTemplate : 'file:/tmp/test-turso-mt-{tenant}.db' ,
40+ urlTemplate : tmpTemplate ( 'mt' ) ,
3341 } ) ;
3442
3543 const driver = await router . getDriverForTenant ( 'acme' ) ;
@@ -40,7 +48,7 @@ describe('Multi-Tenant Router', () => {
4048
4149 it ( 'should cache drivers and return same instance' , async ( ) => {
4250 router = createMultiTenantRouter ( {
43- urlTemplate : 'file:/tmp/test-turso- cache-{tenant}.db' ,
51+ urlTemplate : tmpTemplate ( ' cache' ) ,
4452 } ) ;
4553
4654 const driver1 = await router . getDriverForTenant ( 'acme' ) ;
@@ -50,7 +58,7 @@ describe('Multi-Tenant Router', () => {
5058
5159 it ( 'should create separate drivers per tenant' , async ( ) => {
5260 router = createMultiTenantRouter ( {
53- urlTemplate : 'file:/tmp/test-turso- sep-{tenant}.db' ,
61+ urlTemplate : tmpTemplate ( ' sep' ) ,
5462 } ) ;
5563
5664 const driver1 = await router . getDriverForTenant ( 'tenant-a' ) ;
@@ -63,15 +71,15 @@ describe('Multi-Tenant Router', () => {
6371
6472 it ( 'should reject empty tenantId' , async ( ) => {
6573 router = createMultiTenantRouter ( {
66- urlTemplate : 'file:/tmp/test-turso- val-{tenant}.db' ,
74+ urlTemplate : tmpTemplate ( ' val' ) ,
6775 } ) ;
6876
6977 await expect ( router . getDriverForTenant ( '' ) ) . rejects . toThrow ( ) ;
7078 } ) ;
7179
7280 it ( 'should reject invalid tenantId with special characters' , async ( ) => {
7381 router = createMultiTenantRouter ( {
74- urlTemplate : 'file:/tmp/test-turso- val2-{tenant}.db' ,
82+ urlTemplate : tmpTemplate ( ' val2' ) ,
7583 } ) ;
7684
7785 await expect ( router . getDriverForTenant ( 'a' ) ) . rejects . toThrow ( ) ;
@@ -80,7 +88,7 @@ describe('Multi-Tenant Router', () => {
8088
8189 it ( 'should accept valid tenantId' , async ( ) => {
8290 router = createMultiTenantRouter ( {
83- urlTemplate : 'file:/tmp/test-turso- accept-{tenant}.db' ,
91+ urlTemplate : tmpTemplate ( ' accept' ) ,
8492 } ) ;
8593
8694 const driver = await router . getDriverForTenant ( 'valid-tenant' ) ;
@@ -91,7 +99,7 @@ describe('Multi-Tenant Router', () => {
9199
92100 it ( 'should report cache size' , async ( ) => {
93101 router = createMultiTenantRouter ( {
94- urlTemplate : 'file:/tmp/test-turso- size-{tenant}.db' ,
102+ urlTemplate : tmpTemplate ( ' size' ) ,
95103 } ) ;
96104
97105 expect ( router . getCacheSize ( ) ) . toBe ( 0 ) ;
@@ -103,7 +111,7 @@ describe('Multi-Tenant Router', () => {
103111
104112 it ( 'should invalidate cache for a tenant' , async ( ) => {
105113 router = createMultiTenantRouter ( {
106- urlTemplate : 'file:/tmp/test-turso- inv-{tenant}.db' ,
114+ urlTemplate : tmpTemplate ( ' inv' ) ,
107115 } ) ;
108116
109117 await router . getDriverForTenant ( 'to-invalidate' ) ;
@@ -115,7 +123,7 @@ describe('Multi-Tenant Router', () => {
115123
116124 it ( 'should destroy all cached drivers' , async ( ) => {
117125 router = createMultiTenantRouter ( {
118- urlTemplate : 'file:/tmp/test-turso- destroy-{tenant}.db' ,
126+ urlTemplate : tmpTemplate ( ' destroy' ) ,
119127 } ) ;
120128
121129 await router . getDriverForTenant ( 'destroy-a' ) ;
@@ -132,7 +140,7 @@ describe('Multi-Tenant Router', () => {
132140 const created : string [ ] = [ ] ;
133141
134142 router = createMultiTenantRouter ( {
135- urlTemplate : 'file:/tmp/test-turso-cb-{tenant}.db' ,
143+ urlTemplate : tmpTemplate ( 'cb' ) ,
136144 onTenantCreate : async ( tenantId ) => {
137145 created . push ( tenantId ) ;
138146 } ,
@@ -150,7 +158,7 @@ describe('Multi-Tenant Router', () => {
150158 const evicted : string [ ] = [ ] ;
151159
152160 router = createMultiTenantRouter ( {
153- urlTemplate : 'file:/tmp/test-turso- evict-{tenant}.db' ,
161+ urlTemplate : tmpTemplate ( ' evict' ) ,
154162 onTenantEvict : async ( tenantId ) => {
155163 evicted . push ( tenantId ) ;
156164 } ,
@@ -163,11 +171,39 @@ describe('Multi-Tenant Router', () => {
163171 expect ( evicted ) . toEqual ( [ 'evict-test' ] ) ;
164172 } ) ;
165173
174+ // ── Concurrent Access Guard ────────────────────────────────────────────
175+
176+ it ( 'should deduplicate concurrent getDriverForTenant calls' , async ( ) => {
177+ let createCount = 0 ;
178+
179+ router = createMultiTenantRouter ( {
180+ urlTemplate : tmpTemplate ( 'conc' ) ,
181+ onTenantCreate : async ( ) => {
182+ createCount ++ ;
183+ } ,
184+ } ) ;
185+
186+ // Fire multiple concurrent calls for the same tenant
187+ const [ d1 , d2 , d3 ] = await Promise . all ( [
188+ router . getDriverForTenant ( 'same-tenant' ) ,
189+ router . getDriverForTenant ( 'same-tenant' ) ,
190+ router . getDriverForTenant ( 'same-tenant' ) ,
191+ ] ) ;
192+
193+ // All calls should return the same driver instance
194+ expect ( d1 ) . toBe ( d2 ) ;
195+ expect ( d2 ) . toBe ( d3 ) ;
196+
197+ // onTenantCreate should only be called once
198+ expect ( createCount ) . toBe ( 1 ) ;
199+ expect ( router . getCacheSize ( ) ) . toBe ( 1 ) ;
200+ } ) ;
201+
166202 // ── CRUD through Multi-Tenant Driver ───────────────────────────────────
167203
168204 it ( 'should perform CRUD operations through a tenant driver' , async ( ) => {
169205 router = createMultiTenantRouter ( {
170- urlTemplate : 'file:/tmp/test-turso- crud-{tenant}.db' ,
206+ urlTemplate : tmpTemplate ( ' crud' ) ,
171207 } ) ;
172208
173209 const driver = await router . getDriverForTenant ( 'crud-test' ) ;
0 commit comments