@@ -16,19 +16,22 @@ import * as syncProtocol from 'y-protocols/sync'
1616import * as Y from 'yjs'
1717import type { IRoomManager } from '@/rooms'
1818
19- const { mockAuthorizeRoom , mockFetchFileDocSeed , mockFetchFileDocMerge , mockFetchFileDocPersist } =
20- vi . hoisted ( ( ) => ( {
21- mockAuthorizeRoom : vi . fn ( ) ,
22- mockFetchFileDocSeed : vi . fn ( ) ,
23- mockFetchFileDocMerge : vi . fn ( ) ,
24- mockFetchFileDocPersist : vi . fn ( ) ,
25- } ) )
26-
27- vi . mock ( '@sim/platform-authz/rooms' , ( ) => ( {
28- authorizeRoom : mockAuthorizeRoom ,
19+ const {
20+ mockFetchFileDocAccess ,
21+ mockFetchFileDocSeed ,
22+ mockFetchFileDocMerge ,
23+ mockFetchFileDocPersist ,
24+ } = vi . hoisted ( ( ) => ( {
25+ mockFetchFileDocAccess : vi . fn ( ) ,
26+ mockFetchFileDocSeed : vi . fn ( ) ,
27+ mockFetchFileDocMerge : vi . fn ( ) ,
28+ mockFetchFileDocPersist : vi . fn ( ) ,
2929} ) )
3030
31+ vi . mock ( '@sim/platform-authz/rooms' , ( ) => ( { authorizeRoom : vi . fn ( ) } ) )
32+
3133vi . mock ( '@/handlers/file-doc-app' , ( ) => ( {
34+ fetchFileDocAccess : mockFetchFileDocAccess ,
3235 fetchFileDocSeed : mockFetchFileDocSeed ,
3336 fetchFileDocMerge : mockFetchFileDocMerge ,
3437 fetchFileDocPersist : mockFetchFileDocPersist ,
@@ -213,7 +216,7 @@ function joinSuccessFileId(socket: { emit: ReturnType<typeof vi.fn> }) {
213216
214217describe ( 'setupWorkspaceFileDocHandlers' , ( ) => {
215218 beforeEach ( ( ) => {
216- mockAuthorizeRoom . mockResolvedValue ( {
219+ mockFetchFileDocAccess . mockResolvedValue ( {
217220 allowed : true ,
218221 status : 200 ,
219222 workspaceId : 'ws-1' ,
@@ -251,7 +254,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
251254 } )
252255
253256 it ( 'fails closed when authorization does not resolve a workspace context' , async ( ) => {
254- mockAuthorizeRoom . mockResolvedValueOnce ( {
257+ mockFetchFileDocAccess . mockResolvedValueOnce ( {
255258 allowed : true ,
256259 status : 200 ,
257260 workspacePermission : 'write' ,
@@ -445,7 +448,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
445448 const { io } = createIo ( )
446449 const { socket, handlers } = setup ( 'socket-race' , io , { userId : 'user-race' } )
447450
448- mockAuthorizeRoom . mockImplementation ( async ( ) => {
451+ mockFetchFileDocAccess . mockImplementation ( async ( ) => {
449452 // Simulate the revocation landing between this join's authorize and its commit,
450453 // exactly as the sweep would record it.
451454 commitRoomPermission (
@@ -479,16 +482,21 @@ describe('setupWorkspaceFileDocHandlers', () => {
479482 const { io } = createIo ( )
480483 const { socket, handlers } = setup ( 'socket-stale' , io , { userId : 'user-stale' } )
481484
482- mockAuthorizeRoom . mockImplementation ( async ( { action } : { action : string } ) => {
483- // The authoritative current answer: access is gone.
484- if ( action !== 'write' )
485- return { allowed : false , status : 403 , workspaceId : 'ws-1' , workspacePermission : null }
485+ mockFetchFileDocAccess
486486 // This join's own authorize saw the pre-revocation state, and the sweep records
487487 // the revocation (later read ticket) while it is still in flight.
488- commitRoomPermission ( 'user-stale' , room , null , beginRoomPermissionRead ( ) )
489- await sleep ( 31_000 )
490- return { allowed : true , status : 200 , workspaceId : 'ws-1' , workspacePermission : 'write' }
491- } )
488+ . mockImplementationOnce ( async ( ) => {
489+ commitRoomPermission ( 'user-stale' , room , null , beginRoomPermissionRead ( ) )
490+ await sleep ( 31_000 )
491+ return { allowed : true , status : 200 , workspaceId : 'ws-1' , workspacePermission : 'write' }
492+ } )
493+ // The authoritative current answer: access is gone.
494+ . mockResolvedValue ( {
495+ allowed : false ,
496+ status : 403 ,
497+ workspaceId : 'ws-1' ,
498+ workspacePermission : null ,
499+ } )
492500
493501 const joining = handlers [ FILE_DOC_EVENTS . JOIN ] ( { fileId : 'file-stale' , clientId : 1 } )
494502 await vi . advanceTimersByTimeAsync ( 31_000 )
@@ -597,7 +605,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
597605 await vi . advanceTimersByTimeAsync ( 0 )
598606
599607 // Access is downgraded to read-only, and the cached join-time decision expires.
600- mockAuthorizeRoom . mockResolvedValue ( {
608+ mockFetchFileDocAccess . mockResolvedValue ( {
601609 allowed : false ,
602610 status : 403 ,
603611 workspaceId : 'ws-1' ,
@@ -663,7 +671,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
663671 . filter ( ( [ event ] ) => event === FILE_DOC_EVENTS . PERMISSION )
664672 . map ( ( [ , payload ] ) => ( payload as { canWrite : boolean } ) . canWrite )
665673 const changeRole = async ( workspacePermission : 'read' | 'write' ) => {
666- mockAuthorizeRoom . mockResolvedValue ( {
674+ mockFetchFileDocAccess . mockResolvedValue ( {
667675 allowed : true ,
668676 status : 200 ,
669677 workspaceId : 'ws-1' ,
@@ -1144,7 +1152,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
11441152 it ( 'aborts a join superseded by a newer join during authorization (no cross-binding)' , async ( ) => {
11451153 const { io } = createIo ( )
11461154 let resolveFirst : ( v : unknown ) => void = ( ) => { }
1147- mockAuthorizeRoom
1155+ mockFetchFileDocAccess
11481156 . mockReturnValueOnce ( new Promise ( ( resolve ) => ( resolveFirst = resolve ) ) )
11491157 . mockResolvedValueOnce ( {
11501158 allowed : true ,
@@ -1172,7 +1180,7 @@ describe('setupWorkspaceFileDocHandlers', () => {
11721180 it ( 'cancels an in-flight join when the client leaves that same file (no ghost owner)' , async ( ) => {
11731181 const { io, sent } = createIo ( )
11741182 let resolveAuth : ( v : unknown ) => void = ( ) => { }
1175- mockAuthorizeRoom . mockReturnValueOnce ( new Promise ( ( resolve ) => ( resolveAuth = resolve ) ) )
1183+ mockFetchFileDocAccess . mockReturnValueOnce ( new Promise ( ( resolve ) => ( resolveAuth = resolve ) ) )
11761184 const s = setup ( 'socket-a' , io )
11771185
11781186 // Join file-1 is awaiting authorization when the client leaves file-1 (fast open→close).
0 commit comments