/*! * Copyright (c) Microsoft Corporation and contributors. All rights reserved. * Licensed under the MIT License. */ import { type BaseFuzzTestState, type Generator, type SaveInfo } from "@fluid-private/stochastic-test-utils"; import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; import { IdCompressor } from "../idCompressor.js"; import { type IIdCompressor, type IIdCompressorCore, type OpSpaceCompressedId, type SerializedIdCompressorWithNoSession, type SerializedIdCompressorWithOngoingSession, type SessionId, type SessionSpaceCompressedId } from "../index.js"; import { type ReadonlyIdCompressor } from "./testCommon.js"; /** * A readonly `Map` which is known to contain a value for every possible key */ export interface ClosedMap extends Omit, "delete" | "clear"> { get(key: K): V; } /** * Identifies a compressor in a network */ export declare enum Client { Client1 = "Client1", Client2 = "Client2", Client3 = "Client3" } /** * Identifies categories of compressors */ export declare enum MetaClient { All = "All" } /** * Identifies a compressor inside the network but outside the three specially tracked clients. */ export declare enum OutsideClient { Remote = "Remote" } /** * Used to attribute actions to clients in a distributed collaboration session. * `Local` implies a local and unsequenced operation. All others imply sequenced operations. */ export type OriginatingClient = Client | OutsideClient; export declare const OriginatingClient: { Remote: OutsideClient.Remote; Client1: Client.Client1; Client2: Client.Client2; Client3: Client.Client3; }; /** * Identifies a compressor to which to send an operation */ export type DestinationClient = Client | MetaClient; export declare const DestinationClient: { All: MetaClient.All; Client1: Client.Client1; Client2: Client.Client2; Client3: Client.Client3; }; export declare class CompressorFactory { /** * Creates a new compressor with the supplied cluster capacity. */ static createCompressor(client: Client, clusterCapacity?: number, logger?: ITelemetryBaseLogger): IdCompressor; /** * Creates a new compressor with the supplied cluster capacity. */ static createCompressorWithSession(sessionId: SessionId, clusterCapacity?: number, logger?: ITelemetryBaseLogger): IdCompressor; } /** * Modify the requested cluster size of the provided compressor. * @remarks * This is useful for testing purposes for a few reasons: * - Id compressor bugs are often related to edge cases that occur on cluster boundaries * - Smaller cluster sizes can enable writing tests without for loops generating "ids until a new cluster is created" */ export declare function modifyClusterSize(compressor: IIdCompressor, newClusterSize: number): void; /** * Returns the current cluster size of the compressor. * @privateRemarks * This is useful in writing tests to avoid having to hardcode the (currently constant) cluster size. */ export declare function getClusterSize(compressor: ReadonlyIdCompressor): number; /** * Utility for building a huge compressor. * Build via the compressor factory. */ export declare function buildHugeCompressor(numSessions?: number, capacity?: number, numClustersPerSession?: number): IdCompressor; /** * A closed map from NamedClient to T. */ export type ClientMap = ClosedMap; /** * An array of session ID strings corresponding to all non-local `Client` entries. */ export declare const sessionIds: ClientMap; /** * Information about a generated ID in a network to be validated by tests */ export interface TestIdData { readonly id: SessionSpaceCompressedId; readonly originatingClient: OriginatingClient; readonly sessionId: SessionId; readonly isSequenced: boolean; } /** * Simulates a network of ID compressors. * Not suitable for performance testing. */ export declare class IdCompressorTestNetwork { readonly initialClusterSize: number; /** * The compressors used in this network */ private readonly compressors; /** * The log of operations seen by the server so far. Append-only. */ private readonly serverOperations; /** * An index into `serverOperations` for each client which represents how many operations have been delivered to that client */ private readonly clientProgress; /** * All ids (local and sequenced) that a client has created or received, in order. */ private readonly idLogs; /** * All ids that a client has received from the server, in order. */ private readonly sequencedIdLogs; constructor(initialClusterSize?: number); /** * Returns the number of undelivered operations for the given client that are in flight in the network. */ getPendingOperations(destination: Client): number; /** * Returns an immutable handle to a compressor in the network. */ getCompressor(client: Client): ReadonlyIdCompressor; /** * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and * should only be used if the network will not be used again. */ getCompressorUnsafe(client: Client): IdCompressor; /** * Returns a mutable handle to a compressor in the network. Use of mutation methods will break the network invariants and * should only be used if the network will not be used again. Additionally, the returned compressor will be invalidated/unusable * if any network operations cause it to be regenerated (serialization/deserialization, etc.). */ getCompressorUnsafeNoProxy(client: Client): IdCompressor; /** * Returns data for all IDs created and received by this client, including ack's of their own (i.e. their own IDs will appear twice) */ getIdLog(client: Client): readonly TestIdData[]; /** * Returns data for all IDs received by this client, including ack's of their own. */ getSequencedIdLog(client: Client): readonly TestIdData[]; /** * Get all compressors for the given destination */ getTargetCompressors(clientTo: DestinationClient): [Client, IdCompressor][]; /** * Changes the capacity request amount for a client. It will take effect immediately. */ changeCapacity(client: Client, newClusterCapacity: number): void; private addNewId; /** * Allocates a new range of local IDs and enqueues them for future delivery via a `testIdDelivery` action. * Calls to this method determine the total order of delivery, regardless of when `deliverOperations` is called. */ allocateAndSendIds(clientFrom: Client, numIds: number): OpSpaceCompressedId[]; /** * Same contract as `allocateAndSendIds`, but the originating client will be a client with the supplied sessionId. */ allocateAndSendIdsFromRemoteClient(clientFrom: OriginatingClient, sessionIdFrom: SessionId, numIds: number): OpSpaceCompressedId[]; /** * Delivers all undelivered ID ranges from the server to the target clients. */ deliverOperations(clientTakingDelivery: Client, opsToDeliver?: number): void; /** * Delivers all undelivered ID ranges from the server to the target clients. */ deliverOperations(clientTakingDelivery: DestinationClient): void; /** * Simulate a client disconnecting (and serializing), then reconnecting (and deserializing) */ goOfflineThenResume(client: Client): void; /** * Ensure general validity of the network state. Useful for calling periodically or at the end of test scenarios. */ assertNetworkState(): void; } /** * Roundtrips the supplied compressor through serialization and deserialization. */ export declare function roundtrip(compressor: ReadonlyIdCompressor, withSession: true): [SerializedIdCompressorWithOngoingSession, IdCompressor]; /** * Roundtrips the supplied compressor through serialization and deserialization. */ export declare function roundtrip(compressor: ReadonlyIdCompressor, withSession: false): [SerializedIdCompressorWithNoSession, IdCompressor]; /** * Asserts that the supplied compressor correctly roundtrips through serialization/deserialization. */ export declare function expectSerializes(compressor: ReadonlyIdCompressor): [SerializedIdCompressorWithNoSession, SerializedIdCompressorWithOngoingSession]; /** * Merges 'from' into 'to', and returns 'to'. */ export declare function mergeArrayMaps(to: Pick, "get" | "set">, from: ReadonlyMap): Pick, "get" | "set">; interface AllocateIds { type: "allocateIds"; client: Client; numIds: number; } interface AllocateOutsideIds { type: "allocateOutsideIds"; sessionId: SessionId; numIds: number; } interface DeliverAllOperations { type: "deliverAllOperations"; } interface DeliverSomeOperations { type: "deliverSomeOperations"; client: Client; count: number; } interface ChangeCapacity { type: "changeCapacity"; client: Client; newSize: number; } interface Reconnect { type: "reconnect"; client: Client; } interface Validate { type: "validate"; } type Operation = AllocateIds | AllocateOutsideIds | DeliverSomeOperations | DeliverAllOperations | ChangeCapacity | Reconnect | Validate; interface FuzzTestState extends BaseFuzzTestState { network: IdCompressorTestNetwork; activeClients: Client[]; selectableClients: Client[]; clusterSize: number; } export interface OperationGenerationConfig { /** * maximum cluster size of the network. Default: 25 */ maxClusterSize?: number; /** * Number of ops between validation ops. Default: 200 */ validateInterval?: number; /** * Fraction of ID allocations that are from an outside client (not Client1/2/3). */ outsideAllocationFraction?: number; } export declare function makeOpGenerator(options: OperationGenerationConfig): Generator; /** * Performs random actions on a test network. * @param generator - the generator used to provide operations * @param network - the test network to test * @param seed - the seed for the random generation of the fuzz actions * @param observerClient - if provided, this client will never generate local ids * @param synchronizeAtEnd - if provided, all client will have all operations delivered from the server at the end of the test * @param validator - if provided, this callback will be invoked periodically during the fuzz test. */ export declare function performFuzzActions(generator: Generator, network: IdCompressorTestNetwork, seed: number, observerClient?: Client, synchronizeAtEnd?: boolean, validator?: (network: IdCompressorTestNetwork) => void, saveInfo?: SaveInfo): void; /** * Helper to generate a fixed number of IDs. */ export declare function generateCompressedIds(compressor: IdCompressor, count: number): SessionSpaceCompressedId[]; /** * Creates a compressor that only produces final IDs. * It should only be used for testing purposes. */ export declare function createAlwaysFinalizedIdCompressor(logger?: ITelemetryBaseLogger): IIdCompressor & IIdCompressorCore; /** * Creates a compressor that only produces final IDs. * It should only be used for testing purposes. */ export declare function createAlwaysFinalizedIdCompressor(sessionId: SessionId, logger?: ITelemetryBaseLogger, seed?: number): IIdCompressor & IIdCompressorCore; export {}; //# sourceMappingURL=idCompressorTestUtilities.d.ts.map