@@ -152,6 +152,30 @@ describe("column catalog batch coordinator", () => {
152152 } ) ;
153153 } ) ;
154154
155+ it ( "separates quoted relation and search-path cache identities" , async ( ) => {
156+ let calls = 0 ;
157+ const { owner } = setup ( ( request ) => {
158+ calls += 1 ;
159+ return ready ( request ) ;
160+ } ) ;
161+ const quoted = {
162+ expectedEpoch : epoch ,
163+ relations : [ {
164+ path : [ { quoted : true , value : "users" } ] ,
165+ requestKey : "quoted" ,
166+ } ] ,
167+ searchPaths : [ [ { quoted : true , value : "main" } ] ] ,
168+ } ;
169+
170+ await expect ( owner . request ( quoted ) . result ) . resolves . toMatchObject ( {
171+ status : "usable" ,
172+ } ) ;
173+ await expect ( owner . request ( quoted ) . result ) . resolves . toMatchObject ( {
174+ status : "usable" ,
175+ } ) ;
176+ expect ( calls ) . toBe ( 1 ) ;
177+ } ) ;
178+
155179 it ( "supports cold null epochs and reuses the observed response epoch" , async ( ) => {
156180 const expected : Array < SqlCatalogEpoch | null > = [ ] ;
157181 const { owner } = setup ( ( request ) => {
@@ -262,6 +286,17 @@ describe("column catalog batch coordinator", () => {
262286 await expect ( ticket . result ) . resolves . toEqual ( { status : "cancelled" } ) ;
263287 } ) ;
264288
289+ it ( "ignores a provider rejection after its consumer is cancelled" , async ( ) => {
290+ const work = deferred < unknown > ( ) ;
291+ const { owner } = setup ( ( ) => work . promise ) ;
292+ const ticket = owner . request ( input ( ) ) ;
293+
294+ ticket . cancel ( ) ;
295+ work . reject ( new Error ( "late rejection" ) ) ;
296+ await expect ( ticket . result ) . resolves . toEqual ( { status : "cancelled" } ) ;
297+ await Promise . resolve ( ) ;
298+ } ) ;
299+
265300 it ( "supersedes prior owner work but isolates other owners" , async ( ) => {
266301 const pending : Array < ReturnType < typeof deferred < unknown > > > = [ ] ;
267302 const { coordinator, owner } = setup ( ( ) => {
@@ -301,6 +336,7 @@ describe("column catalog batch coordinator", () => {
301336 } ) ;
302337 const ownerTicket = owner . request ( input ( ) ) ;
303338 owner . dispose ( ) ;
339+ owner . dispose ( ) ;
304340 await expect ( ownerTicket . result ) . resolves . toEqual ( {
305341 reason : "disposed" ,
306342 status : "unavailable" ,
@@ -376,6 +412,12 @@ describe("column catalog batch coordinator", () => {
376412 reason : "invalid-provider" ,
377413 status : "unavailable" ,
378414 } ) ;
415+ expect ( createSqlColumnCatalogBatchCoordinator ( {
416+ provider : { } ,
417+ } ) ) . toEqual ( {
418+ reason : "invalid-provider" ,
419+ status : "unavailable" ,
420+ } ) ;
379421 expect ( createSqlColumnCatalogBatchCoordinator ( {
380422 maxCacheEntries : 0 ,
381423 provider : { id : "catalog" , loadColumns : vi . fn ( ) } ,
@@ -396,10 +438,33 @@ describe("column catalog batch coordinator", () => {
396438 scope : "scope" ,
397439 } ) ;
398440 if ( prepared . status !== "prepared" ) throw new Error ( "Expected owner" ) ;
399- await expect ( prepared . owner . request ( {
441+ const invalidTicket = prepared . owner . request ( {
400442 expectedEpoch : epoch ,
401443 relations : [ ] ,
402444 searchPaths : [ ] ,
445+ } ) ;
446+ invalidTicket . cancel ( ) ;
447+ await expect ( invalidTicket . result ) . resolves . toEqual ( {
448+ reason : "invalid-request" ,
449+ status : "unavailable" ,
450+ } ) ;
451+ } ) ;
452+
453+ it ( "contains hostile and missing request properties" , async ( ) => {
454+ const { owner } = setup ( vi . fn ( ) ) ;
455+ const hostile = new Proxy ( { } , {
456+ getOwnPropertyDescriptor ( ) {
457+ throw new Error ( "hostile descriptor" ) ;
458+ } ,
459+ } ) ;
460+
461+ await expect ( owner . request ( hostile ) . result ) . resolves . toEqual ( {
462+ reason : "invalid-request" ,
463+ status : "unavailable" ,
464+ } ) ;
465+ await expect ( owner . request ( {
466+ expectedEpoch : epoch ,
467+ relations : [ reference ( "users" ) ] ,
403468 } ) . result ) . resolves . toEqual ( {
404469 reason : "invalid-request" ,
405470 status : "unavailable" ,
0 commit comments