33 */
44import { describe , expect , it } from 'vitest'
55import {
6+ quickBooksAddAttachmentContract ,
67 quickBooksCreateBillPaymentContract ,
8+ quickBooksDownloadDocumentContract ,
79 quickBooksUpdateBillContract ,
810 quickBooksUpdateBillPaymentContract ,
911 quickBooksUpdateCreditMemoContract ,
@@ -16,7 +18,10 @@ import {
1618 quickBooksUpdateVendorContract ,
1719 quickBooksUpdateVendorCreditContract ,
1820} from '@/lib/api/contracts/tools/quickbooks'
21+ import { quickbooksAddAttachmentTool } from '@/tools/quickbooks/add_attachment'
1922import { quickbooksCreateBillPaymentTool } from '@/tools/quickbooks/create_bill_payment'
23+ import { quickbooksDownloadAttachmentTool } from '@/tools/quickbooks/download_attachment'
24+ import { quickbooksDownloadTransactionPdfTool } from '@/tools/quickbooks/download_transaction_pdf'
2025import { quickbooksUpdateBillTool } from '@/tools/quickbooks/update_bill'
2126import { quickbooksUpdateBillPaymentTool } from '@/tools/quickbooks/update_bill_payment'
2227import { quickbooksUpdateCreditMemoTool } from '@/tools/quickbooks/update_credit_memo'
@@ -62,6 +67,44 @@ const CONTRACT_BOUND_OPERATIONS = [
6267 [ 'update_vendor_credit' , quickbooksUpdateVendorCreditTool , quickBooksUpdateVendorCreditContract ] ,
6368] as const
6469
70+ /**
71+ * The file operations do not expose a flat `shape`: the download body is a
72+ * discriminated union (one option per `documentKind`) and the add-attachment
73+ * body carries a `superRefine`. Their declared keys are still introspectable,
74+ * so they are held to the same parity rule as the JSON operations.
75+ */
76+ const FILE_OPERATIONS = [
77+ [
78+ 'download_attachment' ,
79+ quickbooksDownloadAttachmentTool ,
80+ unionOptionKeys ( quickBooksDownloadDocumentContract . body , 'attachment' ) ,
81+ ] ,
82+ [
83+ 'download_transaction_pdf' ,
84+ quickbooksDownloadTransactionPdfTool ,
85+ unionOptionKeys ( quickBooksDownloadDocumentContract . body , 'transaction_pdf' ) ,
86+ ] ,
87+ [
88+ 'add_attachment' ,
89+ quickbooksAddAttachmentTool ,
90+ new Set (
91+ Object . keys (
92+ ( quickBooksAddAttachmentContract . body as unknown as { shape : Record < string , unknown > } )
93+ . shape
94+ )
95+ ) ,
96+ ] ,
97+ ] as const
98+
99+ /** Keys declared by the union option whose `documentKind` literal matches. */
100+ function unionOptionKeys ( body : unknown , documentKind : string ) : Set < string > {
101+ const options = ( body as { options : Array < { shape : Record < string , { value ?: string } > } > } )
102+ . options
103+ const option = options . find ( ( candidate ) => candidate . shape . documentKind ?. value === documentKind )
104+ if ( ! option ) throw new Error ( `No download contract option for documentKind ${ documentKind } ` )
105+ return new Set ( Object . keys ( option . shape ) )
106+ }
107+
65108describe ( 'QuickBooks contract/tool param parity' , ( ) => {
66109 it . each ( CONTRACT_BOUND_OPERATIONS ) (
67110 '%s declares every tool param in its contract body' ,
@@ -72,4 +115,12 @@ describe('QuickBooks contract/tool param parity', () => {
72115 expect ( dropped ) . toEqual ( [ ] )
73116 }
74117 )
118+
119+ it . each ( FILE_OPERATIONS ) (
120+ '%s declares every tool param in its contract body' ,
121+ ( _n , tool , declared ) => {
122+ const dropped = Object . keys ( tool . params ) . filter ( ( param ) => ! declared . has ( param ) )
123+ expect ( dropped ) . toEqual ( [ ] )
124+ }
125+ )
75126} )
0 commit comments