import * as fs from 'fs' ; import { resolve , join } from 'path' ; import { assert , expect } from 'chai' ; import { TestObjectProvider } from '@fluidframework/test-utils' ; import { fail } from '../../Common' ; import { SharedTree } from '../../default-edits' ; import { EditId } from '../../Identifiers' ; import { deserialize } from '../../SummaryBackCompatibility' ; import { fullHistorySummarizer , fullHistorySummarizer_0_1_0 , SharedTreeSummarizer , SharedTreeSummaryBase , } from '../../generic' ; import { SharedTreeWithAnchors } from '../../anchored-edits' ; import { createStableEdits , LocalServerSharedTreeTestingComponents , LocalServerSharedTreeTestingOptions , SharedTreeTestingComponents , SharedTreeTestingOptions , } from './TestUtilities' ; import { TestFluidSerializer } from './TestSerializer' ; const pathBase = resolve ( __dirname , '../../../src/test/summary-files/' ) ; function summaryFilePath ( summaryName ) { return join ( pathBase , `${ summaryName }.json` ) ; } /**
 * A version/summarizer pair must be specified for a write test to be generated.
 * Versions that can no longer be written should be removed from this list.
 */ const supportedSummarizers = [ { version : '0.0.2' , summarizer : fullHistorySummarizer } , { version : '0.1.0' , summarizer : fullHistorySummarizer_0_1_0 } , ] ; /**
 * Runs a test suite for summaries on `SharedTree`.
 * This suite can be used to test other implementations that aim to fulfill `SharedTree`'s contract.
 */ export function runSummaryTests < TSharedTree extends SharedTree | SharedTreeWithAnchors > ( title , setUpTestSharedTree , setUpLocalServerTestSharedTree ) { describe ( title , ( ) => { const setupEditId = '9406d301-7449-48a5-b2ea-9be637b0c6e4' as EditId ; const testSerializer = new TestFluidSerializer ( ) ; let expectedTree ; let testObjectProvider ; const testSummaryFiles = fs . readdirSync ( pathBase ) ; const summaryTypes = new Map < string , string [ ] > ( ) ; for ( let fileName of testSummaryFiles ) { const fileNameRegularExpression = /(?<summaryType>[\w+-]*\w+)-(?<version>\d+\.\d\.\d).json/ ; const match = fileNameRegularExpression . exec ( fileName ) ; const matchGroups = match ?. groups ?? fail ( `invalid filename ${ fileName }` ) ; const summaryType = matchGroups . summaryType ; fileName = `${ matchGroups . summaryType }-${ matchGroups . version }` ; let collection = summaryTypes . get ( summaryType ) ; if ( collection === undefined ) { collection = [ ] ; summaryTypes . set ( summaryType , collection ) ; } collection . push ( fileName ) ; } beforeEach ( async ( ) => { const testingComponents = await setUpLocalServerTestSharedTree ( { setupEditId } ) ; expectedTree = testingComponents . tree ; testObjectProvider = testingComponents . testObjectProvider ; } ) ; afterEach ( async ( ) => { testObjectProvider . reset ( ) ; } ) ; const validateSummaryRead = ( fileName ) : void => { const serializedSummary = fs . readFileSync ( summaryFilePath ( fileName ) , 'utf8' ) ; const summary = deserialize ( serializedSummary , testSerializer ) ; const { tree } = setUpTestSharedTree ( ) ; assert . typeOf ( summary , 'object' ) ; tree . loadSummary ( summary as SharedTreeSummaryBase ) ; expect ( tree . equals ( expectedTree ) ) . to . be . true ; } ; const validateSummaryWrite = ( summarizer ) : void => { const newSummary = summarizer ( expectedTree . edits , expectedTree . currentView ) ; const { tree : tree2 } = setUpTestSharedTree ( ) ; tree2 . loadSummary ( newSummary ) ; expect ( tree2 . equals ( expectedTree ) ) . to . be . true ; } ; for ( const [ summaryType , files ] of summaryTypes . entries ( ) ) { it ( `files of type '${ summaryType }' with different format versions produce identical trees` , ( ) => { const serializedSummary = fs . readFileSync ( summaryFilePath ( files [ 0 ] ) , 'utf8' ) ; const summary = deserialize ( serializedSummary , testSerializer ) ; assert . typeOf ( summary , 'object' ) ; expectedTree . loadSummary ( summary as SharedTreeSummaryBase ) ; for ( let i = 1 ; i < files . length ; i ++ ) { const { tree } = setUpTestSharedTree ( ) ; const serializedSummary = fs . readFileSync ( summaryFilePath ( files [ i ] ) , 'utf8' ) ; const summary = deserialize ( serializedSummary , testSerializer ) ; assert . typeOf ( summary , 'object' ) ; tree . loadSummary ( summary as SharedTreeSummaryBase ) ; expect ( tree . equals ( expectedTree ) ) . to . be . true ; } } ) ; for ( const { version , summarizer } of supportedSummarizers ) { it ( `format version ${ version } can be written for ${ summaryType } summary type` , async ( ) => { const serializedSummary = fs . readFileSync ( summaryFilePath ( files . sort ( ) [ 0 ] ) , 'utf8' ) ; const summary = deserialize ( serializedSummary , testSerializer ) ; assert . typeOf ( summary , 'object' ) ; await testObjectProvider . ensureSynchronized ( ) ; expectedTree . loadSummary ( summary as SharedTreeSummaryBase ) ; await testObjectProvider . ensureSynchronized ( ) ; const newSummary = expectedTree . saveSerializedSummary ( { summarizer } ) ; const fileName = `${ summaryType }-${ version }` ; const expectedSummary = JSON . stringify ( JSON . parse ( fs . readFileSync ( summaryFilePath ( fileName ) , 'utf8' ) ) ) ; expect ( newSummary ) . to . equal ( expectedSummary ) ; } ) ; } } describe ( 'version 0.0.2' , ( ) => { it ( 'can be read and written with no history' , async ( ) => { validateSummaryRead ( 'no-history-0.0.2' ) ; validateSummaryWrite ( fullHistorySummarizer ) ; } ) ; it ( 'can be read and written with small history' , async ( ) => { createStableEdits ( 11 ) . forEach ( ( edit ) => { expectedTree . processLocalEdit ( edit ) ; } ) ; await testObjectProvider . ensureSynchronized ( ) ; validateSummaryRead ( 'small-history-0.0.2' ) ; validateSummaryWrite ( fullHistorySummarizer ) ; } ) ; } ) ; describe ( 'version 0.1.0' , ( ) => { it ( 'can be read and written with no history' , async ( ) => { validateSummaryRead ( 'no-history-0.1.0' ) ; validateSummaryWrite ( fullHistorySummarizer_0_1_0 ) ; } ) ; it ( 'can be read and written with large history' , async ( ) => { createStableEdits ( 251 ) . forEach ( ( edit ) => { expectedTree . processLocalEdit ( edit ) ; } ) ; await testObjectProvider . ensureSynchronized ( ) ; validateSummaryRead ( 'large-history-0.1.0' ) ; validateSummaryWrite ( fullHistorySummarizer_0_1_0 ) ; } ) ; } ) ; } ) ; }