@@ -53,6 +53,13 @@ function makeSseResponse(events: string[]): Response {
5353 } )
5454}
5555
56+ function makeProblemResponse ( status : number , body : Record < string , unknown > ) : Response {
57+ return new Response ( JSON . stringify ( body ) , {
58+ status,
59+ headers : { 'Content-Type' : 'application/problem+json' } ,
60+ } )
61+ }
62+
5663describe ( 'Session' , ( ) => {
5764 describe ( 'parseEvent round-trip via SSE' , ( ) => {
5865 test ( 'parses message events from SSE stream' , ( ) => {
@@ -530,6 +537,84 @@ describe('Session', () => {
530537 } )
531538
532539 describe ( '.sse() event parsing' , ( ) => {
540+ test ( 'preserves headers instances while adding SSE accept header' , async ( ) => {
541+ const mockFetch = vi . fn ( ) . mockResolvedValue ( makeSseResponse ( [ ] ) )
542+
543+ const s = sessionManager ( {
544+ account : '0x0000000000000000000000000000000000000001' ,
545+ fetch : mockFetch as typeof globalThis . fetch ,
546+ } )
547+
548+ const body = new TextEncoder ( ) . encode ( '{"stream":true}' ) . buffer
549+ await s . sse ( 'https://api.example.com/stream' , {
550+ method : 'POST' ,
551+ headers : new Headers ( { 'Content-Type' : 'application/json' } ) ,
552+ body,
553+ } )
554+
555+ const requestInit = mockFetch . mock . calls [ 0 ] ?. [ 1 ]
556+ const headers = new Headers ( requestInit ?. headers )
557+
558+ expect ( headers . get ( 'Accept' ) ) . toBe ( 'text/event-stream' )
559+ expect ( headers . get ( 'Content-Type' ) ) . toBe ( 'application/json' )
560+ expect ( requestInit ?. body ) . toBe ( body )
561+ } )
562+
563+ test ( 'rejects restored SSE when paid response remains a 402 problem response' , async ( ) => {
564+ vi . resetModules ( )
565+
566+ const createCredential = vi . fn ( ) . mockResolvedValue ( 'voucher' )
567+ const helperCreateCredential = vi . fn ( ) . mockResolvedValue ( 'restore:5000000' )
568+
569+ vi . doMock ( './Session.js' , ( ) => ( {
570+ session : vi . fn ( ( ) => ( {
571+ createCredential,
572+ } ) ) ,
573+ UnrecoverableRestoreError,
574+ } ) )
575+
576+ vi . doMock ( '../../client/internal/Fetch.js' , ( ) => ( {
577+ from : ( {
578+ fetch,
579+ onChallenge,
580+ } : {
581+ fetch : typeof globalThis . fetch
582+ onChallenge : Function
583+ } ) => {
584+ return async ( input : RequestInfo | URL , init ?: RequestInit ) => {
585+ await onChallenge ( makeChallenge ( ) , { createCredential : helperCreateCredential } )
586+ return fetch ( input , init )
587+ }
588+ } ,
589+ } ) )
590+
591+ try {
592+ const { sessionManager : isolatedSessionManager } = await import ( './SessionManager.js' )
593+ const mockFetch = vi . fn < typeof globalThis . fetch > ( ) . mockResolvedValue (
594+ makeProblemResponse ( 402 , {
595+ type : 'https://example.com/problems/payment-required' ,
596+ title : 'Payment Required' ,
597+ detail : 'Session restore voucher was rejected' ,
598+ } ) ,
599+ )
600+
601+ const s = isolatedSessionManager ( {
602+ account : '0x0000000000000000000000000000000000000001' ,
603+ fetch : mockFetch ,
604+ restore : {
605+ channelId,
606+ cumulativeAmount : 5_000_000n ,
607+ } ,
608+ } )
609+
610+ await expect ( s . sse ( 'https://api.example.com/stream' ) ) . rejects . toThrow ( / s t a t u s 4 0 2 / i)
611+ } finally {
612+ vi . doUnmock ( './Session.js' )
613+ vi . doUnmock ( '../../client/internal/Fetch.js' )
614+ vi . resetModules ( )
615+ }
616+ } )
617+
533618 test ( 'restored sse resumes same channel when required cumulative exceeds current' , async ( ) => {
534619 vi . resetModules ( )
535620
0 commit comments