import { strict as assert } from "assert" ; import { ContainerRuntimeFactoryWithDefaultDataStore , DataObject , DataObjectFactory , } from "@fluidframework/aqueduct" ; import { TelemetryNullLogger } from "@fluidframework/common-utils" ; import { IContainer , LoaderHeader } from "@fluidframework/container-definitions" ; import { IAckedSummary , IContainerRuntimeOptions , SummaryCollection } from "@fluidframework/container-runtime" ; import { IFluidHandle , IRequest } from "@fluidframework/core-interfaces" ; import { SharedMatrix } from "@fluidframework/matrix" ; import { Marker , ReferenceType , reservedMarkerIdKey } from "@fluidframework/merge-tree" ; import { ISummaryConfiguration } from "@fluidframework/protocol-definitions" ; import { requestFluidObject } from "@fluidframework/runtime-utils" ; import { SharedString } from "@fluidframework/sequence" ; import { ITestObjectProvider } from "@fluidframework/test-utils" ; import { describeFullCompat } from "@fluidframework/test-version-utils" ; import { UndoRedoStackManager } from "@fluidframework/undo-redo" ; import { IContainerRuntimeBase } from "@fluidframework/runtime-definitions" ; class TestDataObject extends DataObject { public get _root ( ) { return this . root ; } public get _context ( ) { return this . context ; } private readonly matrixKey = "matrix" ; public matrix ! : SharedMatrix ; public undoRedoStackManager ! : UndoRedoStackManager ; private readonly sharedStringKey = "sharedString" ; public sharedString ! : SharedString ; protected async initializingFirstTime ( ) { const sharedMatrix = SharedMatrix . create ( this . runtime ) ; this . root . set ( this . matrixKey , sharedMatrix . handle ) ; const sharedString = SharedString . create ( this . runtime ) ; this . root . set ( this . sharedStringKey , sharedString . handle ) ; } protected async hasInitialized ( ) { const matrixHandle = this . root . get < IFluidHandle < SharedMatrix > > ( this . matrixKey ) ; assert ( matrixHandle !== undefined , "SharedMatrix not found" ) ; this . matrix = await matrixHandle . get ( ) ; this . undoRedoStackManager = new UndoRedoStackManager ( ) ; this . matrix . insertRows ( 0 , 3 ) ; this . matrix . insertCols ( 0 , 3 ) ; this . matrix . openUndo ( this . undoRedoStackManager ) ; const sharedStringHandle = this . root . get < IFluidHandle < SharedString > > ( this . sharedStringKey ) ; assert ( sharedStringHandle !== undefined , "SharedMatrix not found" ) ; this . sharedString = await sharedStringHandle . get ( ) ; } } /**
 * Validates this scenario: When all references to a data store are deleted, the data store is marked as unreferenced
 * in the next summary. When a reference to the data store is re-added, it is marked as referenced in the next summary.
 * Basically, if the handle to a data store is not stored in any DDS, its summary tree will have the "unreferenced"
 * property set to true. If the handle to a data store exists or it's a root data store, its summary tree does not have
 * the "unreferenced" property.
 *
 * The difference between these tests and the ones in the file 'gcReferenceUpdatesInLocalSummary' is that here we submit
 * summaries to the server, load new containers from the summary downloaded from server and validate them.
 */ describeFullCompat ( "GC reference updates in summarizer" , ( getTestObjectProvider ) => { let provider ; const factory = new DataObjectFactory ( "TestDataObject" , TestDataObject , [ SharedMatrix . getFactory ( ) , SharedString . getFactory ( ) ] , [ ] ) ; 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 ( factory , [ [ factory . type , Promise . resolve ( factory ) ] , ] , 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 ) : Promise < IContainer > => { const requestHeader = { [ LoaderHeader . version ] : summaryVersion } ; return provider . loadContainer ( runtimeFactory , 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 ( ) ) ; } ) ; describe ( "SharedMatrix" , ( ) => { it ( "should reflect handle updates immediately in the next summary" , async ( ) => { const dataStore2 = await factory . createInstance ( mainDataStore . _context . containerRuntime ) ; const request = { url : dataStore2 . id , headers : { externalRequest : true } } ; { mainDataStore . matrix . setCell ( 0 , 0 , dataStore2 . handle ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 200 , "dataStore2 should have successfully loaded" ) ; } { mainDataStore . matrix . removeCols ( 0 , 1 ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should have failed to load" ) ; } } ) ; it ( "should reflect undo / redo of handle updates immediately in the next summary" , async ( ) => { const dataStore2 = await factory . createInstance ( mainDataStore . _context . containerRuntime ) ; const request = { url : dataStore2 . id , headers : { externalRequest : true } } ; { mainDataStore . matrix . setCell ( 0 , 0 , dataStore2 . handle ) ; mainDataStore . undoRedoStackManager . closeCurrentOperation ( ) ; await waitForSummary ( ) ; mainDataStore . matrix . removeCols ( 0 , 1 ) ; mainDataStore . undoRedoStackManager . closeCurrentOperation ( ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should have failed to load" ) ; } { mainDataStore . undoRedoStackManager . undoOperation ( ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 200 , "dataStore2 should successfully load now" ) ; } { mainDataStore . undoRedoStackManager . redoOperation ( ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should successfully load now" ) ; } } ) ; } ) ; describe ( "SharedString" , ( ) => { it ( "should reflect handle updates immediately in the next summary" , async ( ) => { const dataStore2 = await factory . createInstance ( mainDataStore . _context . containerRuntime ) ; const request = { url : dataStore2 . id , headers : { externalRequest : true } } ; { mainDataStore . sharedString . insertText ( 0 , "World" ) ; mainDataStore . sharedString . insertMarker ( 0 , ReferenceType . Simple , { [ reservedMarkerIdKey ] : "markerId" , [ "handle" ] : dataStore2 . handle } ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 200 , "dataStore2 should have successfully loaded" ) ; } { const marker = mainDataStore . sharedString . getMarkerFromId ( "markerId" ) as Marker ; mainDataStore . sharedString . annotateMarker ( marker , { [ "handle" ] : "" } ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 404 , "dataStore2 should have failed to load" ) ; } { const marker = mainDataStore . sharedString . getMarkerFromId ( "markerId" ) as Marker ; mainDataStore . sharedString . annotateMarker ( marker , { [ "handle" ] : dataStore2 . handle } ) ; const summaryVersion = await waitForSummary ( ) ; const container2 = await loadContainer ( summaryVersion ) ; const response = await container2 . request ( request ) ; assert ( response . status === 200 , "dataStore2 should have successfully loaded" ) ; } } ) ; } ) ; } ) ;