@@ -20,6 +20,7 @@ import {
2020 buildQuickBooksCdcUrl ,
2121 buildQuickBooksDocumentUrl ,
2222 buildQuickBooksExchangeRateUrl ,
23+ buildQuickBooksHeaders ,
2324 buildQuickBooksListRecordsQuery ,
2425 buildQuickBooksPreferencesUrl ,
2526 buildQuickBooksRecordUrl ,
@@ -81,6 +82,16 @@ describe('QuickBooks generic operations', () => {
8182 ) . toThrow ( 'QuickBooks entity "Vendor" cannot be deleted' )
8283 } )
8384
85+ it ( 'rejects invalid access tokens before constructing QuickBooks headers' , ( ) => {
86+ expect ( ( ) => buildQuickBooksHeaders ( '' ) ) . toThrow ( 'QuickBooks access token is required' )
87+ expect ( ( ) => buildQuickBooksHeaders ( `token\r\nX-Injected: true` ) ) . toThrow (
88+ 'QuickBooks access token contains invalid characters'
89+ )
90+ expect ( ( ) => buildQuickBooksHeaders ( 'x' . repeat ( 4097 ) ) ) . toThrow (
91+ 'QuickBooks access token must be 4096 characters or less'
92+ )
93+ } )
94+
8495 it ( 'normalizes create, update, and delete request payloads' , ( ) => {
8596 const createBody = quickBooksCreateRecordTool . request . body
8697 const updateBody = quickBooksUpdateRecordTool . request . body
@@ -428,6 +439,65 @@ describe('QuickBooks generic operations', () => {
428439 } )
429440 } )
430441
442+ it ( 'rejects successful record responses that omit the entity record' , async ( ) => {
443+ const emptyResponse = ( ) =>
444+ new Response ( JSON . stringify ( { time : '2026-01-20T10:00:00-08:00' } ) , { status : 200 } )
445+
446+ await expect (
447+ quickBooksCreateRecordTool . transformResponse ?.( emptyResponse ( ) , {
448+ accessToken : 'token' ,
449+ realmId : '123145' ,
450+ entity : 'Vendor' ,
451+ payload : { DisplayName : 'Acme Supplies' } ,
452+ } )
453+ ) . rejects . toThrow ( 'QuickBooks API response did not include Vendor' )
454+
455+ await expect (
456+ quickBooksGetRecordTool . transformResponse ?.( emptyResponse ( ) , {
457+ accessToken : 'token' ,
458+ realmId : '123145' ,
459+ entity : 'PurchaseOrder' ,
460+ recordId : '42' ,
461+ } )
462+ ) . rejects . toThrow ( 'QuickBooks API response did not include PurchaseOrder' )
463+
464+ await expect (
465+ quickBooksUpdateRecordTool . transformResponse ?.( emptyResponse ( ) , {
466+ accessToken : 'token' ,
467+ realmId : '123145' ,
468+ entity : 'Vendor' ,
469+ recordId : '7' ,
470+ syncToken : '1' ,
471+ payload : { DisplayName : 'Acme Industrial' } ,
472+ } )
473+ ) . rejects . toThrow ( 'QuickBooks API response did not include Vendor' )
474+
475+ await expect (
476+ quickBooksDeleteRecordTool . transformResponse ?.( emptyResponse ( ) , {
477+ accessToken : 'token' ,
478+ realmId : '123145' ,
479+ entity : 'Bill' ,
480+ recordId : '17' ,
481+ syncToken : '2' ,
482+ } )
483+ ) . rejects . toThrow ( 'QuickBooks API response did not include Bill' )
484+
485+ await expect (
486+ quickBooksGetPreferencesTool . transformResponse ?.( emptyResponse ( ) , {
487+ accessToken : 'token' ,
488+ realmId : '123145' ,
489+ } )
490+ ) . rejects . toThrow ( 'QuickBooks API response did not include Preferences' )
491+
492+ await expect (
493+ quickBooksGetExchangeRateTool . transformResponse ?.( emptyResponse ( ) , {
494+ accessToken : 'token' ,
495+ realmId : '123145' ,
496+ sourceCurrencyCode : 'EUR' ,
497+ } )
498+ ) . rejects . toThrow ( 'QuickBooks API response did not include ExchangeRate' )
499+ } )
500+
431501 it ( 'transforms QuickBooks report sections' , async ( ) => {
432502 const response = new Response (
433503 JSON . stringify ( {
0 commit comments