@@ -5,7 +5,12 @@ import { prepareToolExecution, transformBlockTool } from '@/providers/utils'
55import { prepareBlockState } from '@/stores/workflows/prepare-block-state'
66import * as toolMetadata from '@/tools/metadata'
77import { getSubBlocksForToolInput } from '@/tools/params'
8+ import { zohoDeskAddCommentTool } from '@/tools/zoho_desk/add_comment'
89import { zohoDeskCreateTicketTool } from '@/tools/zoho_desk/create_ticket'
10+ import { zohoDeskGetAccountTool } from '@/tools/zoho_desk/get_account'
11+ import { zohoDeskGetContactTool } from '@/tools/zoho_desk/get_contact'
12+ import { zohoDeskGetDepartmentTool } from '@/tools/zoho_desk/get_department'
13+ import { zohoDeskListContactsTool } from '@/tools/zoho_desk/list_contacts'
914import { zohoDeskListTicketsTool } from '@/tools/zoho_desk/list_tickets'
1015import { zohoDeskSearchAccountsTool } from '@/tools/zoho_desk/search_accounts'
1116import { zohoDeskSearchContactsTool } from '@/tools/zoho_desk/search_contacts'
@@ -28,6 +33,11 @@ beforeEach(() => {
2833 zohoDeskSearchTicketsTool ,
2934 zohoDeskSearchContactsTool ,
3035 zohoDeskSearchAccountsTool ,
36+ zohoDeskGetContactTool ,
37+ zohoDeskGetDepartmentTool ,
38+ zohoDeskListContactsTool ,
39+ zohoDeskUpdateContactTool ,
40+ zohoDeskAddCommentTool ,
3141 ] . find ( ( tool ) => tool . id === id )
3242 )
3343} )
@@ -275,3 +285,135 @@ describe('Zoho Desk operation transitions', () => {
275285 ) . not . toHaveProperty ( 'direction' )
276286 } )
277287} )
288+
289+ describe ( 'Zoho Desk lookup and read-option transitions' , ( ) => {
290+ it . each ( [
291+ { operation : 'get_contact' , title : 'Contact ID' } ,
292+ { operation : 'get_department' , title : 'Department' } ,
293+ ] ) ( 'does not reuse a $operation lookup as a ticket reassignment' , ( { operation, title } ) => {
294+ const editor = getSubBlocksForToolInput (
295+ `zoho_desk_${ operation } ` ,
296+ 'zoho_desk' ,
297+ { operation } ,
298+ undefined ,
299+ ZohoDeskBlock
300+ )
301+ const input = editor ?. subBlocks . find ( ( field ) => field . title === title )
302+ if ( ! input ) throw new Error ( `Lookup input missing: ${ title } ` )
303+ const mapper = ZohoDeskBlock . tools . config ?. params
304+ const body = zohoDeskUpdateTicketTool . request . body
305+ if ( ! mapper || typeof body !== 'function' ) throw new Error ( 'Ticket mapping missing' )
306+ const mapped = mapper ( {
307+ operation : 'update_ticket' ,
308+ [ input . id ] : 'lookup-fixture' ,
309+ priority : 'High' ,
310+ } )
311+ expect (
312+ body ( { accessToken : 'fixture' , orgId : 'org-fixture' , ticketId : 'ticket-fixture' , ...mapped } )
313+ ) . toEqual ( { priority : 'High' } )
314+ } )
315+
316+ it . each ( [
317+ {
318+ operation : 'list_tickets' ,
319+ target : 'search_tickets' ,
320+ title : 'Sort By' ,
321+ value : 'responseDueDate' ,
322+ queryKey : 'sortBy' ,
323+ buildUrl : ( params : Record < string , unknown > ) => {
324+ const url = zohoDeskSearchTicketsTool . request . url
325+ if ( typeof url !== 'function' ) throw new Error ( 'Search URL missing' )
326+ return url ( { accessToken : 'fixture' , orgId : 'org-fixture' , ...params } )
327+ } ,
328+ } ,
329+ {
330+ operation : 'list_contacts' ,
331+ target : 'get_account' ,
332+ title : 'Include' ,
333+ value : 'accounts' ,
334+ queryKey : 'include' ,
335+ buildUrl : ( params : Record < string , unknown > ) => {
336+ const url = zohoDeskGetAccountTool . request . url
337+ if ( typeof url !== 'function' ) throw new Error ( 'Account URL missing' )
338+ return url ( {
339+ accessToken : 'fixture' ,
340+ orgId : 'org-fixture' ,
341+ accountId : 'account-fixture' ,
342+ ...params ,
343+ } )
344+ } ,
345+ } ,
346+ ] ) (
347+ 'does not reuse $operation options in $target' ,
348+ ( { operation, target, title, value, queryKey, buildUrl } ) => {
349+ const editor = getSubBlocksForToolInput (
350+ `zoho_desk_${ operation } ` ,
351+ 'zoho_desk' ,
352+ { operation } ,
353+ undefined ,
354+ ZohoDeskBlock
355+ )
356+ const input = editor ?. subBlocks . find ( ( field ) => field . title === title )
357+ if ( ! input ) throw new Error ( `Read option missing: ${ title } ` )
358+ const mapper = ZohoDeskBlock . tools . config ?. params
359+ if ( ! mapper ) throw new Error ( 'Request mapper missing' )
360+ const mapped = mapper ( { operation : target , [ input . id ] : value } )
361+ expect ( new URL ( buildUrl ( mapped ) ) . searchParams . has ( queryKey ) ) . toBe ( false )
362+ }
363+ )
364+ } )
365+
366+ describe ( 'Zoho Desk mutation resource transitions' , ( ) => {
367+ it ( 'does not reuse contact edits in an account update' , ( ) => {
368+ const editor = getSubBlocksForToolInput (
369+ zohoDeskUpdateContactTool . id ,
370+ 'zoho_desk' ,
371+ { operation : 'update_contact' } ,
372+ undefined ,
373+ ZohoDeskBlock
374+ )
375+ const email = editor ?. subBlocks . find ( ( field ) => field . title === 'Email' )
376+ if ( ! email ) throw new Error ( 'Contact email input missing' )
377+ const mapper = ZohoDeskBlock . tools . config ?. params
378+ const body = zohoDeskUpdateAccountTool . request . body
379+ if ( ! mapper || typeof body !== 'function' ) throw new Error ( 'Account mapping missing' )
380+ const mapped = mapper ( {
381+ operation : 'update_account' ,
382+ [ email . id ] : 'contact@example.com' ,
383+ accountName : 'Updated account' ,
384+ } )
385+ expect (
386+ body ( {
387+ accessToken : 'fixture' ,
388+ orgId : 'org-fixture' ,
389+ accountId : 'account-fixture' ,
390+ ...mapped ,
391+ } )
392+ ) . toEqual ( { accountName : 'Updated account' } )
393+ } )
394+
395+ it ( 'does not reuse an internal comment as outbound reply content' , ( ) => {
396+ const stored = editorParams ( zohoDeskAddCommentTool . id , 'add_comment' , {
397+ content : 'Internal staff note' ,
398+ isPublic : false ,
399+ } )
400+ const mapper = ZohoDeskBlock . tools . config ?. params
401+ const body = zohoDeskSendReplyTool . request . body
402+ if ( ! mapper || typeof body !== 'function' ) throw new Error ( 'Reply mapping missing' )
403+ const mapped = mapper ( {
404+ ...stored ,
405+ operation : 'send_reply' ,
406+ fromEmailAddress : 'support@example.com' ,
407+ } )
408+ const params = {
409+ accessToken : 'fixture' ,
410+ orgId : 'org-fixture' ,
411+ ticketId : 'ticket-fixture' ,
412+ content : '' ,
413+ replyContent : '' ,
414+ fromEmailAddress : '' ,
415+ ...mapped ,
416+ }
417+ expect ( body ( params ) ) . not . toHaveProperty ( 'content' )
418+ } )
419+ } )
0 commit comments