@@ -7,12 +7,22 @@ import type { ExecutionContext } from '@/lib/copilot/request/types'
77const { mocks } = vi . hoisted ( ( ) => ( {
88 mocks : {
99 apiKey : vi . fn ( ) ,
10+ cancelWorkflowExecutionRoute : vi . fn ( ) ,
1011 executeWorkflowUseCase : vi . fn ( ) ,
12+ generateInternalToken : vi . fn ( ) ,
1113 hasExecutionResult : vi . fn ( ) ,
1214 readAttemptedExecutionId : vi . fn ( ) ,
1315 } ,
1416} ) )
1517
18+ vi . mock ( '@/app/api/workflows/[id]/executions/[executionId]/cancel/route' , ( ) => ( {
19+ POST : mocks . cancelWorkflowExecutionRoute ,
20+ } ) )
21+
22+ vi . mock ( '@/lib/auth/internal' , ( ) => ( {
23+ generateInternalToken : mocks . generateInternalToken ,
24+ } ) )
25+
1626vi . mock ( '@/lib/copilot/application/execute-workflow-use-case' , ( ) => ( {
1727 executeCopilotWorkflowUseCase : mocks . executeWorkflowUseCase ,
1828 messageForCopilotWorkflowError : ( _error : unknown , fallback = 'Workflow operation failed' ) =>
@@ -53,12 +63,14 @@ const context = {
5363 workspaceId : 'workspace-1' ,
5464 workflowId : 'workflow-1' ,
5565 toolCallId : 'tool-call-1' ,
66+ copilotToolExecution : true ,
5667 billingAttribution : { workspaceId : 'workspace-1' } ,
5768} as ExecutionContext
5869
5970describe ( 'workflow mutation Copilot adapters' , ( ) => {
6071 beforeEach ( ( ) => {
6172 vi . clearAllMocks ( )
73+ mocks . generateInternalToken . mockResolvedValue ( 'internal-token' )
6274 mocks . hasExecutionResult . mockReturnValue ( false )
6375 mocks . readAttemptedExecutionId . mockReturnValue ( undefined )
6476 } )
@@ -162,18 +174,18 @@ describe('workflow mutation Copilot adapters', () => {
162174 )
163175 } )
164176
165- it ( 'cancels a workflow run through the registered application command ' , async ( ) => {
166- mocks . executeWorkflowUseCase . mockResolvedValue ( {
167- success : true ,
168- executionId : 'execution-1' ,
169- redisAvailable : true ,
170- durablyRecorded : true ,
171- locallyAborted : false ,
172- pausedCancelled : false ,
173- reason : 'recorded' ,
174- workflowId : 'workflow-1 ' ,
175- workspaceId : 'workspace-1' ,
176- } )
177+ it ( 'cancels a workflow run through the same route as the logs UI ' , async ( ) => {
178+ mocks . cancelWorkflowExecutionRoute . mockResolvedValue (
179+ Response . json ( {
180+ success : true ,
181+ executionId : 'execution-1' ,
182+ redisAvailable : true ,
183+ durablyRecorded : true ,
184+ locallyAborted : false ,
185+ pausedCancelled : false ,
186+ reason : 'recorded ' ,
187+ } )
188+ )
177189
178190 const result = await executeCancelWorkflowRun (
179191 { workflowId : 'workflow-1' , executionId : 'execution-1' } ,
@@ -191,19 +203,40 @@ describe('workflow mutation Copilot adapters', () => {
191203 reason : 'recorded' ,
192204 } ,
193205 } )
194- expect ( mocks . executeWorkflowUseCase ) . toHaveBeenCalledWith (
195- context ,
196- expect . objectContaining ( {
197- operation : expect . objectContaining ( { id : 'workflows.runs.cancel' } ) ,
198- } ) ,
199- { workflowId : 'workflow-1' , runId : 'execution-1' }
206+ expect ( mocks . generateInternalToken ) . toHaveBeenCalledWith ( 'user-1' )
207+ expect ( mocks . cancelWorkflowExecutionRoute ) . toHaveBeenCalledOnce ( )
208+ const [ request , routeContext ] = mocks . cancelWorkflowExecutionRoute . mock . calls [ 0 ]
209+ expect ( request . method ) . toBe ( 'POST' )
210+ expect ( request . headers . get ( 'authorization' ) ) . toBe ( 'Bearer internal-token' )
211+ expect ( request . nextUrl . pathname ) . toBe ( '/api/workflows/workflow-1/executions/execution-1/cancel' )
212+ await expect ( routeContext . params ) . resolves . toEqual ( {
213+ id : 'workflow-1' ,
214+ executionId : 'execution-1' ,
215+ } )
216+ expect ( mocks . executeWorkflowUseCase ) . not . toHaveBeenCalled ( )
217+ } )
218+
219+ it ( 'returns the cancellation route error to the Run agent' , async ( ) => {
220+ mocks . cancelWorkflowExecutionRoute . mockResolvedValue (
221+ Response . json ( { error : 'Execution cannot be cancelled while completed' } , { status : 409 } )
222+ )
223+
224+ const result = await executeCancelWorkflowRun (
225+ { workflowId : 'workflow-1' , executionId : 'execution-1' } ,
226+ context
200227 )
228+
229+ expect ( result ) . toEqual ( {
230+ success : false ,
231+ error : 'Execution cannot be cancelled while completed' ,
232+ } )
201233 } )
202234
203235 it ( 'requires an execution ID before attempting workflow-run cancellation' , async ( ) => {
204236 const result = await executeCancelWorkflowRun ( { workflowId : 'workflow-1' } , context )
205237
206238 expect ( result ) . toEqual ( { success : false , error : 'executionId is required' } )
239+ expect ( mocks . cancelWorkflowExecutionRoute ) . not . toHaveBeenCalled ( )
207240 expect ( mocks . executeWorkflowUseCase ) . not . toHaveBeenCalled ( )
208241 } )
209242
0 commit comments