import { strict as assert } from "assert" ; import { ContainerRuntimeFactoryWithDefaultDataStore , DataObjectFactory } from "@fluidframework/aqueduct" ; import { TelemetryNullLogger } from "@fluidframework/common-utils" ; import { IContainer } from "@fluidframework/container-definitions" ; import { ContainerRuntime , IAckedSummary , IContainerRuntimeOptions , SummaryCollection , } from "@fluidframework/container-runtime" ; import { ISummaryContext } from "@fluidframework/driver-definitions" ; import { SharedMap } from "@fluidframework/map" ; import { ISummaryTree } from "@fluidframework/protocol-definitions" ; import { requestFluidObject } from "@fluidframework/runtime-utils" ; import { ITestObjectProvider } from "@fluidframework/test-utils" ; import { describeFullCompat } from "@fluidframework/test-version-utils" ; import { IRequest } from "@fluidframework/core-interfaces" ; import { IContainerRuntimeBase } from "@fluidframework/runtime-definitions" ; import { wrapDocumentServiceFactory } from "./gcDriverWrappers" ; import { loadSummarizer , TestDataObject , submitAndAckSummary , getGCStateFromSummary } from "./mockSummarizerClient" ; /**
 * Validates that the unreferenced timestamp is correctly set in the summary tree of unreferenced data stores. Also,
 * the timestamp is removed when an unreferenced data store becomes referenced again.
 */ describeFullCompat ( "GC unreferenced timestamp" , ( getTestObjectProvider ) => { let provider ; const dataObjectFactory = new DataObjectFactory ( "TestDataObject" , TestDataObject , [ ] , [ ] ) ; const runtimeOptions = { summaryOptions : { disableSummaries : true } , gcOptions : { gcAllowed : true , writeDataAtRoot : true } } ; const innerRequestHandler = async ( request , runtime ) => runtime . IFluidHandleContext . resolveHandle ( request ) ; const runtimeFactory = new ContainerRuntimeFactoryWithDefaultDataStore ( dataObjectFactory , [ [ dataObjectFactory . type , Promise . resolve ( dataObjectFactory ) ] , ] , undefined , [ innerRequestHandler ] , runtimeOptions ) ; const logger = new TelemetryNullLogger ( ) ; let latestUploadedSummary ; let latestSummaryContext ; let latestAckedSummary ; let mainContainer ; let dataStoreA ; const createContainer = async ( ) : Promise < IContainer > => { return provider . createContainer ( runtimeFactory ) ; } ; const getNewSummarizer = async ( summaryVersion ? ) => { return loadSummarizer ( provider , runtimeFactory , mainContainer . deltaManager . lastSequenceNumber , summaryVersion ) ; } ; /**
     * Callback that will be called by the document storage service whenever a summary is uploaded by the client.
     * Update the summary context to include the summary proposal and ack handle as per the latest ack for the
     * document.
     */ function uploadSummaryCb ( summaryTree , context ) { latestUploadedSummary = summaryTree ; latestSummaryContext = context ; const newSummaryContext = { ... context } ; if ( latestAckedSummary !== undefined ) { newSummaryContext . ackHandle = latestAckedSummary . summaryAck . contents . handle ; newSummaryContext . proposalHandle = latestAckedSummary . summaryOp . contents . handle ; } return newSummaryContext ; } returns async function getUnreferencedTimestamps ( summarizerClient ) { const summaryResult = await submitAndAckSummary ( provider , summarizerClient , logger , true ) ; latestAckedSummary = summaryResult . ackedSummary ; assert ( latestSummaryContext && latestSummaryContext . referenceSequenceNumber >= summaryResult . summarySequenceNumber , `Did not get expected summary. Expected: ${ summaryResult . summarySequenceNumber }. ` + `Actual: ${ latestSummaryContext ?. referenceSequenceNumber }.` ) ; assert ( latestUploadedSummary !== undefined , "Did not get a summary" ) ; const gcState = getGCStateFromSummary ( latestUploadedSummary ) ; const nodeTimestamps = new Map ( ) ; for ( const [ nodeId , nodeData ] of Object . entries ( gcState . gcNodes ) ) { nodeTimestamps . set ( nodeId . slice ( 1 ) , nodeData . unreferencedTimestampMs ) ; } return nodeTimestamps ; } before ( function ( ) { provider = getTestObjectProvider ( ) ; if ( provider . driver . type !== "local" ) { this . skip ( ) ; } } ) ; beforeEach ( async ( ) => { ( provider as any ) . _documentServiceFactory = wrapDocumentServiceFactory ( provider . documentServiceFactory , uploadSummaryCb ) ; mainContainer = await createContainer ( ) ; dataStoreA = await requestFluidObject < TestDataObject > ( mainContainer , "default" ) ; await provider . ensureSynchronized ( ) ; } ) ; afterEach ( ( ) => { latestAckedSummary = undefined ; latestSummaryContext = undefined ; latestUploadedSummary = undefined ; } ) ; describe ( "unreferenced timestamp in in summary" , ( ) => { it ( "adds / removes unreferenced timestamp from data stores correctly" , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTimestamp1 = timestamps1 . get ( dataStoreB . id ) ; assert ( dsBTimestamp1 === undefined , `new data store should not have unreferenced timestamp` ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTimestamp2 = timestamps2 . get ( dataStoreB . id ) ; assert ( dsBTimestamp2 !== undefined , `data store should have unreferenced timestamp` ) ; dataStoreA . _root . set ( "key" , "value" ) ; const timestamps3 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTimestamp3 = timestamps3 . get ( dataStoreB . id ) ; assert ( dsBTimestamp3 !== undefined , `data store should still have unreferenced timestamp` ) ; assert . strictEqual ( dsBTimestamp2 , dsBTimestamp3 , "unreferenced timestamp should not have changed" ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; const timestamps4 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTimestamp4 = timestamps4 . get ( dataStoreB . id ) ; assert ( dsBTimestamp4 === undefined , `data store should not have unreferenced timestamp anymore` ) ; } ) ; it ( "uses unreferenced timestamp from previous summary correctly" , async ( ) => { const summarizerClient1 = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient1 ) ; const dsBTimestamp1 = timestamps1 . get ( dataStoreB . id ) ; assert ( dsBTimestamp1 === undefined , `new data store should not have unreferenced timestamp` ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient1 ) ; const dsBTimestamp2 = timestamps2 . get ( dataStoreB . id ) ; assert ( dsBTimestamp2 !== undefined , `new data store should have unreferenced timestamp` ) ; assert ( latestAckedSummary !== undefined , "Summary ack isn't available as expected" ) ; const summarizerClient2 = await getNewSummarizer ( latestAckedSummary . summaryAck . contents . handle ) ; const timestamps3 = await getUnreferencedTimestamps ( summarizerClient2 ) ; const dsBTimestamp3 = timestamps3 . get ( dataStoreB . id ) ; assert ( dsBTimestamp3 !== undefined , `new data store should still have unreferenced timestamp` ) ; assert . strictEqual ( dsBTimestamp2 , dsBTimestamp3 , "The unreferenced timestamp should not have changed" ) ; } ) ; } ) ; /**
     * These tests validate such scenarios where nodes transition from unreferenced -> referenced -> ureferenced state
     * by verifing that their unreferenced timestamps are updated correctly.
     *
     * In these tests, V = nodes and E = edges between nodes. Root nodes that are always referenced are marked as *.
     * The nodes are data stores / DDSs represented by alphabets A, B, C and so on.
     */ describe ( "References between summaries" , ( ) => { /**
         * Validates that we can detect references that were added and then removed.
         * 1. Summary 1 at t1. V = [A*, B]. E = []. B has unreferenced time t1.
         * 2. Op adds reference from A to B. E = [A -> B].
         * 3. Op removes reference from A to B. E = [].
         * 4. Summary 2 at t2. V = [A*, B]. E = []. B has unreferenced time t2.
         * Validates that the unreferenced time for B is t2 which is > t1.
         */ it ( `Scenario 1 - Reference added and then removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime1 = timestamps1 . get ( dataStoreB . id ) ; assert ( dsBTime1 !== undefined , `B should have unreferenced timestamp` ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime2 = timestamps2 . get ( dataStoreB . id ) ; assert ( dsBTime2 !== undefined && dsBTime2 > dsBTime1 , `B's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect references that were added transitively and then removed.
         * 1. Summary 1 at t1. V = [A*, B, C]. E = [B -> C]. B and C have unreferenced time t2.
         * 2. Op adds reference from A to B. E = [A -> B, B -> C].
         * 3. Op removes reference from B to C. E = [A -> B].
         * 4. Op removes reference from A to B. E = [].
         * 5. Summary 2 at t2. V = [A*, B, C]. E = []. B and C have unreferenced time t2.
         * Validates that the unreferenced time for B and C is t2 which is > t1.
         */ it ( `Scenario 2 - Reference transitively added and removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime1 = timestamps1 . get ( dataStoreB . id ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; assert ( dsBTime1 !== undefined , `B should have unreferenced timestamp` ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreB . _root . delete ( "dataStoreC" ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime2 = timestamps2 . get ( dataStoreB . id ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; assert ( dsBTime2 !== undefined && dsBTime2 > dsBTime1 , `B's timestamp should have updated` ) ; assert ( dsCTime2 !== undefined && dsCTime2 > dsCTime1 , `C's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect chain of references in which the first reference was added and then removed.
         * 1. Summary 1 at t1. V = [A*, B, C, D]. E = [B -> C, C -> D]. B, C and D have unreferenced time t2.
         * 2. Op adds reference from A to B. E = [A -> B, B -> C, C -> D].
         * 3. Op removes reference from A to B. E = [B -> C, C -> D].
         * 4. Summary 2 at t2. V = [A*, B, C, D]. E = [B -> C, C -> D]. B, C and D have unreferenced time t2.
         * Validates that the unreferenced time for B, C and D is t2 which is > t1.
         */ it ( `Scenario 3 - Reference added through chain of references and removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; const dataStoreD = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreC . _root . set ( "dataStoreD" , dataStoreD . handle ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime1 = timestamps1 . get ( dataStoreB . id ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; const dsDTime1 = timestamps1 . get ( dataStoreD . id ) ; assert ( dsBTime1 !== undefined , `B should have unreferenced timestamp` ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; assert ( dsDTime1 !== undefined , `D should have unreferenced timestamp` ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime2 = timestamps2 . get ( dataStoreB . id ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; const dsDTime2 = timestamps2 . get ( dataStoreD . id ) ; assert ( dsBTime2 !== undefined && dsBTime2 > dsBTime1 , `B's timestamp should have updated` ) ; assert ( dsCTime2 !== undefined && dsCTime2 > dsCTime1 , `C's timestamp should have updated` ) ; assert ( dsDTime2 !== undefined && dsDTime2 > dsDTime1 , `D's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect references that were added and removed via new data stores.
         * 1. Summary 1 at t1. V = [A*, C]. E = []. C has unreferenced time t1.
         * 2. Data store B is created. E = [].
         * 3. Op adds reference from A to B. E = [A -> B].
         * 4. Op adds reference from B to C. E = [A -> B, B -> C].
         * 5. Op removes reference from B to C. E = [A -> B].
         * 6. Summary 2 at t2. V = [A*, B, C]. E = [A -> B]. C has unreferenced time t2.
         * Validates that the unreferenced time for C is t2 which is > t1.
         */ it ( `Scenario 4 - Reference added and removed via new nodes` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . delete ( "dataStoreC" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreB . _root . delete ( "dataStoreC" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; assert ( dsCTime2 !== undefined && dsCTime2 > dsCTime1 , `C's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect references that were added and removed via new root data stores.
         * 1. Summary 1 at t1. V = [A*, C]. E = []. C has unreferenced time t1.
         * 2. Root data store B is created. E = [].
         * 3. Op adds reference from A to B. E = [A -> B].
         * 4. Op adds reference from B to C. E = [A -> B, B -> C].
         * 5. Op removes reference from B to C. E = [A -> B].
         * 6. Summary 2 at t2. V = [A*, B, C]. E = [A -> B]. C has unreferenced time t2.
         * Validates that the unreferenced time for C is t2 which is > t1.
         *
         * The difference from the previous tests is that the new data stores is a root data store. So, this validates
         * that we can detect new root data stores and outbound references from them.
         */ it ( `Scenario 5 - Reference added via new root nodes and removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . delete ( "dataStoreC" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; const dataStoreB = await dataObjectFactory . createRootInstance ( "dataStoreA" , dataStoreA . containerRuntime ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreB . _root . delete ( "dataStoreC" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; assert ( dsCTime2 !== undefined && dsCTime2 > dsCTime1 , `C's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect references that were added via new data stores before they are referenced
         * themselves, and then the reference from the new data store is removed.
         * 1. Summary 1 at t1. V = [A*, C]. E = []. C has unreferenced time t1.
         * 2. Data store B is created. E = [].
         * 3. Add reference from B to C. E = [].
         * 4. Op adds reference from A to B. E = [A -> B, B -> C].
         * 5. Op removes reference from B to C. E = [A -> B].
         * 6. Summary 2 at t2. V = [A*, B, C]. E = [A -> B]. C has unreferenced time t2.
         * Validates that the unreferenced time for C is t2 which is > t1.
         *
         * The difference from previous test case is that the reference from B to C is added before B is referenced and
         * observed by summarizer. So, the summarizer does not see this reference directly but only when B is realized.
         */ it ( `Scenario 6 - Reference added via new unreferenced nodes and removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . delete ( "dataStoreC" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreB . _root . delete ( "dataStoreC" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; assert ( dsCTime2 !== undefined && dsCTime2 > dsCTime1 , `C's timestamp should have updated` ) ; } ) ; /**
         * Validates that we can detect references that were added transitively via new data stores before they are
         * references themselves, and then the reference from the new data store is removed.
         * 1. Summary 1 at t1. V = [A*, D]. E = []. D has unreferenced time t1.
         * 2. Data stores B and C are created. E = [].
         * 3. Add reference from B to C. E = [].
         * 4. Add reference from C to D. E = [].
         * 5. Op adds reference from A to B. E = [A -> B, B -> C, C -> D].
         * 6. Op removes reference from C to D. E = [A -> B, B -> C].
         * 7. Summary 2 at t2. V = [A*, B, C]. E = [A -> B, B -> C]. D has unreferenced time t2.
         * Validates that the unreferenced time for D is t2 which is > t1.
         *
         * This difference from the previous test case is that there is another level of indirection here that
         * references the node which was unreferenced in previous summary.
         */ it ( `Scenario 7 - Reference added transitively via new nodes and removed` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreD = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreD" , dataStoreD . handle ) ; dataStoreA . _root . delete ( "dataStoreD" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsDTime1 = timestamps1 . get ( dataStoreD . id ) ; assert ( dsDTime1 !== undefined , `D should have unreferenced timestamp` ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreC . _root . set ( "dataStoreD" , dataStoreD . handle ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreC . _root . delete ( "dataStoreD" ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsDTime2 = timestamps2 . get ( dataStoreD . id ) ; assert ( dsDTime2 !== undefined && dsDTime2 > dsDTime1 , `D's timestamp should have updated` ) ; } ) ; /**
         * Validates that references added by unreferences nodes do not show up as references.
         * 1. Summary 1 at t1. V = [A*, B, C]. E = []. B and C have unreferenced time t1.
         * 2. Op adds reference from B to C. E = [B -> C].
         * 3. Summary 2 at t2. V = [A*, B, C]. E = [B -> C]. B and C have unreferenced time t1.
         * Validates that the unreferenced time for B and C is still t1.
         */ it ( `Scenario 8 - Reference added via unreferenced nodes` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const dataStoreB = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; const dataStoreC = await dataObjectFactory . createInstance ( dataStoreA . containerRuntime ) ; dataStoreA . _root . set ( "dataStoreB" , dataStoreB . handle ) ; dataStoreA . _root . set ( "dataStoreC" , dataStoreC . handle ) ; dataStoreA . _root . delete ( "dataStoreB" ) ; dataStoreA . _root . delete ( "dataStoreC" ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime1 = timestamps1 . get ( dataStoreB . id ) ; const dsCTime1 = timestamps1 . get ( dataStoreC . id ) ; assert ( dsBTime1 !== undefined , `B should have unreferenced timestamp` ) ; assert ( dsCTime1 !== undefined , `C should have unreferenced timestamp` ) ; dataStoreB . _root . set ( "dataStoreC" , dataStoreC . handle ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const dsBTime2 = timestamps2 . get ( dataStoreB . id ) ; const dsCTime2 = timestamps2 . get ( dataStoreC . id ) ; assert ( dsBTime2 === dsBTime1 , `B's unreferenced timestamp should be unchanged` ) ; assert ( dsCTime2 === dsCTime1 , `C's unreferenced timestamp should be unchanged` ) ; } ) ; /**
         * Validates that DDSs are referenced even though we don't detect their referenced between summaries. Once we
         * do GC at DDS level, this test will fail - https://github.com/microsoft/FluidFramework/issues/8470.
         * 1. Summary 1 at t1. V = [A*]. E = [].
         * 2. DDS B is created. No reference is added to it.
         * 3. Summary 2 at t2. V = [A*, B]. E = []. B is still referenced.
         */ it ( `Scenario 9 - Reference to DDS not added` , async ( ) => { const summarizerClient = await getNewSummarizer ( ) ; const timestamps1 = await getUnreferencedTimestamps ( summarizerClient ) ; assert ( timestamps1 . get ( dataStoreA . id ) === undefined , "A should be referenced" ) ; const ddsB = SharedMap . create ( dataStoreA . dataStoreRuntime ) ; ddsB . bindToContext ( ) ; const timestamps2 = await getUnreferencedTimestamps ( summarizerClient ) ; const ddsBUrl = `/${ dataStoreA . id }/${ ddsB . id }` ; const ddsBTime1 = timestamps2 . get ( ddsBUrl . slice ( 1 ) ) ; assert ( ddsBTime1 === undefined , `B should not have unreferenced timestamp since we do not have GC at DDS level yet` ) ; } ) ; } ) ; } ) ;