File tree Expand file tree Collapse file tree
app/api/tools/brex/upload-receipt Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -94,6 +94,29 @@ describe('POST /api/tools/brex/upload-receipt', () => {
9494 expect ( uploadInit . method ) . toBe ( 'PUT' )
9595 } )
9696
97+ it ( 'rejects a whitespace-only expense ID instead of falling back to receipt match' , async ( ) => {
98+ const response = await POST ( createMockRequest ( 'POST' , { ...baseBody , expenseId : ' ' } ) )
99+ expect ( response . status ) . toBe ( 400 )
100+ expect ( mockFetch ) . not . toHaveBeenCalled ( )
101+ } )
102+
103+ it ( 'trims a padded expense ID before building the upload URL' , async ( ) => {
104+ mockFetch
105+ . mockResolvedValueOnce (
106+ jsonResponse ( { id : 'receipt_5' , uri : 'https://s3.example.com/presigned' } )
107+ )
108+ . mockResolvedValueOnce ( jsonResponse ( { } ) )
109+
110+ const response = await POST (
111+ createMockRequest ( 'POST' , { ...baseBody , expenseId : ' expense_123 ' } )
112+ )
113+ expect ( response . status ) . toBe ( 200 )
114+ const [ createUrl ] = mockFetch . mock . calls [ 0 ]
115+ expect ( createUrl ) . toBe ( 'https://api.brex.com/v1/expenses/card/expense_123/receipt_upload' )
116+ const data = await response . json ( )
117+ expect ( data . output . expenseId ) . toBe ( 'expense_123' )
118+ } )
119+
97120 it ( 'uses receipt match when no expense ID is provided' , async ( ) => {
98121 mockFetch
99122 . mockResolvedValueOnce (
Original file line number Diff line number Diff line change @@ -53,13 +53,12 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
5353 }
5454
5555 const effectiveReceiptName = receiptName ?. trim ( ) || userFile . name
56- const trimmedExpenseId = expenseId ?. trim ( ) || undefined
57- const endpoint = trimmedExpenseId
58- ? `${ BREX_API_BASE } /v1/expenses/card/${ encodeURIComponent ( trimmedExpenseId ) } /receipt_upload`
56+ const endpoint = expenseId
57+ ? `${ BREX_API_BASE } /v1/expenses/card/${ encodeURIComponent ( expenseId ) } /receipt_upload`
5958 : `${ BREX_API_BASE } /v1/expenses/card/receipt_match`
6059
6160 logger . info (
62- `[${ requestId } ] Creating Brex ${ trimmedExpenseId ? 'receipt upload' : 'receipt match' } : ${ effectiveReceiptName } (${ fileBuffer . length } bytes)`
61+ `[${ requestId } ] Creating Brex ${ expenseId ? 'receipt upload' : 'receipt match' } : ${ effectiveReceiptName } (${ fileBuffer . length } bytes)`
6362 )
6463
6564 const createResponse = await fetch ( endpoint , {
@@ -116,7 +115,7 @@ export const POST = withRouteHandler(async (request: NextRequest) => {
116115 output : {
117116 receiptId : createData . id ,
118117 receiptName : effectiveReceiptName ,
119- expenseId : trimmedExpenseId ?? null ,
118+ expenseId : expenseId ?? null ,
120119 } ,
121120 } )
122121 } catch ( error ) {
Original file line number Diff line number Diff line change @@ -5,7 +5,7 @@ import { RawFileInputSchema } from '@/lib/uploads/utils/file-schemas'
55
66export const brexUploadReceiptBodySchema = z . object ( {
77 apiKey : z . string ( ) . min ( 1 , 'API key is required' ) ,
8- expenseId : z . string ( ) . min ( 1 , 'Expense ID cannot be empty' ) . optional ( ) ,
8+ expenseId : z . string ( ) . trim ( ) . min ( 1 , 'Expense ID cannot be empty' ) . optional ( ) ,
99 file : RawFileInputSchema ,
1010 receiptName : z
1111 . string ( )
You can’t perform that action at this time.
0 commit comments