import { strict as assert } from "assert" ; import { Deferred } from "@fluidframework/common-utils" ; import { ConnectionMode , IClient , IConnected , IDocumentMessage , ISequencedDocumentMessage , ISequencedDocumentSystemMessage , ITokenClaims , IUser , MessageType , ScopeType , } from "@fluidframework/protocol-definitions" ; import { IWebSocket } from "@fluidframework/server-services-core" ; import { KJUR as jsrsasign } from "jsrsasign" ; import { ILocalDeltaConnectionServer , LocalDeltaConnectionServer } from "../localDeltaConnectionServer" ; describe ( "LocalDeltaConnectionServer" , ( ) => { let deltaConnectionServer ; function connectNewClient ( mode , userId ) { const user = { id : userId } ; const client = { details : { capabilities : { interactive : true } } , mode , permission : [ ] , scopes : [ ] , user } ; const now = Math . round ( ( new Date ( ) ) . getTime ( ) / 1000 ) ; const claims = { documentId : "document" , scopes : [ ScopeType . DocRead , ScopeType . DocWrite , ScopeType . SummaryWrite ] , tenantId : "tenant" , user , iat : now , exp : now + 60 * 60 , ver : "1.0" } ; const utf8Key = { utf8 : "key" } ; const token = jsrsasign . jws . JWS . sign ( null , JSON . stringify ( { alg : "HS256" , typ : "JWT" } ) , claims , utf8Key ) ; return deltaConnectionServer . connectWebSocket ( "tenant" , "document" , token , client , [ "^0.4.0" ] ) ; } async function addJoinHandler ( socket ) { const joinP = new Deferred < any > ( ) ; const joinHandler = ( msgs ) => { for ( const msg of msgs ) { if ( joinP . isCompleted === false ) { if ( msg . type !== MessageType . ClientJoin ) { joinP . reject ( `expected join msg:\n${ JSON . stringify ( msg , undefined , 1 ) }` ) ; } else { joinP . resolve ( JSON . parse ( msg . data ) ) ; } } } } ; socket . on ( "op" , ( id , msgs ) => joinHandler ( msgs ) ) ; return joinP . promise ; } async function addMessagehandler ( socket ) { const messageP = new Deferred < any > ( ) ; const messageHandler = ( msgs ) => { for ( const msg of msgs ) { if ( messageP . isCompleted === false ) { if ( msg . type === MessageType . Operation ) { messageP . resolve ( msg . contents ) ; } } } } ; socket . on ( "op" , ( id , msgs ) => messageHandler ( msgs ) ) ; return messageP . promise ; } beforeEach ( ( ) => { deltaConnectionServer = LocalDeltaConnectionServer . create ( ) ; } ) ; afterEach ( async ( ) => { await deltaConnectionServer . close ( ) ; } ) ; it ( "can connect to web socket and join client" , async ( ) => { const [ socket1 , connected1P ] = connectNewClient ( "write" , "userId1" ) ; const join1P = addJoinHandler ( socket1 ) ; const connected1 = await connected1P ; assert . equal ( connected1 . existing , true , "The document should be existing for the first client" ) ; const join1 = await join1P ; assert . equal ( connected1 . clientId , join1 . clientId , "The clientId in the join message should be same as the one in connected message" ) ; const [ socket2 , connected2P ] = connectNewClient ( "write" , "userId2" ) ; const join2P = addJoinHandler ( socket2 ) ; const connected2 = await connected2P ; assert . equal ( connected2 . existing , true , "The document should be existing for the second client" ) ; const join2 = await join2P ; assert . equal ( connected2 . clientId , join2 . clientId , "The clientId in the join message should be same as the one in connected message" ) ; assert . notEqual ( connected2 . clientId , connected1 . clientId , "The clientIds for the two clients should be different" ) ; socket1 . disconnect ( ) ; socket2 . disconnect ( ) ; } ) ; it ( "can send and receive ops on client in write mode" , async ( ) => { const [ socket1 , connected1P ] = connectNewClient ( "write" , "userId1" ) ; const join1P = addJoinHandler ( socket1 ) ; const connected1 = await connected1P ; assert . equal ( connected1 . mode , "write" , "The first client should be connected in write mode" ) ; await join1P ; const [ socket2 , connected2P ] = connectNewClient ( "write" , "userId2" ) ; const join2P = addJoinHandler ( socket2 ) ; const connected2 = await connected2P ; assert . equal ( connected2 . mode , "write" , "The second client should be connected in write mode" ) ; await join2P ; const content = "writeModeClient" ; const message = { clientSequenceNumber : 1 , contents : content , metadata : undefined , referenceSequenceNumber : 0 , traces : [ ] , type : MessageType . Operation } ; socket2 . emit ( "submitOp" , connected2 . clientId , [ message ] ) ; const message1P = addMessagehandler ( socket1 ) ; const message2P = addMessagehandler ( socket2 ) ; const content1 = await message1P ; assert . equal ( content1 , content , "The content received on first client is not as expected" ) ; const content2 = await message2P ; assert . equal ( content2 , content , "The content received on second client is not as expected" ) ; socket1 . disconnect ( ) ; socket2 . disconnect ( ) ; } ) ; it ( "can receive ops on client in read mode" , async ( ) => { const [ socket1 , connected1P ] = connectNewClient ( "write" , "userId1" ) ; const join1P = addJoinHandler ( socket1 ) ; const connected1 = await connected1P ; assert . equal ( connected1 . mode , "write" , "The first client should be connected in write mode" ) ; await join1P ; const [ socket2 , connected2P ] = connectNewClient ( "read" , "userId2" ) ; const connected2 = await connected2P ; assert . equal ( connected2 . mode , "read" , "The second client should be connected in read mode" ) ; const content = "readModeClient" ; const message = { clientSequenceNumber : 1 , contents : content , metadata : undefined , referenceSequenceNumber : 0 , traces : [ ] , type : MessageType . Operation } ; socket1 . emit ( "submitOp" , connected1 . clientId , [ message ] ) ; const message1P = addMessagehandler ( socket1 ) ; const message2P = addMessagehandler ( socket2 ) ; const content1 = await message1P ; assert . equal ( content1 , content , "The content received on first client is not as expected" ) ; const content2 = await message2P ; assert . equal ( content2 , content , "The content received on second client is not as expected" ) ; socket1 . disconnect ( ) ; socket2 . disconnect ( ) ; } ) ; } ) ;