@novasamatech/host-container 0.7.7 → 0.7.8-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -130,6 +130,73 @@ describe('chainConnectionManager', () => {
|
|
|
130
130
|
mock.simulateMessage({ jsonrpc: '2.0', id: followCalls[1].id, result: 'sub-id-2' });
|
|
131
131
|
expect(manager.hasActiveFollow(GENESIS)).toBe(true);
|
|
132
132
|
});
|
|
133
|
+
it('routes follow events to the new listener after stop+refollow (regression: best blocks must keep flowing)', () => {
|
|
134
|
+
// Reproduces the user-reported scenario: WS reconnect produces a Stop,
|
|
135
|
+
// the papp refollows, and subsequent chain events (notably
|
|
136
|
+
// bestBlockChanged) must reach the NEW listener under the NEW chainSubId.
|
|
137
|
+
const mock = createMockProvider();
|
|
138
|
+
const manager = createChainConnectionManager(() => mock.provider);
|
|
139
|
+
const firstEvents = [];
|
|
140
|
+
const secondEvents = [];
|
|
141
|
+
manager.getOrCreateChain(GENESIS);
|
|
142
|
+
manager.startFollow(GENESIS, true, e => firstEvents.push(e));
|
|
143
|
+
mock.simulateMessage({ jsonrpc: '2.0', id: findCallId(mock, 'chainHead_v1_follow'), result: 'sub-id-1' });
|
|
144
|
+
// Sanity: events on the first follow reach the first listener.
|
|
145
|
+
mock.simulateMessage({
|
|
146
|
+
jsonrpc: '2.0',
|
|
147
|
+
method: 'chainHead_v1_followEvent',
|
|
148
|
+
params: { subscription: 'sub-id-1', result: { event: 'bestBlockChanged', bestBlockHash: '0xaa' } },
|
|
149
|
+
});
|
|
150
|
+
expect(firstEvents).toContainEqual({ event: 'bestBlockChanged', bestBlockHash: '0xaa' });
|
|
151
|
+
// Stop kills the first follow.
|
|
152
|
+
mock.simulateMessage({
|
|
153
|
+
jsonrpc: '2.0',
|
|
154
|
+
method: 'chainHead_v1_followEvent',
|
|
155
|
+
params: { subscription: 'sub-id-1', result: { event: 'stop' } },
|
|
156
|
+
});
|
|
157
|
+
// Papp refollows with a fresh listener.
|
|
158
|
+
manager.startFollow(GENESIS, true, e => secondEvents.push(e));
|
|
159
|
+
const followCalls = sentMessages(mock).filter(m => m.method === 'chainHead_v1_follow');
|
|
160
|
+
expect(followCalls).toHaveLength(2);
|
|
161
|
+
mock.simulateMessage({ jsonrpc: '2.0', id: followCalls[1].id, result: 'sub-id-2' });
|
|
162
|
+
// Server now streams events under the NEW chainSubId.
|
|
163
|
+
mock.simulateMessage({
|
|
164
|
+
jsonrpc: '2.0',
|
|
165
|
+
method: 'chainHead_v1_followEvent',
|
|
166
|
+
params: {
|
|
167
|
+
subscription: 'sub-id-2',
|
|
168
|
+
result: {
|
|
169
|
+
event: 'initialized',
|
|
170
|
+
finalizedBlockHashes: ['0xbb'],
|
|
171
|
+
finalizedBlockRuntime: {
|
|
172
|
+
type: 'valid',
|
|
173
|
+
spec: { specName: 'x', implName: 'y', specVersion: 1, implVersion: 1, apis: {} },
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
mock.simulateMessage({
|
|
179
|
+
jsonrpc: '2.0',
|
|
180
|
+
method: 'chainHead_v1_followEvent',
|
|
181
|
+
params: {
|
|
182
|
+
subscription: 'sub-id-2',
|
|
183
|
+
result: { event: 'newBlock', blockHash: '0xcc', parentBlockHash: '0xbb' },
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
mock.simulateMessage({
|
|
187
|
+
jsonrpc: '2.0',
|
|
188
|
+
method: 'chainHead_v1_followEvent',
|
|
189
|
+
params: { subscription: 'sub-id-2', result: { event: 'bestBlockChanged', bestBlockHash: '0xcc' } },
|
|
190
|
+
});
|
|
191
|
+
// The new listener must receive the new follow's events.
|
|
192
|
+
expect(secondEvents).toContainEqual(expect.objectContaining({ event: 'initialized' }));
|
|
193
|
+
expect(secondEvents).toContainEqual({ event: 'newBlock', blockHash: '0xcc', parentBlockHash: '0xbb' });
|
|
194
|
+
expect(secondEvents).toContainEqual({ event: 'bestBlockChanged', bestBlockHash: '0xcc' });
|
|
195
|
+
// Old listener must NOT receive events from the new follow — leaking
|
|
196
|
+
// sub-id-2 events to the dead first listener would mean substrate-client
|
|
197
|
+
// never cleared the old subscription handle.
|
|
198
|
+
expect(firstEvents).not.toContainEqual({ event: 'bestBlockChanged', bestBlockHash: '0xcc' });
|
|
199
|
+
});
|
|
133
200
|
});
|
|
134
201
|
describe('chainHeadOp', () => {
|
|
135
202
|
it('rejects with "No active follow for this chain" when no follow is active', async () => {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-container",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.7.
|
|
4
|
+
"version": "0.7.8-0",
|
|
5
5
|
"description": "Host container for hosting and managing products within the Polkadot ecosystem.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@noble/hashes": "2.2.0",
|
|
29
29
|
"polkadot-api": ">=2",
|
|
30
30
|
"@polkadot-api/substrate-client": "^0.7.0",
|
|
31
|
-
"@novasamatech/host-api": "0.7.
|
|
31
|
+
"@novasamatech/host-api": "0.7.8-0",
|
|
32
32
|
"nanoid": "5.1.9",
|
|
33
33
|
"neverthrow": "^8.2.0"
|
|
34
34
|
},
|