@@ -437,3 +437,128 @@ test("a self-wait does not leave the queued op's rejection unhandled", async ()
437437 process . off ( "unhandledRejection" , onRejection ) ;
438438 }
439439} ) ;
440+
441+ test ( "a microtask detached from a finished op body is not a self-wait" , async ( ) => {
442+ const log : string [ ] = [ ] ;
443+ const { enqueue, awaitTail } = createSessionOperationQueue ( ) ;
444+ let detached : Promise < void > = Promise . resolve ( ) ;
445+
446+ const outer = enqueue ( async ( ) => {
447+ detached = ( async ( ) => {
448+ await Promise . resolve ( ) ;
449+ await enqueue ( async ( ) => {
450+ log . push ( "inner" ) ;
451+ } ) ;
452+ log . push ( "detached:ok" ) ;
453+ } ) ( ) ;
454+ log . push ( "outer:end" ) ;
455+ } ) ;
456+
457+ await outer ;
458+ await detached ;
459+ await awaitTail ( ) ;
460+ expect ( log ) . toEqual ( [ "outer:end" , "inner" , "detached:ok" ] ) ;
461+ } ) ;
462+
463+ test ( "queueMicrotask after the op body returns is not a self-wait" , async ( ) => {
464+ const log : string [ ] = [ ] ;
465+ const { enqueue, awaitTail } = createSessionOperationQueue ( ) ;
466+ let done : Promise < void > = Promise . resolve ( ) ;
467+
468+ const outer = enqueue ( async ( ) => {
469+ done = new Promise ( ( resolve , reject ) => {
470+ queueMicrotask ( ( ) => {
471+ void enqueue ( async ( ) => {
472+ log . push ( "inner" ) ;
473+ } ) . then ( resolve , reject ) ;
474+ } ) ;
475+ } ) ;
476+ log . push ( "outer:end" ) ;
477+ } ) ;
478+
479+ await outer ;
480+ await done ;
481+ await awaitTail ( ) ;
482+ expect ( log ) . toEqual ( [ "outer:end" , "inner" ] ) ;
483+ } ) ;
484+
485+ test ( "an outside waiter is not rejected while the enqueuing op is blocked" , async ( ) => {
486+ const log : string [ ] = [ ] ;
487+ const { enqueue, awaitTail } = createSessionOperationQueue ( ) ;
488+ let release : ( ) => void = ( ) => undefined ;
489+ const gate = new Promise < void > ( ( resolve ) => {
490+ release = resolve ;
491+ } ) ;
492+ let inner : Promise < void > | undefined ;
493+
494+ const outer = enqueue ( async ( ) => {
495+ inner = enqueue ( async ( ) => {
496+ log . push ( "inner" ) ;
497+ } ) ;
498+ await gate ;
499+ log . push ( "outer:end" ) ;
500+ } ) ;
501+
502+ await Promise . resolve ( ) ;
503+ await Promise . resolve ( ) ;
504+ if ( inner === undefined ) throw new Error ( "inner missing" ) ;
505+ let observed = "pending" ;
506+ const waiting = inner . then (
507+ ( ) => {
508+ observed = "resolved" ;
509+ } ,
510+ ( err : unknown ) => {
511+ observed =
512+ err instanceof SessionOperationSelfWaitError ? "self-wait" : "error" ;
513+ } ,
514+ ) ;
515+ await Promise . resolve ( ) ;
516+ expect ( observed ) . toBe ( "pending" ) ;
517+ release ( ) ;
518+ await outer ;
519+ await waiting ;
520+ await awaitTail ( ) ;
521+ expect ( observed ) . toBe ( "resolved" ) ;
522+ expect ( log ) . toEqual ( [ "outer:end" , "inner" ] ) ;
523+ } ) ;
524+
525+ async function settleOrTimeout ( promise : Promise < unknown > ) : Promise < string > {
526+ return Promise . race ( [
527+ promise . then (
528+ ( ) => "resolved" ,
529+ ( err : unknown ) =>
530+ err instanceof SessionOperationSelfWaitError ? "self-wait" : "error" ,
531+ ) ,
532+ new Promise < string > ( ( resolve ) => setTimeout ( ( ) => resolve ( "timeout" ) , 200 ) ) ,
533+ ] ) ;
534+ }
535+
536+ test ( "a non-async callback that returns enqueue rejects and the queue drains" , async ( ) => {
537+ const log : string [ ] = [ ] ;
538+ const { enqueue, awaitTail } = createSessionOperationQueue ( ) ;
539+
540+ const outer = enqueue ( ( ) => {
541+ log . push ( "outer" ) ;
542+ return enqueue ( async ( ) => {
543+ log . push ( "inner" ) ;
544+ } ) ;
545+ } ) ;
546+
547+ expect ( await settleOrTimeout ( outer ) ) . toBe ( "self-wait" ) ;
548+ expect ( await settleOrTimeout ( awaitTail ( ) ) ) . toBe ( "resolved" ) ;
549+ expect ( log ) . toEqual ( [ "outer" , "inner" ] ) ;
550+ } ) ;
551+
552+ test ( "a non-async callback that returns awaitTail rejects and the queue drains" , async ( ) => {
553+ const log : string [ ] = [ ] ;
554+ const { enqueue, awaitTail } = createSessionOperationQueue ( ) ;
555+
556+ const outer = enqueue ( ( ) => {
557+ log . push ( "outer" ) ;
558+ return awaitTail ( ) ;
559+ } ) ;
560+
561+ expect ( await settleOrTimeout ( outer ) ) . toBe ( "self-wait" ) ;
562+ expect ( await settleOrTimeout ( awaitTail ( ) ) ) . toBe ( "resolved" ) ;
563+ expect ( log ) . toEqual ( [ "outer" ] ) ;
564+ } ) ;
0 commit comments