@@ -45,16 +45,13 @@ import { serializeChain, serializeNodeInfo, serializeProviderCache } from './uti
4545import type { ProviderCacheJson } from './utils/serialization' ;
4646
4747const getCustomFetch =
48- ( expectedOperationName : string , expectedResponse : object ) =>
48+ ( expectedOperationName : string , expectedBody ?: BodyInit | null ) =>
4949 async ( url : string , options : RequestInit | undefined ) => {
5050 const graphqlRequest = JSON . parse ( options ?. body as string ) ;
5151 const { operationName } = graphqlRequest ;
5252
5353 if ( operationName === expectedOperationName ) {
54- const responseText = JSON . stringify ( {
55- data : expectedResponse ,
56- } ) ;
57- const response = Promise . resolve ( new Response ( responseText , options ) ) ;
54+ const response = Promise . resolve ( new Response ( expectedBody , options ) ) ;
5855
5956 return response ;
6057 }
@@ -244,13 +241,16 @@ describe('Provider', () => {
244241 using launched = await setupTestProviderAndWallets ( ) ;
245242 const { provider } = launched ;
246243
247- const mockProvider = new Provider ( provider . url , {
248- fetch : getCustomFetch ( 'getTransaction' , {
244+ const expectedBody = JSON . stringify ( {
245+ data : {
249246 transaction : {
250247 id : '0x1234567890abcdef' ,
251248 rawPayload : MOCK_TX_UNKNOWN_RAW_PAYLOAD , // Unknown transaction type
252249 } ,
253- } ) ,
250+ } ,
251+ } ) ;
252+ const mockProvider = new Provider ( provider . url , {
253+ fetch : getCustomFetch ( 'getTransaction' , expectedBody ) ,
254254 } ) ;
255255
256256 // Spy on console.warn
@@ -273,9 +273,8 @@ describe('Provider', () => {
273273 using launched = await setupTestProviderAndWallets ( ) ;
274274 const { provider : nodeProvider } = launched ;
275275
276- // Create a mock provider with custom getTransactions operation
277- const mockProvider = new Provider ( nodeProvider . url , {
278- fetch : getCustomFetch ( 'getTransactions' , {
276+ const expectedBody = JSON . stringify ( {
277+ data : {
279278 transactions : {
280279 edges : [
281280 {
@@ -298,7 +297,12 @@ describe('Provider', () => {
298297 endCursor : null ,
299298 } ,
300299 } ,
301- } ) ,
300+ } ,
301+ } ) ;
302+
303+ // Create a mock provider with custom getTransactions operation
304+ const mockProvider = new Provider ( nodeProvider . url , {
305+ fetch : getCustomFetch ( 'getTransactions' , expectedBody ) ,
302306 } ) ;
303307
304308 // Spy on console.warn
@@ -476,7 +480,10 @@ describe('Provider', () => {
476480 const providerUrl = providerForUrl . url ;
477481
478482 const provider = new Provider ( providerUrl , {
479- fetch : getCustomFetch ( 'getVersion' , { nodeInfo : { nodeVersion : '0.30.0' } } ) ,
483+ fetch : getCustomFetch (
484+ 'getVersion' ,
485+ JSON . stringify ( { data : { nodeInfo : { nodeVersion : '0.30.0' } } } )
486+ ) ,
480487 } ) ;
481488
482489 expect ( await provider . getVersion ( ) ) . toEqual ( '0.30.0' ) ;
@@ -515,7 +522,10 @@ describe('Provider', () => {
515522 fetchChainAndNodeInfo . mockRestore ( ) ;
516523
517524 await provider . connect ( providerUrl , {
518- fetch : getCustomFetch ( 'getVersion' , { nodeInfo : { nodeVersion : '0.30.0' } } ) ,
525+ fetch : getCustomFetch (
526+ 'getVersion' ,
527+ JSON . stringify ( { data : { nodeInfo : { nodeVersion : '0.30.0' } } } )
528+ ) ,
519529 } ) ;
520530
521531 expect ( await provider . getVersion ( ) ) . toEqual ( '0.30.0' ) ;
@@ -851,10 +861,10 @@ describe('Provider', () => {
851861
852862 const provider = new Provider ( nodeProvider . url , {
853863 fetch : async ( url , options ) =>
854- getCustomFetch ( 'getMessageProof' , { messageProof : MESSAGE_PROOF_RAW_RESPONSE } ) (
855- url ,
856- options
857- ) ,
864+ getCustomFetch (
865+ 'getMessageProof' ,
866+ JSON . stringify ( { data : { messageProof : MESSAGE_PROOF_RAW_RESPONSE } } )
867+ ) ( url , options ) ,
858868 } ) ;
859869
860870 const transactionId = '0x79c54219a5c910979e5e4c2728df163fa654a1fe03843e6af59daa2c3fcd42ea' ;
@@ -874,7 +884,10 @@ describe('Provider', () => {
874884
875885 const provider = new Provider ( nodeProvider . url , {
876886 fetch : async ( url , options ) =>
877- getCustomFetch ( 'getMessageStatus' , { messageStatus : messageStatusResponse } ) ( url , options ) ,
887+ getCustomFetch (
888+ 'getMessageStatus' ,
889+ JSON . stringify ( { data : { messageStatus : messageStatusResponse } } )
890+ ) ( url , options ) ,
878891 } ) ;
879892 const messageStatus = await provider . getMessageStatus (
880893 '0x0000000000000000000000000000000000000000000000000000000000000008'
@@ -2656,6 +2669,137 @@ describe('Provider', () => {
26562669 vi . restoreAllMocks ( ) ;
26572670 } ) ;
26582671
2672+ describe ( 'Non-populated response body' , ( ) => {
2673+ // Reset back to default
2674+ afterAll ( ( ) => {
2675+ Provider . ENABLE_RPC_CONSISTENCY = true ;
2676+ } ) ;
2677+
2678+ describe ( 'ENABLE_RPC_CONSISTENCY = false' , ( ) => {
2679+ beforeAll ( ( ) => {
2680+ Provider . ENABLE_RPC_CONSISTENCY = false ;
2681+ } ) ;
2682+
2683+ it (
2684+ 'should not fall over if the response body is null [non-subscription]' ,
2685+ { timeout : 20000 } ,
2686+ async ( ) => {
2687+ using launched = await setupTestProviderAndWallets ( ) ;
2688+ const {
2689+ provider : { url } ,
2690+ } = launched ;
2691+ const provider = new Provider ( url , {
2692+ fetch : getCustomFetch ( 'estimateGasPrice' , null ) ,
2693+ } ) ;
2694+
2695+ await expectToThrowFuelError ( ( ) => provider . estimateGasPrice ( 10 ) , {
2696+ code : ErrorCode . RESPONSE_BODY_EMPTY ,
2697+ message : 'The response from the server is missing the body' ,
2698+ metadata : {
2699+ timestamp : expect . any ( String ) ,
2700+ request : expect . any ( Object ) ,
2701+ response : expect . any ( Object ) ,
2702+ } ,
2703+ } ) ;
2704+ }
2705+ ) ;
2706+
2707+ it (
2708+ 'should not fall over if the response body is null [subscription]' ,
2709+ { timeout : 20000 } ,
2710+ async ( ) => {
2711+ using launched = await setupTestProviderAndWallets ( ) ;
2712+ const {
2713+ provider : { url } ,
2714+ } = launched ;
2715+ const provider = new Provider ( url , {
2716+ fetch : getCustomFetch ( 'submitAndAwaitStatus' , null ) ,
2717+ } ) ;
2718+
2719+ const request = new ScriptTransactionRequest ( ) ;
2720+
2721+ await expectToThrowFuelError (
2722+ ( ) =>
2723+ provider . operations . submitAndAwaitStatus ( {
2724+ encodedTransaction : hexlify ( request . toTransactionBytes ( ) ) ,
2725+ } ) ,
2726+ {
2727+ code : ErrorCode . RESPONSE_BODY_EMPTY ,
2728+ message : 'The response from the server is missing the body' ,
2729+ metadata : {
2730+ timestamp : expect . any ( String ) ,
2731+ request : expect . any ( Object ) ,
2732+ response : expect . any ( Object ) ,
2733+ } ,
2734+ }
2735+ ) ;
2736+ }
2737+ ) ;
2738+ } ) ;
2739+
2740+ describe ( 'ENABLE_RPC_CONSISTENCY = true' , ( ) => {
2741+ beforeAll ( ( ) => {
2742+ Provider . ENABLE_RPC_CONSISTENCY = true ;
2743+ } ) ;
2744+
2745+ it (
2746+ 'should not fall over if the response body is null [non-subscription]' ,
2747+ { timeout : 20000 } ,
2748+ async ( ) => {
2749+ using launched = await setupTestProviderAndWallets ( ) ;
2750+ const {
2751+ provider : { url } ,
2752+ } = launched ;
2753+ const provider = new Provider ( url , {
2754+ fetch : getCustomFetch ( 'estimateGasPrice' , null ) ,
2755+ } ) ;
2756+
2757+ await expectToThrowFuelError ( ( ) => provider . estimateGasPrice ( 10 ) , {
2758+ code : ErrorCode . RESPONSE_BODY_EMPTY ,
2759+ message : 'The response from the server is missing the body' ,
2760+ metadata : {
2761+ timestamp : expect . any ( String ) ,
2762+ request : expect . any ( Object ) ,
2763+ response : expect . any ( Object ) ,
2764+ } ,
2765+ } ) ;
2766+ }
2767+ ) ;
2768+
2769+ it (
2770+ 'should not fall over if the response body is null [subscription]' ,
2771+ { timeout : 20000 } ,
2772+ async ( ) => {
2773+ using launched = await setupTestProviderAndWallets ( ) ;
2774+ const {
2775+ provider : { url } ,
2776+ } = launched ;
2777+ const provider = new Provider ( url , {
2778+ fetch : getCustomFetch ( 'submitAndAwaitStatus' , null ) ,
2779+ } ) ;
2780+
2781+ const request = new ScriptTransactionRequest ( ) ;
2782+
2783+ await expectToThrowFuelError (
2784+ ( ) =>
2785+ provider . operations . submitAndAwaitStatus ( {
2786+ encodedTransaction : hexlify ( request . toTransactionBytes ( ) ) ,
2787+ } ) ,
2788+ {
2789+ code : ErrorCode . RESPONSE_BODY_EMPTY ,
2790+ message : 'The response from the server is missing the body' ,
2791+ metadata : {
2792+ timestamp : expect . any ( String ) ,
2793+ request : expect . any ( Object ) ,
2794+ response : expect . any ( Object ) ,
2795+ } ,
2796+ }
2797+ ) ;
2798+ }
2799+ ) ;
2800+ } ) ;
2801+ } ) ;
2802+
26592803 describe ( 'Waiting for transaction statuses' , ( ) => {
26602804 const PreconfirmationSuccessStatus = 'PreconfirmationSuccessStatus' ;
26612805 const SuccessStatus = 'SuccessStatus' ;
0 commit comments