55import { NextRequest } from 'next/server'
66import { beforeEach , describe , expect , it , vi } from 'vitest'
77
8- const mockValidateWorkflowAccess = vi . fn ( )
9- const mockDbSelect = vi . fn ( )
10- const mockDbFrom = vi . fn ( )
11- const mockDbWhere = vi . fn ( )
12- const mockDbLimit = vi . fn ( )
13- const mockDbOrderBy = vi . fn ( )
14- const mockDeployWorkflow = vi . fn ( )
15- const mockUndeployWorkflow = vi . fn ( )
16- const mockCleanupWebhooksForWorkflow = vi . fn ( )
17- const mockRemoveMcpToolsForWorkflow = vi . fn ( )
8+ const {
9+ mockCleanupWebhooksForWorkflow,
10+ mockDbLimit,
11+ mockDbOrderBy,
12+ mockDbFrom,
13+ mockDbSelect,
14+ mockDbSet,
15+ mockDbUpdate,
16+ mockDbWhere,
17+ mockCreateSchedulesForDeploy,
18+ mockDeployWorkflow,
19+ mockLoadWorkflowFromNormalizedTables,
20+ mockRemoveMcpToolsForWorkflow,
21+ mockSaveTriggerWebhooksForDeploy,
22+ mockSyncMcpToolsForWorkflow,
23+ mockUndeployWorkflow,
24+ mockValidatePublicApiAllowed,
25+ mockValidateWorkflowAccess,
26+ } = vi . hoisted ( ( ) => ( {
27+ mockCleanupWebhooksForWorkflow : vi . fn ( ) ,
28+ mockDbLimit : vi . fn ( ) ,
29+ mockDbOrderBy : vi . fn ( ) ,
30+ mockDbFrom : vi . fn ( ) ,
31+ mockDbSelect : vi . fn ( ) ,
32+ mockDbSet : vi . fn ( ) ,
33+ mockDbUpdate : vi . fn ( ) ,
34+ mockDbWhere : vi . fn ( ) ,
35+ mockCreateSchedulesForDeploy : vi . fn ( ) ,
36+ mockDeployWorkflow : vi . fn ( ) ,
37+ mockLoadWorkflowFromNormalizedTables : vi . fn ( ) ,
38+ mockRemoveMcpToolsForWorkflow : vi . fn ( ) ,
39+ mockSaveTriggerWebhooksForDeploy : vi . fn ( ) ,
40+ mockSyncMcpToolsForWorkflow : vi . fn ( ) ,
41+ mockUndeployWorkflow : vi . fn ( ) ,
42+ mockValidatePublicApiAllowed : vi . fn ( ) ,
43+ mockValidateWorkflowAccess : vi . fn ( ) ,
44+ } ) )
1845
1946vi . mock ( '@sim/logger' , ( ) => ( {
2047 createLogger : ( ) => ( { info : vi . fn ( ) , warn : vi . fn ( ) , error : vi . fn ( ) } ) ,
@@ -33,19 +60,23 @@ vi.mock('@/lib/core/utils/request', () => ({
3360} ) )
3461
3562vi . mock ( '@sim/db' , ( ) => ( {
36- db : { select : mockDbSelect } ,
63+ db : { select : mockDbSelect , update : mockDbUpdate } ,
3764 workflow : { variables : 'variables' , id : 'id' } ,
3865 workflowDeploymentVersion : { state : 'state' , workflowId : 'workflowId' , isActive : 'isActive' , createdAt : 'createdAt' , id : 'id' } ,
3966} ) )
4067
41- vi . mock ( 'drizzle-orm' , ( ) => ( {
42- and : vi . fn ( ) ,
43- desc : vi . fn ( ) ,
44- eq : vi . fn ( ) ,
45- } ) )
68+ vi . mock ( 'drizzle-orm' , async ( importOriginal ) => {
69+ const actual = await importOriginal < typeof import ( 'drizzle-orm' ) > ( )
70+ return {
71+ ...actual ,
72+ and : vi . fn ( ) ,
73+ desc : vi . fn ( ) ,
74+ eq : vi . fn ( ) ,
75+ }
76+ } )
4677
4778vi . mock ( '@/lib/workflows/persistence/utils' , ( ) => ( {
48- loadWorkflowFromNormalizedTables : vi . fn ( ) . mockResolvedValue ( null ) ,
79+ loadWorkflowFromNormalizedTables : ( ... args : unknown [ ] ) => mockLoadWorkflowFromNormalizedTables ( ... args ) ,
4980 deployWorkflow : ( ...args : unknown [ ] ) => mockDeployWorkflow ( ...args ) ,
5081 undeployWorkflow : ( ...args : unknown [ ] ) => mockUndeployWorkflow ( ...args ) ,
5182} ) )
@@ -56,19 +87,19 @@ vi.mock('@/lib/workflows/comparison', () => ({
5687
5788vi . mock ( '@/lib/workflows/schedules' , ( ) => ( {
5889 cleanupDeploymentVersion : vi . fn ( ) ,
59- createSchedulesForDeploy : vi . fn ( ) ,
90+ createSchedulesForDeploy : ( ... args : unknown [ ] ) => mockCreateSchedulesForDeploy ( ... args ) ,
6091 validateWorkflowSchedules : vi . fn ( ) . mockReturnValue ( { isValid : true } ) ,
6192} ) )
6293
6394vi . mock ( '@/lib/webhooks/deploy' , ( ) => ( {
6495 cleanupWebhooksForWorkflow : ( ...args : unknown [ ] ) => mockCleanupWebhooksForWorkflow ( ...args ) ,
6596 restorePreviousVersionWebhooks : vi . fn ( ) ,
66- saveTriggerWebhooksForDeploy : vi . fn ( ) ,
97+ saveTriggerWebhooksForDeploy : ( ... args : unknown [ ] ) => mockSaveTriggerWebhooksForDeploy ( ... args ) ,
6798} ) )
6899
69100vi . mock ( '@/lib/mcp/workflow-mcp-sync' , ( ) => ( {
70101 removeMcpToolsForWorkflow : ( ...args : unknown [ ] ) => mockRemoveMcpToolsForWorkflow ( ...args ) ,
71- syncMcpToolsForWorkflow : vi . fn ( ) ,
102+ syncMcpToolsForWorkflow : ( ... args : unknown [ ] ) => mockSyncMcpToolsForWorkflow ( ... args ) ,
72103} ) )
73104
74105vi . mock ( '@/lib/audit/log' , ( ) => ( {
@@ -77,7 +108,12 @@ vi.mock('@/lib/audit/log', () => ({
77108 recordAudit : vi . fn ( ) ,
78109} ) )
79110
80- import { POST , DELETE } from '@/app/api/workflows/[id]/deploy/route'
111+ vi . mock ( '@/ee/access-control/utils/permission-check' , ( ) => ( {
112+ PublicApiNotAllowedError : class PublicApiNotAllowedError extends Error { } ,
113+ validatePublicApiAllowed : ( ...args : unknown [ ] ) => mockValidatePublicApiAllowed ( ...args ) ,
114+ } ) )
115+
116+ import { DELETE , PATCH , POST } from '@/app/api/workflows/[id]/deploy/route'
81117
82118describe ( 'Workflow deploy route' , ( ) => {
83119 beforeEach ( ( ) => {
@@ -87,8 +123,20 @@ describe('Workflow deploy route', () => {
87123 mockDbWhere . mockReturnValue ( { limit : mockDbLimit , orderBy : mockDbOrderBy } )
88124 mockDbOrderBy . mockReturnValue ( { limit : mockDbLimit } )
89125 mockDbLimit . mockResolvedValue ( [ ] )
126+ mockDbUpdate . mockReturnValue ( { set : mockDbSet } )
127+ mockDbSet . mockReturnValue ( { where : mockDbWhere } )
90128 mockCleanupWebhooksForWorkflow . mockResolvedValue ( undefined )
129+ mockCreateSchedulesForDeploy . mockResolvedValue ( { success : true } )
130+ mockLoadWorkflowFromNormalizedTables . mockResolvedValue ( {
131+ blocks : { 'block-1' : { id : 'block-1' , type : 'start_trigger' , name : 'Start' } } ,
132+ edges : [ ] ,
133+ loops : { } ,
134+ parallels : { } ,
135+ } )
136+ mockSaveTriggerWebhooksForDeploy . mockResolvedValue ( { success : true , warnings : [ ] } )
91137 mockRemoveMcpToolsForWorkflow . mockResolvedValue ( undefined )
138+ mockSyncMcpToolsForWorkflow . mockResolvedValue ( undefined )
139+ mockValidatePublicApiAllowed . mockResolvedValue ( undefined )
92140 } )
93141
94142 it ( 'allows API-key auth for deploy using hybrid auth userId' , async ( ) => {
@@ -136,4 +184,21 @@ describe('Workflow deploy route', () => {
136184 expect ( data . isDeployed ) . toBe ( false )
137185 expect ( mockUndeployWorkflow ) . toHaveBeenCalledWith ( { workflowId : 'wf-1' } )
138186 } )
187+
188+ it ( 'checks public API restrictions against hybrid auth userId' , async ( ) => {
189+ mockValidateWorkflowAccess . mockResolvedValue ( {
190+ workflow : { id : 'wf-1' , name : 'Test Workflow' , workspaceId : 'ws-1' } ,
191+ auth : { success : true , userId : 'api-user' , authType : 'api_key' } ,
192+ } )
193+
194+ const req = new NextRequest ( 'http://localhost:3000/api/workflows/wf-1/deploy' , {
195+ method : 'PATCH' ,
196+ headers : { 'content-type' : 'application/json' , 'x-api-key' : 'test-key' } ,
197+ body : JSON . stringify ( { isPublicApi : true } ) ,
198+ } )
199+ const response = await PATCH ( req , { params : Promise . resolve ( { id : 'wf-1' } ) } )
200+
201+ expect ( response . status ) . toBe ( 200 )
202+ expect ( mockValidatePublicApiAllowed ) . toHaveBeenCalledWith ( 'api-user' )
203+ } )
139204} )
0 commit comments