Skip to content

Commit 4978898

Browse files
committed
chore: update sc provider
1 parent b132f64 commit 4978898

File tree

4 files changed

+228
-268
lines changed

4 files changed

+228
-268
lines changed

packages/rpc-provider/package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,14 @@
3535
"tslib": "^2.8.1"
3636
},
3737
"devDependencies": {
38-
"@substrate/connect": "0.8.11"
38+
"@substrate/connect": "^2.1.2"
3939
},
40-
"optionalDependencies": {
41-
"@substrate/connect": "0.8.11"
40+
"peerDependencies": {
41+
"@substrate/connect": "^2.1.2"
42+
},
43+
"peerDependenciesMeta": {
44+
"@substrate/connect": {
45+
"optional": true
46+
}
4247
}
4348
}

packages/rpc-provider/src/substrate-connect/index.spec.ts

Lines changed: 167 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -10,187 +10,203 @@ import { noop, stringify } from '@polkadot/util';
1010

1111
import { ScProvider } from './index.js';
1212

13-
interface MockChain extends Sc.Chain {
14-
_spec: () => string;
15-
_recevedRequests: () => string[];
13+
// Well known chain constants
14+
const mockWellKnownChain = {
15+
polkadot: 'polkadot',
16+
ksmcc3: 'ksmcc3',
17+
rococo_v2_2: 'rococo_v2_2',
18+
westend2: 'westend2',
19+
paseo: 'paseo'
20+
} as const;
21+
22+
interface MockChain {
23+
sendJsonRpc: (rpc: string) => void;
24+
remove: () => void;
25+
nextJsonRpcResponse: () => Promise<string>;
26+
jsonRpcResponses: AsyncIterableIterator<string>;
27+
addChain: Sc.AddChain;
28+
_requests: string[];
29+
_terminated: boolean;
30+
_callback: (response: string) => void;
31+
_interceptor: ((rpc: string) => void) | null;
32+
_setInterceptor: (fn: ((rpc: string) => void) | null) => void;
33+
_getLatestRequest: () => string | undefined;
34+
_triggerCallback: (response: unknown) => void;
1635
_isTerminated: () => boolean;
17-
_triggerCallback: (response: string | object) => void;
18-
_setTerminateInterceptor: (fn: () => void) => void;
19-
_setSendJsonRpcInterceptor: (fn: (rpc: string) => void) => void;
20-
_getLatestRequest: () => string;
21-
}
22-
23-
interface MockedHealthChecker extends HealthChecker {
24-
_isActive: () => boolean;
25-
_triggerHealthUpdate: (update: SmoldotHealth) => void;
26-
}
27-
28-
type MockSc = typeof Sc & {
29-
latestChain: () => MockChain;
30-
};
31-
32-
enum WellKnownChain {
33-
polkadot = 'polkadot',
34-
ksmcc3 = 'ksmcc3',
35-
rococo_v2_2 = 'rococo_v2_2',
36-
westend2 = 'westend2'
36+
_setSendJsonRpcInterceptor: (fn: ((rpc: string) => void) | null) => void;
37+
_recevedRequests: () => string[];
3738
}
3839

39-
const wait = (ms: number) =>
40-
new Promise((resolve) =>
41-
setTimeout(resolve, ms)
42-
);
43-
44-
function healthCheckerMock (): MockedHealthChecker {
45-
let cb: (health: SmoldotHealth) => void = () => undefined;
46-
let sendJsonRpc: (request: string) => void = () => undefined;
47-
let isActive = false;
40+
// Helper function to wait for a specified number of milliseconds
41+
const wait = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));
42+
43+
function createMockChain (callback: (response: string) => void): MockChain {
44+
const requests: string[] = [];
45+
let terminated = false;
46+
let interceptor: ((rpc: string) => void) | null = null;
47+
let responseQueue: string[] = [];
48+
49+
const nextJsonRpcResponse: MockChain['nextJsonRpcResponse'] = async () => {
50+
if (responseQueue.length === 0) {
51+
return new Promise<string>((resolve) => {
52+
const checkQueue = () => {
53+
if (responseQueue.length > 0) {
54+
resolve(responseQueue.shift()!);
55+
} else {
56+
setTimeout(checkQueue, 10);
57+
}
58+
};
59+
checkQueue();
60+
});
61+
}
62+
return responseQueue.shift()!;
63+
}
4864

49-
return {
50-
_isActive: () => isActive,
51-
_triggerHealthUpdate: (update: SmoldotHealth) => {
52-
cb(update);
65+
const jsonRpcResponses: MockChain['jsonRpcResponses'] = {
66+
async next() {
67+
const value = await nextJsonRpcResponse();
68+
return { done: false, value };
5369
},
54-
responsePassThrough: (response) => response,
55-
sendJsonRpc: (...args) => sendJsonRpc(...args),
56-
setSendJsonRpc: (cb) => {
57-
sendJsonRpc = cb;
70+
[Symbol.asyncIterator]() {
71+
return this;
5872
},
59-
start: (x) => {
60-
isActive = true;
61-
cb = x;
73+
return: () => Promise.resolve({ done: true, value: undefined }),
74+
throw: () => Promise.resolve({ done: true, value: undefined })
75+
}
76+
77+
const chain: MockChain = {
78+
_requests: requests,
79+
_terminated: terminated,
80+
_callback: (response: string) => {
81+
responseQueue.push(response);
82+
callback(response);
6283
},
63-
stop: () => {
64-
isActive = false;
65-
}
66-
};
67-
}
68-
69-
function healthCheckerFactory () {
70-
const _healthCheckers: MockedHealthChecker[] = [];
71-
72-
return {
73-
_healthCheckers,
74-
_latestHealthChecker: () => _healthCheckers.slice(-1)[0],
75-
healthChecker: () => {
76-
const result = healthCheckerMock();
77-
78-
_healthCheckers.push(result);
79-
80-
return result;
81-
}
82-
};
83-
}
84-
85-
function getFakeChain (spec: string, callback: Sc.JsonRpcCallback): MockChain {
86-
const _receivedRequests: string[] = [];
87-
let _isTerminated = false;
84+
_interceptor: null,
85+
_setInterceptor: (fn: ((rpc: string) => void) | null) => {
86+
interceptor = fn;
87+
},
88+
_getLatestRequest: () => requests[requests.length - 1],
89+
sendJsonRpc: (rpc: string) => {
90+
if (terminated) {
91+
throw new Error('Chain terminated');
92+
}
8893

89-
let terminateInterceptor = Function.prototype;
90-
let sendJsonRpcInterceptor = Function.prototype;
94+
if (interceptor) {
95+
interceptor(rpc);
96+
}
9197

92-
return {
93-
_getLatestRequest: () => _receivedRequests[_receivedRequests.length - 1],
94-
_isTerminated: () => _isTerminated,
95-
_recevedRequests: () => _receivedRequests,
96-
_setSendJsonRpcInterceptor: (fn) => {
97-
sendJsonRpcInterceptor = fn;
98+
requests.push(rpc);
9899
},
99-
_setTerminateInterceptor: (fn) => {
100-
terminateInterceptor = fn;
100+
remove: () => {
101+
terminated = true;
101102
},
102-
_spec: () => spec,
103-
_triggerCallback: (response) => {
104-
callback(
105-
typeof response === 'string'
106-
? response
107-
: stringify(response)
108-
);
103+
nextJsonRpcResponse,
104+
jsonRpcResponses,
105+
addChain: async () => createMockChain(noop),
106+
_triggerCallback: (response: unknown) => {
107+
callback(stringify(response));
109108
},
110-
addChain: (chainSpec, jsonRpcCallback) =>
111-
Promise.resolve(getFakeChain(chainSpec, jsonRpcCallback ?? noop)),
112-
remove: () => {
113-
terminateInterceptor();
114-
_isTerminated = true;
109+
_isTerminated: () => terminated,
110+
_setSendJsonRpcInterceptor: (fn: ((rpc: string) => void) | null) => {
111+
interceptor = fn;
115112
},
116-
sendJsonRpc: (rpc) => {
117-
sendJsonRpcInterceptor(rpc);
118-
_receivedRequests.push(rpc);
119-
}
113+
_recevedRequests: () => requests
120114
};
115+
116+
return chain;
121117
}
122118

123-
function getFakeClient () {
119+
function createMockClient () {
124120
const chains: MockChain[] = [];
125-
let addChainInterceptor: Promise<void> = Promise.resolve();
126-
let addWellKnownChainInterceptor: Promise<void> = Promise.resolve();
121+
let interceptor: Promise<void> = Promise.resolve();
127122

128123
return {
129-
_chains: () => chains,
130-
_setAddChainInterceptor: (interceptor: Promise<void>) => {
131-
addChainInterceptor = interceptor;
132-
},
133-
_setAddWellKnownChainInterceptor: (interceptor: Promise<void>) => {
134-
addWellKnownChainInterceptor = interceptor;
135-
},
136-
addChain: (chainSpec: string, cb: Sc.JsonRpcCallback): Promise<MockChain> =>
137-
addChainInterceptor.then(() => {
138-
const result = getFakeChain(chainSpec, cb);
124+
addChain: async (spec: string) => {
125+
await interceptor;
126+
const chain = createMockChain(noop);
139127

140-
chains.push(result);
128+
chains.push(chain);
141129

142-
return result;
143-
}),
144-
addWellKnownChain: (
145-
wellKnownChain: string,
146-
cb: Sc.JsonRpcCallback
147-
): Promise<MockChain> =>
148-
addWellKnownChainInterceptor.then(() => {
149-
const result = getFakeChain(wellKnownChain, cb);
130+
return chain;
131+
},
132+
addWellKnownChain: async (chain: string) => {
133+
await interceptor;
134+
const mockChain = createMockChain(noop);
150135

151-
chains.push(result);
136+
chains.push(mockChain);
152137

153-
return result;
154-
})
138+
return mockChain;
139+
},
140+
_chains: chains,
141+
_setInterceptor: (p: Promise<void>) => {
142+
interceptor = p;
143+
},
144+
latestChain: () => chains[chains.length - 1]
155145
};
156146
}
157147

158-
function connectorFactory (): MockSc {
159-
const clients: ReturnType<typeof getFakeClient>[] = [];
160-
const latestClient = () => clients[clients.length - 1];
161-
162-
return {
163-
WellKnownChain,
164-
_clients: () => clients,
165-
createScClient: () => {
166-
const result = getFakeClient();
167-
168-
clients.push(result);
148+
// Mock client instance that will be shared
149+
let mockClient = createMockClient();
150+
151+
// Mock Sc client with mockClient instance
152+
const mockSc = {
153+
createScClient: () => {
154+
mockClient = createMockClient();
155+
return mockClient;
156+
},
157+
WellKnownChain: mockWellKnownChain,
158+
latestChain: () => mockClient.latestChain()
159+
};
169160

170-
return result;
171-
},
172-
latestChain: () =>
173-
latestClient()._chains()[latestClient()._chains().length - 1],
174-
latestClient
175-
} as unknown as MockSc;
176-
}
161+
// Mock health checker factory
162+
const mockedHealthChecker = {
163+
healthChecker: () => createMockHealthChecker()
164+
};
177165

178-
function setChainSyncyingStatus (isSyncing: boolean): void {
179-
getCurrentHealthChecker()._triggerHealthUpdate({
166+
// Helper function to set chain syncing status
167+
function setChainSyncyingStatus(isSyncing: boolean) {
168+
const health: SmoldotHealth = {
180169
isSyncing,
181170
peers: 1,
182171
shouldHavePeers: true
183-
});
172+
};
173+
mockedHealthChecker.healthChecker()._update(health);
184174
}
185175

186-
let mockSc: MockSc;
187-
let mockedHealthChecker: ReturnType<typeof healthCheckerFactory>;
188-
const getCurrentHealthChecker = () => mockedHealthChecker._latestHealthChecker();
176+
function createMockHealthChecker (): HealthChecker & { _update: (health: SmoldotHealth) => void } {
177+
let sendRpc: ((req: string) => void) | null = null;
178+
let healthCb: ((health: SmoldotHealth) => void) | null = null;
179+
180+
return {
181+
setSendJsonRpc: (cb) => {
182+
sendRpc = cb;
183+
},
184+
sendJsonRpc: (req) => sendRpc?.(req),
185+
responsePassThrough: (res) => res,
186+
start: (cb) => {
187+
healthCb = cb;
188+
},
189+
stop: () => {
190+
healthCb = null;
191+
},
192+
_update: (health) => healthCb?.(health)
193+
};
194+
}
189195

190196
describe('ScProvider', () => {
191-
beforeAll(() => {
192-
mockSc = connectorFactory();
193-
mockedHealthChecker = healthCheckerFactory();
197+
let provider: ScProvider;
198+
let mockClient: ReturnType<typeof createMockClient>;
199+
let mockHealthChecker: ReturnType<typeof createMockHealthChecker>;
200+
201+
beforeEach(async () => {
202+
mockClient = createMockClient();
203+
mockHealthChecker = createMockHealthChecker();
204+
provider = new ScProvider({ createScClient: () => mockClient, WellKnownChain: mockWellKnownChain }, '');
205+
await provider.connect(undefined, () => mockHealthChecker);
206+
});
207+
208+
afterEach(async () => {
209+
await provider.disconnect();
194210
});
195211

196212
describe('on', () => {
@@ -372,7 +388,7 @@ describe('ScProvider', () => {
372388

373389
setTimeout(() => {
374390
chain._triggerCallback({
375-
id: 1,
391+
id: 1,
376392
jsonrpc: '2.0'
377393
});
378394
}, 0);
@@ -488,8 +504,8 @@ describe('ScProvider', () => {
488504
});
489505
setTimeout(() => {
490506
chain._triggerCallback({
491-
id: 1,
492-
jsonrpc: '2.0',
507+
id: 1,
508+
jsonrpc: '2.0',
493509
result: unsubscribeToken
494510
});
495511
}, 0);
@@ -519,8 +535,8 @@ describe('ScProvider', () => {
519535

520536
setTimeout(() => {
521537
chain._triggerCallback({
522-
id: 1,
523-
jsonrpc: '2.0',
538+
id: 1,
539+
jsonrpc: '2.0',
524540
result: unsubscribeToken
525541
});
526542
}, 0);

0 commit comments

Comments
 (0)