import { strict as assert } from "assert" ; import { ContainerRuntimeFactoryWithDefaultDataStore , DataObjectFactory , } from "@fluidframework/aqueduct" ; import { TelemetryNullLogger } from "@fluidframework/common-utils" ; import { IContainer , IRuntimeFactory , LoaderHeader } from "@fluidframework/container-definitions" ; import { IRequest } from "@fluidframework/core-interfaces" ; import { ISummaryConfiguration } from "@fluidframework/protocol-definitions" ; import { requestFluidObject } from "@fluidframework/runtime-utils" ; import { ITestObjectProvider } from "@fluidframework/test-utils" ; import { describeFullCompat } from "@fluidframework/test-version-utils" ; import { IAckedSummary , IContainerRuntimeOptions , SummaryCollection } from "@fluidframework/container-runtime" ; import { IContainerRuntimeBase } from "@fluidframework/runtime-definitions" ; import { TestDataObject } from "./mockSummarizerClient" ; /**
 * Validates this scenario: When a data store is shared with an external app, if the data store becomes unreferenced
 * by the time it is requested via this external app, we return a failure (404).
 * Basically, for data stores that are unreferenced in the base snapshot that a container loads from, we return a
 * failure (404) when they are requested with "externalRequest" flag in the request header.
 */ describeFullCompat ( "GC Data Store Requests" , ( getTestObjectProvider ) => { let provider ; const dataObjectFactory = new DataObjectFactory ( "TestDataObject" , TestDataObject , [ ] , [ ] ) ; const IdleDetectionTime = 100 ; const summaryConfigOverrides = { idleTime : IdleDetectionTime , maxTime : IdleDetectionTime * 12 } ; const runtimeOptions = { summaryOptions : { initialSummarizerDelayMs : 10 , summaryConfigOverrides } , gcOptions : { gcAllowed : true } } ; const innerRequestHandler = async ( request , runtime ) => runtime . IFluidHandleContext . resolveHandle ( request ) ; const runtimeFactory = new ContainerRuntimeFactoryWithDefaultDataStore ( dataObjectFactory , [ [ dataObjectFactory . type , Promise . resolve ( dataObjectFactory ) ] , ] , undefined , [ innerRequestHandler ] , runtimeOptions ) ; let mainContainer ; let mainDataStore ; let summaryCollection ; returns async function waitForSummary ( ) { await provider . ensureSynchronized ( ) ; const ackedSummary = await summaryCollection . waitSummaryAck ( mainContainer . deltaManager . lastSequenceNumber ) ; return ackedSummary . summaryAck . contents . handle ; } const createContainer = async ( ) : Promise < IContainer > => provider . createContainer ( runtimeFactory ) ; const loadContainer = async ( summaryVersion , factory = runtimeFactory ) : Promise < IContainer > => { const requestHeader = { [ LoaderHeader . version ] : summaryVersion } ; return provider . loadContainer ( factory , undefined , requestHeader ) ; } ; beforeEach ( async ( ) => { provider = getTestObjectProvider ( ) ; mainContainer = await createContainer ( ) ; mainDataStore = await requestFluidObject < TestDataObject > ( mainContainer , "default" ) ; mainDataStore . _root . set ( "test" , "value" ) ; await provider . ensureSynchronized ( ) ; summaryCollection = new SummaryCollection ( mainContainer . deltaManager , new TelemetryNullLogger ( ) ) ; } ) ; it ( "should fail requests with externalRequest flag for unreferenced data stores" , async ( ) => { const directoryKey = "dataStore2" ; const dataStore2 = await dataObjectFactory . createInstance ( mainDataStore . containerRuntime ) ; mainDataStore . _root . set ( directoryKey , dataStore2 . handle ) ; await waitForSummary ( ) ; mainDataStore . _root . delete ( directoryKey ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const request = { url : dataStore2 . id } ; let response = await container2 . request ( request ) ; assert ( response . status === 200 && response . mimeType === "fluid/object" , "dataStore2 should have successfully loaded" ) ; request . headers = { externalRequest : true } ; response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should have failed to load" ) ; } ) ; it ( "should succeed requests with externalRequest flag for data stores that are re-referenced" , async ( ) => { const directoryKey = "dataStore2" ; const dataStore2 = await dataObjectFactory . createInstance ( mainDataStore . containerRuntime ) ; mainDataStore . _root . set ( directoryKey , dataStore2 . handle ) ; await waitForSummary ( ) ; mainDataStore . _root . delete ( directoryKey ) ; let summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const request = { url : dataStore2 . id , headers : { externalRequest : true } } ; let response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should have failed to load" ) ; mainDataStore . _root . set ( directoryKey , dataStore2 . handle ) ; summaryVersion = await waitForSummary ( ) ; const container3 = await loadContainer ( summaryVersion ) ; response = await container3 . request ( request ) ; assert ( response . status === 200 , "dataStore2 should successfully load now" ) ; } ) ; it ( "should succced requests with externalRequest flag for unreferenced data stores with GC disabled" , async ( ) => { const directoryKey = "dataStore2" ; const dataStore2 = await dataObjectFactory . createInstance ( mainDataStore . containerRuntime ) ; mainDataStore . _root . set ( directoryKey , dataStore2 . handle ) ; await waitForSummary ( ) ; mainDataStore . _root . delete ( directoryKey ) ; const summaryVersion = await waitForSummary ( ) ; const gcDisabledRuntimeOptions = { summaryOptions : { initialSummarizerDelayMs : 10 , summaryConfigOverrides } , gcOptions : { disableGC : true } } ; const gcDisabledRuntimeFactory = new ContainerRuntimeFactoryWithDefaultDataStore ( dataObjectFactory , [ [ dataObjectFactory . type , Promise . resolve ( dataObjectFactory ) ] , ] , undefined , [ innerRequestHandler ] , gcDisabledRuntimeOptions ) ; const container2 = await loadContainer ( summaryVersion , gcDisabledRuntimeFactory ) ; const request = { url : dataStore2 . id , headers : { externalRequest : true } } ; const response = await container2 . request ( request ) ; assert ( response . status === 200 && response . mimeType === "fluid/object" , "dataStore2 should have successfully loaded" ) ; } ) ; } ) ;