@@ -268,10 +268,21 @@ describe('validateWorkflowAccess', () => {
268268 } )
269269
270270 it ( 'returns 404 for deployed access when workflow is missing' , async ( ) => {
271+ mockAuthenticateApiKeyFromHeader . mockResolvedValue ( {
272+ success : true ,
273+ userId : 'user-1' ,
274+ keyId : 'key-1' ,
275+ keyType : 'workspace' ,
276+ workspaceId : WORKSPACE_ID ,
277+ } )
271278 mockGetActiveWorkflowRecord . mockResolvedValue ( null )
272279 mockGetWorkflowById . mockResolvedValue ( null )
273280
274- const result = await validateWorkflowAccess ( createRequest ( ) , WORKFLOW_ID , {
281+ const request = new NextRequest ( `http://localhost:3000/api/workflows/${ WORKFLOW_ID } /status` , {
282+ headers : { 'x-api-key' : 'valid-key' } ,
283+ } )
284+
285+ const result = await validateWorkflowAccess ( request , WORKFLOW_ID , {
275286 requireDeployment : true ,
276287 } )
277288
@@ -282,14 +293,70 @@ describe('validateWorkflowAccess', () => {
282293 } ,
283294 } )
284295 expect ( mockCheckHybridAuth ) . not . toHaveBeenCalled ( )
296+ expect ( mockAuthenticateApiKeyFromHeader ) . toHaveBeenNthCalledWith ( 1 , 'valid-key' , {
297+ keyTypes : [ 'workspace' , 'personal' ] ,
298+ } )
299+ } )
300+
301+ it ( 'returns 401 before deployed workflow lookup when api key is missing' , async ( ) => {
302+ const result = await validateWorkflowAccess ( createRequest ( ) , WORKFLOW_ID , {
303+ requireDeployment : true ,
304+ } )
305+
306+ expect ( result ) . toEqual ( {
307+ error : {
308+ message : 'Unauthorized: API key required' ,
309+ status : 401 ,
310+ } ,
311+ } )
312+ expect ( mockGetActiveWorkflowRecord ) . not . toHaveBeenCalled ( )
313+ expect ( mockGetWorkflowById ) . not . toHaveBeenCalled ( )
285314 expect ( mockAuthenticateApiKeyFromHeader ) . not . toHaveBeenCalled ( )
286315 } )
287316
288- it ( 'returns 403 for deployed access when workflow has no workspace' , async ( ) => {
317+ it ( 'returns 401 before deployed workflow lookup when api key is invalid' , async ( ) => {
318+ mockAuthenticateApiKeyFromHeader . mockResolvedValue ( {
319+ success : false ,
320+ error : 'Invalid API key' ,
321+ } )
322+
323+ const request = new NextRequest ( `http://localhost:3000/api/workflows/${ WORKFLOW_ID } /status` , {
324+ headers : { 'x-api-key' : 'invalid-key' } ,
325+ } )
326+
327+ const result = await validateWorkflowAccess ( request , WORKFLOW_ID , {
328+ requireDeployment : true ,
329+ } )
330+
331+ expect ( result ) . toEqual ( {
332+ error : {
333+ message : 'Unauthorized: Invalid API key' ,
334+ status : 401 ,
335+ } ,
336+ } )
337+ expect ( mockGetActiveWorkflowRecord ) . not . toHaveBeenCalled ( )
338+ expect ( mockGetWorkflowById ) . not . toHaveBeenCalled ( )
339+ expect ( mockAuthenticateApiKeyFromHeader ) . toHaveBeenCalledWith ( 'invalid-key' , {
340+ keyTypes : [ 'workspace' , 'personal' ] ,
341+ } )
342+ } )
343+
344+ it ( 'returns 403 for deployed access when authenticated workflow has no workspace' , async ( ) => {
345+ mockAuthenticateApiKeyFromHeader . mockResolvedValue ( {
346+ success : true ,
347+ userId : 'user-1' ,
348+ keyId : 'key-1' ,
349+ keyType : 'workspace' ,
350+ workspaceId : WORKSPACE_ID ,
351+ } )
289352 mockGetActiveWorkflowRecord . mockResolvedValue ( null )
290353 mockGetWorkflowById . mockResolvedValue ( createWorkflow ( { workspaceId : null , isDeployed : true } ) )
291354
292- const result = await validateWorkflowAccess ( createRequest ( ) , WORKFLOW_ID , {
355+ const request = new NextRequest ( `http://localhost:3000/api/workflows/${ WORKFLOW_ID } /status` , {
356+ headers : { 'x-api-key' : 'valid-key' } ,
357+ } )
358+
359+ const result = await validateWorkflowAccess ( request , WORKFLOW_ID , {
293360 requireDeployment : true ,
294361 } )
295362
@@ -301,14 +368,27 @@ describe('validateWorkflowAccess', () => {
301368 } ,
302369 } )
303370 expect ( mockCheckHybridAuth ) . not . toHaveBeenCalled ( )
304- expect ( mockAuthenticateApiKeyFromHeader ) . not . toHaveBeenCalled ( )
371+ expect ( mockAuthenticateApiKeyFromHeader ) . toHaveBeenNthCalledWith ( 1 , 'valid-key' , {
372+ keyTypes : [ 'workspace' , 'personal' ] ,
373+ } )
305374 } )
306375
307- it ( 'returns 404 for deployed access when workflow workspace is archived' , async ( ) => {
376+ it ( 'returns 404 for deployed access when authenticated workflow workspace is archived' , async ( ) => {
377+ mockAuthenticateApiKeyFromHeader . mockResolvedValue ( {
378+ success : true ,
379+ userId : 'user-1' ,
380+ keyId : 'key-1' ,
381+ keyType : 'workspace' ,
382+ workspaceId : WORKSPACE_ID ,
383+ } )
308384 mockGetActiveWorkflowRecord . mockResolvedValue ( null )
309385 mockGetWorkflowById . mockResolvedValue ( createWorkflow ( { isDeployed : true } ) )
310386
311- const result = await validateWorkflowAccess ( createRequest ( ) , WORKFLOW_ID , {
387+ const request = new NextRequest ( `http://localhost:3000/api/workflows/${ WORKFLOW_ID } /status` , {
388+ headers : { 'x-api-key' : 'valid-key' } ,
389+ } )
390+
391+ const result = await validateWorkflowAccess ( request , WORKFLOW_ID , {
312392 requireDeployment : true ,
313393 } )
314394
@@ -320,6 +400,38 @@ describe('validateWorkflowAccess', () => {
320400 } )
321401 expect ( mockGetWorkflowById ) . toHaveBeenCalledWith ( WORKFLOW_ID )
322402 expect ( mockCheckHybridAuth ) . not . toHaveBeenCalled ( )
323- expect ( mockAuthenticateApiKeyFromHeader ) . not . toHaveBeenCalled ( )
403+ expect ( mockAuthenticateApiKeyFromHeader ) . toHaveBeenNthCalledWith ( 1 , 'valid-key' , {
404+ keyTypes : [ 'workspace' , 'personal' ] ,
405+ } )
406+ } )
407+
408+ it ( 'returns 403 for deployed access when authenticated workflow is not deployed' , async ( ) => {
409+ mockAuthenticateApiKeyFromHeader . mockResolvedValue ( {
410+ success : true ,
411+ userId : 'user-1' ,
412+ keyId : 'key-1' ,
413+ keyType : 'workspace' ,
414+ workspaceId : WORKSPACE_ID ,
415+ } )
416+ mockGetActiveWorkflowRecord . mockResolvedValue ( createWorkflow ( { isDeployed : false } ) )
417+
418+ const request = new NextRequest ( `http://localhost:3000/api/workflows/${ WORKFLOW_ID } /status` , {
419+ headers : { 'x-api-key' : 'valid-key' } ,
420+ } )
421+
422+ const result = await validateWorkflowAccess ( request , WORKFLOW_ID , {
423+ requireDeployment : true ,
424+ } )
425+
426+ expect ( result ) . toEqual ( {
427+ error : {
428+ message : 'Workflow is not deployed' ,
429+ status : 403 ,
430+ } ,
431+ } )
432+ expect ( mockAuthenticateApiKeyFromHeader ) . toHaveBeenCalledWith ( 'valid-key' , {
433+ keyTypes : [ 'workspace' , 'personal' ] ,
434+ } )
435+ expect ( mockUpdateApiKeyLastUsed ) . not . toHaveBeenCalled ( )
324436 } )
325437} )
0 commit comments