@@ -14,14 +14,14 @@ const {
1414 mockGetWorkspaceCustomTool,
1515 mockGetWorkspaceCustomToolByTitle,
1616 mockDeleteWorkspaceCustomTool,
17- mockUpsertCustomTools ,
17+ mockUpdateWorkspaceCustomTool ,
1818} = vi . hoisted ( ( ) => ( {
1919 mockCheckRateLimit : vi . fn ( ) ,
2020 mockResolveWorkspaceAccess : vi . fn ( ) ,
2121 mockGetWorkspaceCustomTool : vi . fn ( ) ,
2222 mockGetWorkspaceCustomToolByTitle : vi . fn ( ) ,
2323 mockDeleteWorkspaceCustomTool : vi . fn ( ) ,
24- mockUpsertCustomTools : vi . fn ( ) ,
24+ mockUpdateWorkspaceCustomTool : vi . fn ( ) ,
2525} ) )
2626
2727vi . mock ( '@/app/api/v1/middleware' , ( ) => ( {
@@ -33,7 +33,7 @@ vi.mock('@/lib/workflows/custom-tools/operations', () => ({
3333 getWorkspaceCustomTool : mockGetWorkspaceCustomTool ,
3434 getWorkspaceCustomToolByTitle : mockGetWorkspaceCustomToolByTitle ,
3535 deleteWorkspaceCustomTool : mockDeleteWorkspaceCustomTool ,
36- upsertCustomTools : mockUpsertCustomTools ,
36+ updateWorkspaceCustomTool : mockUpdateWorkspaceCustomTool ,
3737} ) )
3838
3939vi . mock ( '@/app/api/v2/lib/gate' , ( ) => ( {
@@ -175,7 +175,7 @@ describe('PATCH /api/v2/custom-tools/[id]', () => {
175175 mockResolveWorkspaceAccess . mockResolvedValue ( null )
176176 mockGetWorkspaceCustomTool . mockResolvedValue ( buildTool ( ) )
177177 mockGetWorkspaceCustomToolByTitle . mockResolvedValue ( null )
178- mockUpsertCustomTools . mockResolvedValue ( [ buildTool ( ) ] )
178+ mockUpdateWorkspaceCustomTool . mockResolvedValue ( buildTool ( ) )
179179 } )
180180
181181 it ( 'returns 404 when the v2 API surface flag is off' , async ( ) => {
@@ -186,20 +186,20 @@ describe('PATCH /api/v2/custom-tools/[id]', () => {
186186 const res = await callPatch ( { workspaceId : 'workspace-1' , code : 'return 1' } )
187187
188188 expect ( res . status ) . toBe ( 404 )
189- expect ( mockUpsertCustomTools ) . not . toHaveBeenCalled ( )
189+ expect ( mockUpdateWorkspaceCustomTool ) . not . toHaveBeenCalled ( )
190190 } )
191191
192192 it ( '400s when no field to change is supplied' , async ( ) => {
193193 const res = await callPatch ( { workspaceId : 'workspace-1' } )
194194 expect ( res . status ) . toBe ( 400 )
195- expect ( mockUpsertCustomTools ) . not . toHaveBeenCalled ( )
195+ expect ( mockUpdateWorkspaceCustomTool ) . not . toHaveBeenCalled ( )
196196 } )
197197
198198 it ( 'surfaces an access-denied failure in the v2 error envelope' , async ( ) => {
199199 mockResolveWorkspaceAccess . mockResolvedValue ( ACCESS_DENIED )
200200 const res = await callPatch ( { workspaceId : 'workspace-1' , code : 'return 1' } )
201201 expect ( res . status ) . toBe ( 403 )
202- expect ( mockUpsertCustomTools ) . not . toHaveBeenCalled ( )
202+ expect ( mockUpdateWorkspaceCustomTool ) . not . toHaveBeenCalled ( )
203203 } )
204204
205205 it ( 'returns the rate-limit response when denied' , async ( ) => {
@@ -213,7 +213,7 @@ describe('PATCH /api/v2/custom-tools/[id]', () => {
213213 mockGetWorkspaceCustomTool . mockResolvedValue ( null )
214214 const res = await callPatch ( { workspaceId : 'workspace-1' , code : 'return 1' } )
215215 expect ( res . status ) . toBe ( 404 )
216- expect ( mockUpsertCustomTools ) . not . toHaveBeenCalled ( )
216+ expect ( mockUpdateWorkspaceCustomTool ) . not . toHaveBeenCalled ( )
217217 } )
218218
219219 it ( '409s when renaming onto an existing title' , async ( ) => {
@@ -223,27 +223,29 @@ describe('PATCH /api/v2/custom-tools/[id]', () => {
223223
224224 expect ( res . status ) . toBe ( 409 )
225225 expect ( ( await res . json ( ) ) . error . code ) . toBe ( 'CONFLICT' )
226- expect ( mockUpsertCustomTools ) . not . toHaveBeenCalled ( )
226+ expect ( mockUpdateWorkspaceCustomTool ) . not . toHaveBeenCalled ( )
227227 } )
228228
229229 it ( 'merges the partial body against the stored tool' , async ( ) => {
230230 const res = await callPatch ( { workspaceId : 'workspace-1' , code : 'return 2' } )
231231
232232 expect ( res . status ) . toBe ( 200 )
233- expect ( mockUpsertCustomTools ) . toHaveBeenCalledWith (
234- expect . objectContaining ( {
235- workspaceId : 'workspace-1' ,
236- userId : 'user-1' ,
237- tools : [
238- {
239- id : 'tool_abc123' ,
240- title : 'lookup_order' ,
241- schema : TOOL_SCHEMA ,
242- code : 'return 2' ,
243- } ,
244- ] ,
245- } )
246- )
233+ expect ( mockUpdateWorkspaceCustomTool ) . toHaveBeenCalledWith ( {
234+ workspaceId : 'workspace-1' ,
235+ toolId : 'tool_abc123' ,
236+ title : 'lookup_order' ,
237+ schema : TOOL_SCHEMA ,
238+ code : 'return 2' ,
239+ } )
240+ } )
241+
242+ it ( '404s rather than orphaning a tool deleted between the read and the write' , async ( ) => {
243+ mockUpdateWorkspaceCustomTool . mockResolvedValue ( null )
244+
245+ const res = await callPatch ( { workspaceId : 'workspace-1' , code : 'return 2' } )
246+
247+ expect ( res . status ) . toBe ( 404 )
248+ expect ( ( await res . json ( ) ) . error . code ) . toBe ( 'NOT_FOUND' )
247249 } )
248250} )
249251
0 commit comments