@@ -1308,6 +1308,73 @@ describe('setupWorkspaceFileDocHandlers', () => {
13081308 }
13091309 )
13101310
1311+ it . each ( [ 'revoked' , 'expired' , 'unchanged' ] as const ) (
1312+ 'checks %s access after an asynchronous content-room join' ,
1313+ async ( access ) => {
1314+ mockFetchFileDocSeed . mockResolvedValue ( seedResult ( '# Private' , 'doc-private' ) )
1315+ const { io } = createIo ( )
1316+ const memberships = new Set < string > ( )
1317+ let finishSubscription ! : ( ) => void
1318+ const subscription = new Promise < void > ( ( resolve ) => {
1319+ finishSubscription = resolve
1320+ } )
1321+ const pending = setup ( 'socket-content-subscription-access' , io , {
1322+ join : vi . fn ( ( name : string ) => {
1323+ if ( name === ROOM_NAME )
1324+ return subscription . then ( ( ) => {
1325+ memberships . add ( name )
1326+ } )
1327+ memberships . add ( name )
1328+ } ) ,
1329+ leave : vi . fn ( ( name : string ) => memberships . delete ( name ) ) ,
1330+ } )
1331+ const clock = vi . spyOn ( Date , 'now' )
1332+ const joining = pending . handlers [ FILE_DOC_EVENTS . JOIN ] ( { fileId : 'file-1' , clientId : 1 } )
1333+ try {
1334+ await vi . waitFor ( ( ) => expect ( pending . socket . join ) . toHaveBeenCalledWith ( ROOM_NAME ) )
1335+ expect ( joinSuccessFileId ( pending . socket ) ) . toBeUndefined ( )
1336+ if ( access === 'revoked' ) {
1337+ commitRoomPermission (
1338+ 'user-1' ,
1339+ { type : ROOM_TYPES . WORKSPACE_FILE_DOC , id : 'file-1' } ,
1340+ 'read' ,
1341+ beginRoomPermissionRead ( )
1342+ )
1343+ } else if ( access === 'expired' ) {
1344+ clock . mockReturnValue ( Date . now ( ) + ROLE_REVALIDATION_TTL_MS + 1 )
1345+ }
1346+ finishSubscription ( )
1347+ await joining
1348+ expect ( memberships . has ( fileDocAdmissionRoom ( 'file-1' ) ) ) . toBe ( false )
1349+ expect ( memberships . has ( ROOM_NAME ) ) . toBe ( access === 'unchanged' )
1350+ if ( access === 'unchanged' ) {
1351+ expect ( joinSuccessFileId ( pending . socket ) ) . toBe ( 'file-1' )
1352+ } else {
1353+ expect ( joinSuccessFileId ( pending . socket ) ) . toBeUndefined ( )
1354+ expect ( pending . socket . emit ) . toHaveBeenCalledWith (
1355+ FILE_DOC_EVENTS . JOIN_ERROR ,
1356+ expect . objectContaining ( {
1357+ code : access === 'revoked' ? 'ACCESS_DENIED' : 'JOIN_FAILED' ,
1358+ retryable : access === 'expired' ,
1359+ } )
1360+ )
1361+ expect ( pending . socket . emit ) . not . toHaveBeenCalledWith (
1362+ FILE_DOC_EVENTS . MESSAGE ,
1363+ expect . anything ( )
1364+ )
1365+ expect ( pending . socket . emit ) . not . toHaveBeenCalledWith (
1366+ FILE_DOC_EVENTS . PRESENCE ,
1367+ expect . anything ( )
1368+ )
1369+ }
1370+ } finally {
1371+ clock . mockRestore ( )
1372+ finishSubscription ( )
1373+ await joining
1374+ }
1375+ }
1376+ )
1377+
13111378 it ( 'keeps a shared provisional subscription until the other provider finishes joining' , async ( ) => {
13121379 mockFetchFileDocSeed . mockResolvedValue ( seedResult ( '# Shared' , 'doc-shared' ) )
13131380 const { io } = createIo ( )
0 commit comments