import { strict as assert } from "assert" ; import { IFluidCodeDetails , IRequest } from "@fluidframework/core-interfaces" ; import { IGenericError , IPendingLocalState , ContainerErrorType , LoaderHeader , } from "@fluidframework/container-definitions" ; import { Container , ConnectionState , Loader , ILoaderProps , waitContainerToCatchUp , } from "@fluidframework/container-loader" ; import { IDocumentServiceFactory , IFluidResolvedUrl , } from "@fluidframework/driver-definitions" ; import { MockDocumentDeltaConnection } from "@fluidframework/test-loader-utils" ; import { LocalCodeLoader , TestObjectProvider , LoaderContainerTracker , TestContainerRuntimeFactory , ITestObjectProvider , TestFluidObjectFactory , } from "@fluidframework/test-utils" ; import { ensureFluidResolvedUrl } from "@fluidframework/driver-utils" ; import { requestFluidObject } from "@fluidframework/runtime-utils" ; import { getDataStoreFactory , ITestDataObject , TestDataObjectType , describeNoCompat , } from "@fluidframework/test-version-utils" ; import { IContainerRuntimeBase } from "@fluidframework/runtime-definitions" ; const id = "fluid-test://localhost/containerTest" ; const testRequest = { url : id } ; const codeDetails = { package : "test" } ; describeNoCompat ( "Container" , ( getTestObjectProvider ) => { let provider ; const loaderContainerTracker = new LoaderContainerTracker ( ) ; before ( function ( ) { provider = getTestObjectProvider ( ) ; if ( provider . driver . type !== "local" ) { this . skip ( ) ; } } ) ; before ( async ( ) => { const loader = new Loader ( { logger : provider . logger , urlResolver : provider . urlResolver , documentServiceFactory : provider . documentServiceFactory , codeLoader : new LocalCodeLoader ( [ [ codeDetails , new TestFluidObjectFactory ( [ ] ) ] ] ) } ) ; loaderContainerTracker . add ( loader ) ; const container = await loader . createDetachedContainer ( codeDetails ) ; await container . attach ( provider . driver . createCreateNewRequest ( "containerTest" ) ) ; } ) ; afterEach ( ( ) => { loaderContainerTracker . reset ( ) ; } ) ; async function loadContainer ( props ? ) { const loader = new Loader ( { ... props , logger : provider . logger , urlResolver : props ?. urlResolver ?? provider . urlResolver , documentServiceFactory : props ?. documentServiceFactory ?? provider . documentServiceFactory , codeLoader : props ?. codeLoader ?? new LocalCodeLoader ( [ [ codeDetails , new TestFluidObjectFactory ( [ ] ) ] ] ) } ) ; loaderContainerTracker . add ( loader ) ; const testResolved = await loader . services . urlResolver . resolve ( testRequest ) ; ensureFluidResolvedUrl ( testResolved ) ; return Container . load ( loader , { canReconnect : testRequest . headers ?. [ LoaderHeader . reconnect ] , clientDetailsOverride : testRequest . headers ?. [ LoaderHeader . clientDetails ] , resolvedUrl : testResolved , version : testRequest . headers ?. [ LoaderHeader . version ] ?? undefined , loadMode : testRequest . headers ?. [ LoaderHeader . loadMode ] } ) ; } it ( "Load container successfully" , async ( ) => { const container = await loadContainer ( ) ; assert . strictEqual ( container . id , "containerTest" , "Container's id should be set" ) ; assert . strictEqual ( container . clientDetails . capabilities . interactive , true , "Client details should be set with interactive as true" ) ; } ) ; it ( "Load container unsuccessfully" , async ( ) => { let success = true ; try { const documentServiceFactory = provider . documentServiceFactory ; const mockFactory = Object . create ( documentServiceFactory ) as IDocumentServiceFactory ; mockFactory . createDocumentService = async ( resolvedUrl ) => { const service = await documentServiceFactory . createDocumentService ( resolvedUrl ) ; service . connectToStorage = async ( ) => Promise . reject ( new Error ( "expectedFailure" ) ) ; return service ; } ; await loadContainer ( { documentServiceFactory : mockFactory } ) ; assert . fail ( "Error expected" ) ; } catch ( error ) { assert . strictEqual ( error . errorType , ContainerErrorType . genericError , "Error should be a general error" ) ; const genericError = error as IGenericError ; assert . equal ( genericError . message , "expectedFailure" , "Expected the injected error message" ) ; success = false ; } assert . strictEqual ( success , false ) ; } ) ; it ( "Load container with error" , async ( ) => { let success = true ; try { const documentServiceFactory = provider . documentServiceFactory ; const mockFactory = Object . create ( documentServiceFactory ) as IDocumentServiceFactory ; mockFactory . createDocumentService = async ( resolvedUrl ) => { const service = await documentServiceFactory . createDocumentService ( resolvedUrl ) ; service . connectToDeltaStorage = async ( ) => Promise . reject ( new Error ( "expectedFailure" ) ) ; return service ; } ; const container2 = await loadContainer ( { documentServiceFactory : mockFactory } ) ; await waitContainerToCatchUp ( container2 ) ; assert . fail ( "Error expected" ) ; } catch ( error ) { assert . strictEqual ( error . errorType , ContainerErrorType . genericError , "Error should be a general error" ) ; const genericError = error as IGenericError ; assert . equal ( genericError . message , "expectedFailure" , "Expected the injected error message" ) ; success = false ; } assert . strictEqual ( success , false ) ; } ) ; it ( "Raise disconnected event" , async ( ) => { const deltaConnection = new MockDocumentDeltaConnection ( "test" ) ; const documentServiceFactory = provider . documentServiceFactory ; const mockFactory = Object . create ( documentServiceFactory ) as IDocumentServiceFactory ; mockFactory . createDocumentService = async ( resolvedUrl ) => { const service = await documentServiceFactory . createDocumentService ( resolvedUrl ) ; service . connectToDeltaStream = async ( ) => deltaConnection ; return service ; } ; const container = await loadContainer ( { documentServiceFactory : mockFactory } ) ; assert . strictEqual ( container . connectionState , ConnectionState . Connecting , "Container should be in Connecting state" ) ; deltaConnection . dispose ( ) ; try { assert . strictEqual ( container . connectionState , ConnectionState . Disconnected , "Container should be in Disconnected state" ) ; let disconnectedEventArgs ; container . on ( "disconnected" , ( ... args ) => { disconnectedEventArgs = args ; } ) ; await Promise . resolve ( ) ; assert . deepEqual ( disconnectedEventArgs , [ ] ) ; } finally { deltaConnection . removeAllListeners ( ) ; container . close ( ) ; } } ) ; it ( "Raise connection error event" , async ( ) => { const deltaConnection = new MockDocumentDeltaConnection ( "test" ) ; const documentServiceFactory = provider . documentServiceFactory ; const mockFactory = Object . create ( documentServiceFactory ) as IDocumentServiceFactory ; mockFactory . createDocumentService = async ( resolvedUrl ) => { const service = await documentServiceFactory . createDocumentService ( resolvedUrl ) ; service . connectToDeltaStream = async ( ) => deltaConnection ; return service ; } ; let errorRaised = false ; const container = await loadContainer ( { documentServiceFactory : mockFactory } ) ; container . on ( "error" , ( ) => { errorRaised = true ; } ) ; assert . strictEqual ( container . connectionState , ConnectionState . Connecting , "Container should be in Connecting state" ) ; const err = { message : "Test error" , canRetry : false } ; deltaConnection . emitError ( err ) ; try { assert . strictEqual ( container . connectionState , ConnectionState . Disconnected , "Container should be in Disconnected state" ) ; assert . strictEqual ( container . closed , false , "Container should not be closed" ) ; assert . strictEqual ( errorRaised , false , "Error event should not be raised." ) ; } finally { deltaConnection . removeAllListeners ( ) ; container . close ( ) ; } } ) ; it ( "Close called on container" , async ( ) => { const deltaConnection = new MockDocumentDeltaConnection ( "test" ) ; const documentServiceFactory = provider . documentServiceFactory ; const mockFactory = Object . create ( documentServiceFactory ) as IDocumentServiceFactory ; mockFactory . createDocumentService = async ( resolvedUrl ) => { const service = await documentServiceFactory . createDocumentService ( resolvedUrl ) ; service . connectToDeltaStream = async ( ) => deltaConnection ; return service ; } ; const container = await loadContainer ( { documentServiceFactory : mockFactory } ) ; container . on ( "error" , ( ) => { assert . ok ( false , "Error event should not be raised." ) ; } ) ; assert . strictEqual ( container . connectionState , ConnectionState . Connecting , "Container should be in Connecting state" ) ; container . close ( ) ; assert . strictEqual ( container . connectionState , ConnectionState . Disconnected , "Container should be in Disconnected state" ) ; assert . strictEqual ( container . closed , true , "Container should be closed" ) ; deltaConnection . removeAllListeners ( ) ; } ) ; it ( "Delta manager receives readonly event when calling container.forceReadonly()" , async ( ) => { const innerRequestHandler = async ( request , runtime ) => runtime . IFluidHandleContext . resolveHandle ( request ) ; const runtimeFactory = ( _ ? ) => new TestContainerRuntimeFactory ( TestDataObjectType , getDataStoreFactory ( ) , { } , [ innerRequestHandler ] ) ; const localTestObjectProvider = new TestObjectProvider ( Loader , provider . driver , runtimeFactory ) ; const container = await localTestObjectProvider . makeTestContainer ( ) as Container ; const dataObject = await requestFluidObject < ITestDataObject > ( container , "default" ) ; let runCount = 0 ; dataObject . _context . deltaManager . on ( "readonly" , ( ) => { runCount ++ ; } ) ; container . forceReadonly ( true ) ; assert . strictEqual ( container . readOnlyInfo . readonly , true ) ; assert . strictEqual ( runCount , 1 ) ; } ) ; it ( "closeAndGetPendingLocalState() called on container" , async ( ) => { const runtimeFactory = ( _ ? ) => new TestContainerRuntimeFactory ( TestDataObjectType , getDataStoreFactory ( ) ) ; const localTestObjectProvider = new TestObjectProvider ( Loader , provider . driver , runtimeFactory ) ; const container = await localTestObjectProvider . makeTestContainer ( ) as Container ; const pendingLocalState = JSON . parse ( container . closeAndGetPendingLocalState ( ) ) ; assert . strictEqual ( container . closed , true ) ; assert . strictEqual ( pendingLocalState . url , ( container . resolvedUrl as IFluidResolvedUrl ) . url ) ; } ) ; } ) ;