import { HealthChecker , AddChainOptions , Chain , Client , JsonRpcCallback , ClientOptions , } from "@substrate/smoldot-light" ; import { JsonRpcObject } from "@polkadot/rpc-provider/types" ; import { jest } from "@jest/globals" ; import asap from "asap" ; interface Smoldot { start ( options ? ) : Client ; healthChecker ( ) : HealthChecker ; } remarks export interface SystemHealth { isSyncing : boolean ; peerCount : number ; shouldHavePeers : boolean ; } /**
 * Response builders - functions that simplify making RPC responses
 */ returns const systemHealthReponse = ( id , health ) => { return `{"jsonrpc":"2.0","id":${ id } ,"result":{"isSyncing":${ health . isSyncing },"peers":${ health . peerCount },"shouldHavePeers":${ health . shouldHavePeers }}}` ; } ; returns remarks const devChainHealthResponse = ( id ) => { return systemHealthReponse ( id , { isSyncing : true , peerCount : 0 , shouldHavePeers : false , } ) ; } ; /**
 * RpcResponder - an RpcResponder is a type representing a function that takes
 * a JSON string representing an RPC request and uses it to create a JSON RPC
 * response.
 */ type RpcResponder = ( requestJSON : string ) => string ; param requestJSON returns const healthyResponder = ( requestJSON ) => { const { id } = JSON . parse ( requestJSON ) as JsonRpcObject ; return systemHealthReponse ( id , { isSyncing : false , peerCount : 1 , shouldHavePeers : true , } ) ; } ; param requestJSON returns export const devChainHealthResponder = ( requestJSON ) : string => { const { id } = JSON . parse ( requestJSON ) as JsonRpcObject ; return devChainHealthResponse ( id ) ; } ; param requestJSON returns export const erroringResponder = ( requestJSON ) : string => { const { id } = JSON . parse ( requestJSON ) as JsonRpcObject ; return `{ "id": ${ id }, "jsonrpc": "2.0", "error": {"code": 666, "message": "boom!" } }` ; } ; /**
 * Orchestrators - these are higher order factory functions take the
 * definitions of a sequence of reponses to respond with and returns
 * `RpcResponder` function that when called will return a reponse matching what
 * was supplied one-by-one each time it is called until there are no more
 * responses.
 */ const NO_MORE_RESPONSES = "json_rpc_callback was called but there are no mock responses left to respond with" ; param healthResponses returns export const customHealthResponder = ( healthResponses ) => { return ( requestJSON ) : string => { const { id } = JSON . parse ( requestJSON ) as JsonRpcObject ; const health = healthResponses . shift ( ) ; if ( ! health ) { throw new Error ( NO_MORE_RESPONSES ) ; } return systemHealthReponse ( id , health ) ; } ; } ; param jsonResponses returns export const respondWith = ( jsonResponses ) => { return ( _ ) : string => { const mockResponse = jsonResponses . shift ( ) ; if ( ! mockResponse ) { throw new Error ( NO_MORE_RESPONSES ) ; } return mockResponse ; } ; } ; /**
 * Fakes - these are the actual mock / spy implementations you will
 * create to use in place of a real instance of smoldot
 */ /**
 * createRequestProcessor - returns a function that mimics the behaviour of the
 * WASM light client by deferring a call to `jsonRpcCallback` after it is
 * called which returns the response returned by the supplied `responder`.
 *
 * If this is called with an rpc string containing the word subscribe (but not
 * unsubscribe), it is considered a subscription and `responder` must have 2
 * mock responses for each subscriptionrequest that the test will make.  The
 * first returning the subscription id.  The second a response to the
 * subscription.
 */ const createRequestProcessor = ( options , responder , healthResponder ) => { return ( rpcRequest ) => { asap ( ( ) => { if ( options && options . jsonRpcCallback ) { if ( /system_health/ . test ( rpcRequest ) ) { options . jsonRpcCallback ( healthResponder ( rpcRequest ) ) ; return ; } options . jsonRpcCallback ( responder ( rpcRequest ) ) ; if ( /(?<!un)[sS]ubscribe/ . test ( rpcRequest ) ) { options . jsonRpcCallback ( responder ( rpcRequest ) ) ; } } } ) ; } ; } ; const doNothing = ( ) => { } ; param responder param healthResponder export const mockSmoldot = ( responder , healthResponder = healthyResponder ) : Smoldot => { return { start : ( ) : Client => { return { terminate : ( ) => Promise . resolve ( ) , addChain : ( options ) : Promise < Chain > => createAddChain ( { chainSpec : options . chainSpec , jsonRpcCallback : createRequestProcessor ( options , responder , healthResponder ) , } ) , } ; } , healthChecker : ( ) : HealthChecker => { return { setSendJsonRpc : doNothing , sendJsonRpc : doNothing , start : doNothing , stop : doNothing , responsePassThrough : ( _resp ) => "" , } ; } , } ; } ; export const createAddChain = ( opts ) : Promise < Chain > => { return Promise . resolve ( { sendJsonRpc : opts . jsonRpcCallback , databaseContent : async ( ) => "" , remove : doNothing , } ) ; } ; param responder param rpcSpy param healthResponder export const smoldotSpy = ( responder , rpcSpy , healthResponder = healthyResponder ) : Smoldot => { return { start : ( ) : Client => { return { terminate : ( ) => Promise . resolve ( ) , addChain : ( options ) : Promise < Chain > => { const processRequest = createRequestProcessor ( options , responder , healthResponder ) ; return createAddChain ( { chainSpec : options . chainSpec , jsonRpcCallback : ( rpc ) => { rpcSpy ( rpc ) ; processRequest ( rpc ) ; } , } ) ; } , } ; } , healthChecker : ( ) : HealthChecker => { return { setSendJsonRpc : doNothing , sendJsonRpc : doNothing , start : doNothing , stop : doNothing , responsePassThrough : ( _resp ) => "" , } ; } , } ; } ;