mirror of
https://github.com/pezkuwichain/pezkuwi-api.git
synced 2026-11-12 17:37:07 +00:00
Rebrand: polkadot → pezkuwi, substrate → bizinikiwi, kusama → dicle
This commit is contained in:
commit
d5f038faea
1383 files changed
+1088018
No files matched your search
@@ -0,0 +1,259 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
import type { Header } from '@pezkuwi/types/interfaces';
|
||||
import type { Codec, Registry } from '@pezkuwi/types/types';
|
||||
import type { ProviderInterface, ProviderInterfaceEmitCb, ProviderInterfaceEmitted } from '../types.js';
|
||||
import type { MockStateDb, MockStateSubscriptionCallback, MockStateSubscriptions } from './types.js';
|
||||
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
|
||||
import { createTestKeyring } from '@pezkuwi/keyring/testing';
|
||||
import { decorateStorage, Metadata } from '@pezkuwi/types';
|
||||
import jsonrpc from '@pezkuwi/types/interfaces/jsonrpc';
|
||||
import rpcHeader from '@pezkuwi/types-support/json/Header.004.json' assert { type: 'json' };
|
||||
import rpcSignedBlock from '@pezkuwi/types-support/json/SignedBlock.004.immortal.json' assert { type: 'json' };
|
||||
import rpcMetadata from '@pezkuwi/types-support/metadata/static-bizinikiwi';
|
||||
import { BN, bnToU8a, logger, u8aToHex } from '@pezkuwi/util';
|
||||
import { randomAsU8a } from '@pezkuwi/util-crypto';
|
||||
|
||||
const INTERVAL = 1000;
|
||||
const SUBSCRIPTIONS: string[] = Array.prototype.concat.apply(
|
||||
[],
|
||||
Object.values(jsonrpc).map((section): string[] =>
|
||||
Object
|
||||
.values(section)
|
||||
.filter(({ isSubscription }) => isSubscription)
|
||||
.map(({ jsonrpc }) => jsonrpc)
|
||||
.concat('chain_subscribeNewHead')
|
||||
)
|
||||
) as string[];
|
||||
|
||||
const keyring = createTestKeyring({ type: 'ed25519' });
|
||||
const l = logger('api-mock');
|
||||
|
||||
/**
|
||||
* A mock provider mainly used for testing.
|
||||
* @return {ProviderInterface} The mock provider
|
||||
* @internal
|
||||
*/
|
||||
export class MockProvider implements ProviderInterface {
|
||||
private db: MockStateDb = {};
|
||||
|
||||
private emitter = new EventEmitter();
|
||||
|
||||
private intervalId?: ReturnType<typeof setInterval> | null;
|
||||
|
||||
public isUpdating = true;
|
||||
|
||||
private registry: Registry;
|
||||
|
||||
private prevNumber = new BN(-1);
|
||||
|
||||
private requests: Record<string, (...params: any[]) => unknown> = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
chain_getBlock: () => this.registry.createType('SignedBlock', rpcSignedBlock.result).toJSON(),
|
||||
chain_getBlockHash: () => '0x1234000000000000000000000000000000000000000000000000000000000000',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
chain_getFinalizedHead: () => this.registry.createType('Header', rpcHeader.result).hash,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||
chain_getHeader: () => this.registry.createType('Header', rpcHeader.result).toJSON(),
|
||||
rpc_methods: () => this.registry.createType('RpcMethods').toJSON(),
|
||||
state_getKeys: () => [],
|
||||
state_getKeysPaged: () => [],
|
||||
state_getMetadata: () => rpcMetadata,
|
||||
state_getRuntimeVersion: () => this.registry.createType('RuntimeVersion').toHex(),
|
||||
state_getStorage: (storage: MockStateDb, [key]: string[]) => u8aToHex(storage[key]),
|
||||
system_chain: () => 'mockChain',
|
||||
system_health: () => ({}),
|
||||
system_name: () => 'mockClient',
|
||||
system_properties: () => ({ ss58Format: 42 }),
|
||||
system_upgradedToTripleRefCount: () => this.registry.createType('bool', true),
|
||||
system_version: () => '9.8.7',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, sort-keys
|
||||
dev_echo: (_, params: any) => params
|
||||
};
|
||||
|
||||
public subscriptions: MockStateSubscriptions = SUBSCRIPTIONS.reduce((subs, name): MockStateSubscriptions => {
|
||||
subs[name] = {
|
||||
callbacks: {},
|
||||
lastValue: null
|
||||
};
|
||||
|
||||
return subs;
|
||||
}, ({} as MockStateSubscriptions));
|
||||
|
||||
private subscriptionId = 0;
|
||||
|
||||
private subscriptionMap: Record<number, string> = {};
|
||||
|
||||
constructor (registry: Registry) {
|
||||
this.registry = registry;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
public get hasSubscriptions (): boolean {
|
||||
return !!true;
|
||||
}
|
||||
|
||||
public clone (): MockProvider {
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
public async connect (): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async disconnect (): Promise<void> {
|
||||
if (this.intervalId) {
|
||||
clearInterval(this.intervalId);
|
||||
this.intervalId = null;
|
||||
}
|
||||
}
|
||||
|
||||
public get isClonable (): boolean {
|
||||
return !!false;
|
||||
}
|
||||
|
||||
public get isConnected (): boolean {
|
||||
return !!true;
|
||||
}
|
||||
|
||||
public on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void {
|
||||
this.emitter.on(type, sub);
|
||||
|
||||
return (): void => {
|
||||
this.emitter.removeListener(type, sub);
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async send <T = any> (method: string, params: unknown[]): Promise<T> {
|
||||
l.debug(() => ['send', method, params]);
|
||||
|
||||
if (!this.requests[method]) {
|
||||
throw new Error(`provider.send: Invalid method '${method}'`);
|
||||
}
|
||||
|
||||
return this.requests[method](this.db, params) as T;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async subscribe (_type: string, method: string, ...params: unknown[]): Promise<number> {
|
||||
l.debug(() => ['subscribe', method, params]);
|
||||
|
||||
if (!this.subscriptions[method]) {
|
||||
throw new Error(`provider.subscribe: Invalid method '${method}'`);
|
||||
}
|
||||
|
||||
const callback = params.pop() as MockStateSubscriptionCallback;
|
||||
const id = ++this.subscriptionId;
|
||||
|
||||
this.subscriptions[method].callbacks[id] = callback;
|
||||
this.subscriptionMap[id] = method;
|
||||
|
||||
if (this.subscriptions[method].lastValue !== null) {
|
||||
callback(null, this.subscriptions[method].lastValue);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async unsubscribe (_type: string, _method: string, id: number): Promise<boolean> {
|
||||
const sub = this.subscriptionMap[id];
|
||||
|
||||
l.debug(() => ['unsubscribe', id, sub]);
|
||||
|
||||
if (!sub) {
|
||||
throw new Error(`Unable to find subscription for ${id}`);
|
||||
}
|
||||
|
||||
delete this.subscriptionMap[id];
|
||||
delete this.subscriptions[sub].callbacks[id];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private init (): void {
|
||||
const emitEvents: ProviderInterfaceEmitted[] = ['connected', 'disconnected'];
|
||||
let emitIndex = 0;
|
||||
let newHead = this.makeBlockHeader();
|
||||
let counter = -1;
|
||||
|
||||
const metadata = new Metadata(this.registry, rpcMetadata);
|
||||
|
||||
this.registry.setMetadata(metadata);
|
||||
|
||||
const query = decorateStorage(this.registry, metadata.asLatest, metadata.version);
|
||||
|
||||
// Do something every 1 seconds
|
||||
this.intervalId = setInterval((): void => {
|
||||
if (!this.isUpdating) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create a new header (next block)
|
||||
newHead = this.makeBlockHeader();
|
||||
|
||||
// increment the balances and nonce for each account
|
||||
keyring.getPairs().forEach(({ publicKey }, index): void => {
|
||||
this.setStateBn(query['system']['account'](publicKey), newHead.number.toBn().addn(index));
|
||||
});
|
||||
|
||||
// set the timestamp for the current block
|
||||
this.setStateBn(query['timestamp']['now'](), Math.floor(Date.now() / 1000));
|
||||
this.updateSubs('chain_subscribeNewHead', newHead);
|
||||
|
||||
// We emit connected/disconnected at intervals
|
||||
if (++counter % 2 === 1) {
|
||||
if (++emitIndex === emitEvents.length) {
|
||||
emitIndex = 0;
|
||||
}
|
||||
|
||||
this.emitter.emit(emitEvents[emitIndex]);
|
||||
}
|
||||
}, INTERVAL);
|
||||
}
|
||||
|
||||
private makeBlockHeader (): Header {
|
||||
const blockNumber = this.prevNumber.addn(1);
|
||||
const header = this.registry.createType('Header', {
|
||||
digest: {
|
||||
logs: []
|
||||
},
|
||||
extrinsicsRoot: randomAsU8a(),
|
||||
number: blockNumber,
|
||||
parentHash: blockNumber.isZero()
|
||||
? new Uint8Array(32)
|
||||
: bnToU8a(this.prevNumber, { bitLength: 256, isLe: false }),
|
||||
stateRoot: bnToU8a(blockNumber, { bitLength: 256, isLe: false })
|
||||
});
|
||||
|
||||
this.prevNumber = blockNumber;
|
||||
|
||||
return header as unknown as Header;
|
||||
}
|
||||
|
||||
private setStateBn (key: Uint8Array, value: BN | number): void {
|
||||
this.db[u8aToHex(key)] = bnToU8a(value, { bitLength: 64, isLe: true });
|
||||
}
|
||||
|
||||
private updateSubs (method: string, value: Codec): void {
|
||||
this.subscriptions[method].lastValue = value;
|
||||
|
||||
Object
|
||||
.values(this.subscriptions[method].callbacks)
|
||||
.forEach((cb): void => {
|
||||
try {
|
||||
cb(null, value.toJSON());
|
||||
} catch (error) {
|
||||
l.error(`Error on '${method}' subscription`, error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Mock } from './types.js';
|
||||
|
||||
import nock from 'nock';
|
||||
|
||||
interface Request {
|
||||
code?: number;
|
||||
method: string;
|
||||
reply?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
interface HttpMock extends Mock {
|
||||
post: (uri: string) => {
|
||||
reply: (code: number, handler: (uri: string, body: { id: string }) => unknown) => HttpMock
|
||||
}
|
||||
}
|
||||
|
||||
export const TEST_HTTP_URL = 'http://localhost:9944';
|
||||
|
||||
export function mockHttp (requests: Request[]): Mock {
|
||||
nock.cleanAll();
|
||||
|
||||
return requests.reduce((scope: HttpMock, request: Request) =>
|
||||
scope
|
||||
.post('/')
|
||||
.reply(request.code || 200, (_uri: string, body: { id: string }) => {
|
||||
scope.body = scope.body || {};
|
||||
scope.body[request.method] = body;
|
||||
|
||||
return Object.assign({ id: body.id, jsonrpc: '2.0' }, request.reply || {}) as unknown;
|
||||
}),
|
||||
nock(TEST_HTTP_URL) as unknown as HttpMock);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { Server, WebSocket } from 'mock-socket';
|
||||
|
||||
import { stringify } from '@pezkuwi/util';
|
||||
|
||||
interface Scope {
|
||||
body: Record<string, Record<string, unknown>>;
|
||||
requests: number;
|
||||
server: Server;
|
||||
done: any;
|
||||
}
|
||||
|
||||
interface ErrorDef {
|
||||
id: number;
|
||||
error: {
|
||||
code: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ReplyDef {
|
||||
id: number;
|
||||
reply: {
|
||||
result: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
interface RpcBase {
|
||||
id: number;
|
||||
jsonrpc: '2.0';
|
||||
}
|
||||
|
||||
type RpcError = RpcBase & ErrorDef;
|
||||
type RpcReply = RpcBase & { result: unknown };
|
||||
|
||||
export type Request = { method: string } & (ErrorDef | ReplyDef);
|
||||
|
||||
global.WebSocket = WebSocket as typeof global.WebSocket;
|
||||
|
||||
export const TEST_WS_URL = 'ws://localhost:9955';
|
||||
|
||||
// should be JSONRPC def return
|
||||
function createError ({ error: { code, message }, id }: ErrorDef): RpcError {
|
||||
return {
|
||||
error: {
|
||||
code,
|
||||
message
|
||||
},
|
||||
id,
|
||||
jsonrpc: '2.0'
|
||||
};
|
||||
}
|
||||
|
||||
// should be JSONRPC def return
|
||||
function createReply ({ id, reply: { result } }: ReplyDef): RpcReply {
|
||||
return {
|
||||
id,
|
||||
jsonrpc: '2.0',
|
||||
result
|
||||
};
|
||||
}
|
||||
|
||||
// scope definition returned
|
||||
export function mockWs (requests: Request[], wsUrl: string = TEST_WS_URL): Scope {
|
||||
const server = new Server(wsUrl);
|
||||
|
||||
let requestCount = 0;
|
||||
const scope: Scope = {
|
||||
body: {},
|
||||
done: () => new Promise<void>((resolve) => server.stop(resolve)),
|
||||
requests: 0,
|
||||
server
|
||||
};
|
||||
|
||||
server.on('connection', (socket): void => {
|
||||
socket.on('message', (body): void => {
|
||||
const request = requests[requestCount];
|
||||
const response = (request as ErrorDef).error
|
||||
? createError(request as ErrorDef)
|
||||
: createReply(request as ReplyDef);
|
||||
|
||||
scope.body[request.method] = body as unknown as Record<string, unknown>;
|
||||
requestCount++;
|
||||
|
||||
socket.send(stringify(response));
|
||||
});
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@pezkuwi/dev-test/globals.d.ts" />
|
||||
|
||||
import type { ProviderInterfaceEmitted } from '../types.js';
|
||||
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
|
||||
import { MockProvider } from './index.js';
|
||||
|
||||
describe('on', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let mock: MockProvider;
|
||||
|
||||
beforeEach((): void => {
|
||||
mock = new MockProvider(registry);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await mock.disconnect();
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('emits both connected and disconnected events', async (): Promise<void> => {
|
||||
const events: Record<string, boolean> = { connected: false, disconnected: false };
|
||||
|
||||
await new Promise<boolean>((resolve) => {
|
||||
const handler = (type: ProviderInterfaceEmitted): void => {
|
||||
mock.on(type, (): void => {
|
||||
events[type] = true;
|
||||
|
||||
if (Object.values(events).filter((value): boolean => value).length === 2) {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
handler('connected');
|
||||
handler('disconnected');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@pezkuwi/dev-test/globals.d.ts" />
|
||||
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
|
||||
import { MockProvider } from './index.js';
|
||||
|
||||
describe('send', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let mock: MockProvider;
|
||||
|
||||
beforeEach((): void => {
|
||||
mock = new MockProvider(registry);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await mock.disconnect();
|
||||
});
|
||||
|
||||
it('fails on non-supported methods', (): Promise<any> => {
|
||||
return mock
|
||||
.send('something_invalid', [])
|
||||
.catch((error): void => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect((error as Error).message).toMatch(/Invalid method/);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns values for mocked requests', (): Promise<void> => {
|
||||
return mock
|
||||
.send('system_name', [])
|
||||
.then((result): void => {
|
||||
expect(result).toBe('mockClient');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@pezkuwi/dev-test/globals.d.ts" />
|
||||
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
|
||||
import { MockProvider } from './index.js';
|
||||
|
||||
describe('subscribe', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let mock: MockProvider;
|
||||
|
||||
beforeEach((): void => {
|
||||
mock = new MockProvider(registry);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await mock.disconnect();
|
||||
});
|
||||
|
||||
it('fails on unknown methods', async (): Promise<void> => {
|
||||
await mock
|
||||
.subscribe('test', 'test_notFound')
|
||||
.catch((error): void => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect((error as Error).message).toMatch(/Invalid method 'test_notFound'/);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a subscription id', async (): Promise<void> => {
|
||||
await mock
|
||||
.subscribe('chain_newHead', 'chain_subscribeNewHead', (): void => undefined)
|
||||
.then((id): void => {
|
||||
expect(id).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('calls back with the last known value', async (): Promise<void> => {
|
||||
mock.isUpdating = false;
|
||||
mock.subscriptions.chain_subscribeNewHead.lastValue = 'testValue';
|
||||
|
||||
await new Promise<boolean>((resolve) => {
|
||||
mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, value: string): void => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(value).toEqual('testValue');
|
||||
resolve(true);
|
||||
}).catch(console.error);
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('calls back with new headers', async (): Promise<void> => {
|
||||
await new Promise<boolean>((resolve) => {
|
||||
mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: { number: number }): void => {
|
||||
if (header.number === 4) {
|
||||
resolve(true);
|
||||
}
|
||||
}).catch(console.error);
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('handles errors within callbacks gracefully', async (): Promise<void> => {
|
||||
let hasThrown = false;
|
||||
|
||||
await new Promise<boolean>((resolve) => {
|
||||
mock.subscribe('chain_newHead', 'chain_subscribeNewHead', (_: any, header: { number: number }): void => {
|
||||
if (!hasThrown) {
|
||||
hasThrown = true;
|
||||
|
||||
throw new Error('testing');
|
||||
}
|
||||
|
||||
if (header.number === 3) {
|
||||
resolve(true);
|
||||
}
|
||||
}).catch(console.error);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Server } from 'mock-socket';
|
||||
|
||||
export type Global = typeof globalThis & {
|
||||
WebSocket: typeof WebSocket;
|
||||
fetch: any;
|
||||
}
|
||||
|
||||
export interface Mock {
|
||||
body: Record<string, Record<string, unknown>>;
|
||||
requests: number;
|
||||
server: Server;
|
||||
done: () => Promise<void>;
|
||||
}
|
||||
|
||||
export type MockStateSubscriptionCallback = (error: Error | null, value: any) => void;
|
||||
|
||||
export interface MockStateSubscription {
|
||||
callbacks: Record<number, MockStateSubscriptionCallback>;
|
||||
lastValue: any;
|
||||
}
|
||||
|
||||
export interface MockStateSubscriptions {
|
||||
// known
|
||||
chain_subscribeNewHead: MockStateSubscription;
|
||||
state_subscribeStorage: MockStateSubscription;
|
||||
|
||||
// others
|
||||
[key: string]: MockStateSubscription;
|
||||
}
|
||||
|
||||
export type MockStateDb = Record<string, Uint8Array>;
|
||||
|
||||
export type MockStateRequests = Record<string, (db: MockStateDb, params: any[]) => string>;
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2017-2025 @pezkuwi/rpc-provider authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="@pezkuwi/dev-test/globals.d.ts" />
|
||||
|
||||
import { TypeRegistry } from '@pezkuwi/types/create';
|
||||
|
||||
import { MockProvider } from './index.js';
|
||||
|
||||
describe('unsubscribe', (): void => {
|
||||
const registry = new TypeRegistry();
|
||||
let mock: MockProvider;
|
||||
let id: number;
|
||||
|
||||
beforeEach((): Promise<void> => {
|
||||
mock = new MockProvider(registry);
|
||||
|
||||
return mock
|
||||
.subscribe('chain_newHead', 'chain_subscribeNewHead', (): void => undefined)
|
||||
.then((_id): void => {
|
||||
id = _id;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await mock.disconnect();
|
||||
});
|
||||
|
||||
it('fails on unknown ids', async (): Promise<void> => {
|
||||
await mock
|
||||
.unsubscribe('chain_newHead', 'chain_subscribeNewHead', 5)
|
||||
.catch((error): boolean => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect((error as Error).message).toMatch(/Unable to find/);
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/expect-expect
|
||||
it('unsubscribes successfully', async (): Promise<void> => {
|
||||
await mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id);
|
||||
});
|
||||
|
||||
it('fails on double unsubscribe', async (): Promise<void> => {
|
||||
await mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id)
|
||||
.then((): Promise<boolean> =>
|
||||
mock.unsubscribe('chain_newHead', 'chain_subscribeNewHead', id)
|
||||
)
|
||||
.catch((error): boolean => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect((error as Error).message).toMatch(/Unable to find/);
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user