33 */
44import { authMockFns , createMockRequest } from '@sim/testing'
55import { beforeEach , describe , expect , it , vi } from 'vitest'
6+ import { OrchestrationError } from '@/lib/core/orchestration/types'
67
78const {
8- mockAssertWorkspaceAdminAccess ,
9+ mockAuthorizeWorkspaceOperation ,
910 mockGetForkParent,
1011 mockGetForkChildren,
1112 mockGetUndoableRunForTarget,
1213 mockGetEffectiveWorkspacePermission,
1314} = vi . hoisted ( ( ) => ( {
14- mockAssertWorkspaceAdminAccess : vi . fn ( ) ,
15+ mockAuthorizeWorkspaceOperation : vi . fn ( ) ,
1516 mockGetForkParent : vi . fn ( ) ,
1617 mockGetForkChildren : vi . fn ( ) ,
1718 mockGetUndoableRunForTarget : vi . fn ( ) ,
1819 mockGetEffectiveWorkspacePermission : vi . fn ( ) ,
1920} ) )
2021
2122vi . mock ( '@/ee/workspace-forking/lib/lineage/authz' , ( ) => ( {
22- assertWorkspaceAdminAccess : mockAssertWorkspaceAdminAccess ,
23+ assertForkingEnabled : vi . fn ( ) ,
24+ ForkError : class extends Error { } ,
2325} ) )
2426
2527vi . mock ( '@/ee/workspace-forking/lib/lineage/lineage' , ( ) => ( {
@@ -31,7 +33,17 @@ vi.mock('@/ee/workspace-forking/lib/promote/promote-run-store', () => ({
3133 getUndoableRunForTarget : mockGetUndoableRunForTarget ,
3234} ) )
3335
36+ vi . mock ( '@/lib/core/application/workspace-authorization' , ( ) => ( {
37+ authorizeWorkspaceOperation : mockAuthorizeWorkspaceOperation ,
38+ requireAllowedWorkspacePrincipal : vi . fn ( ) ,
39+ } ) )
40+
3441vi . mock ( '@/lib/workspaces/permissions/utils' , ( ) => ( {
42+ getWorkspaceWithOwner : vi . fn ( async ( id : string ) => ( {
43+ id,
44+ organizationId : null ,
45+ allowPersonalApiKeys : true ,
46+ } ) ) ,
3547 getEffectiveWorkspacePermission : mockGetEffectiveWorkspacePermission ,
3648} ) )
3749
@@ -55,8 +67,8 @@ const childNode = (id: string, name: string) => ({
5567describe ( 'fork lineage route' , ( ) => {
5668 beforeEach ( ( ) => {
5769 vi . clearAllMocks ( )
58- mockGetSession . mockResolvedValue ( { user : { id : VIEWER_ID } } )
59- mockAssertWorkspaceAdminAccess . mockResolvedValue ( { id : WORKSPACE_ID } )
70+ mockGetSession . mockResolvedValue ( { user : { id : VIEWER_ID } , session : { id : 'session-1' } } )
71+ mockAuthorizeWorkspaceOperation . mockResolvedValue ( undefined )
6072 mockGetForkParent . mockResolvedValue ( null )
6173 mockGetForkChildren . mockResolvedValue ( [ ] )
6274 mockGetUndoableRunForTarget . mockResolvedValue ( null )
@@ -69,13 +81,34 @@ describe('fork lineage route', () => {
6981 const res = await GET ( createMockRequest ( 'GET' ) , routeContext )
7082
7183 expect ( res . status ) . toBe ( 401 )
72- expect ( mockAssertWorkspaceAdminAccess ) . not . toHaveBeenCalled ( )
84+ expect ( mockAuthorizeWorkspaceOperation ) . not . toHaveBeenCalled ( )
7385 } )
7486
7587 it ( 'requires admin on the current workspace before loading lineage' , async ( ) => {
7688 await GET ( createMockRequest ( 'GET' ) , routeContext )
7789
78- expect ( mockAssertWorkspaceAdminAccess ) . toHaveBeenCalledWith ( WORKSPACE_ID , VIEWER_ID )
90+ expect ( mockAuthorizeWorkspaceOperation ) . toHaveBeenCalledWith (
91+ expect . objectContaining ( { kind : 'session' , userId : VIEWER_ID } ) ,
92+ expect . objectContaining ( { id : 'workspaces.fork.discover' , minimumRole : 'admin' } ) ,
93+ expect . objectContaining ( { workspaceId : WORKSPACE_ID } ) ,
94+ { }
95+ )
96+ expect ( mockAuthorizeWorkspaceOperation . mock . invocationCallOrder [ 0 ] ) . toBeLessThan (
97+ mockGetForkParent . mock . invocationCallOrder [ 0 ]
98+ )
99+ } )
100+
101+ it ( 'does not read lineage when current workspace authorization is refused' , async ( ) => {
102+ mockAuthorizeWorkspaceOperation . mockRejectedValue (
103+ new OrchestrationError ( 'forbidden' , 'Admin access required' )
104+ )
105+
106+ const response = await GET ( createMockRequest ( 'GET' ) , routeContext )
107+
108+ expect ( response . status ) . toBe ( 403 )
109+ expect ( mockGetForkParent ) . not . toHaveBeenCalled ( )
110+ expect ( mockGetForkChildren ) . not . toHaveBeenCalled ( )
111+ expect ( mockGetUndoableRunForTarget ) . not . toHaveBeenCalled ( )
79112 } )
80113
81114 it ( 'marks accessible and inaccessible nodes via the canonical permission resolver' , async ( ) => {
0 commit comments