diff --git a/package.json b/package.json index 64fbd9f6..78da8f65 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build-wasm": "yarn workspace @acala-network/chopsticks-executor run build", "build:web-test": "yarn build && yarn workspace web-test run build", "check": "cd executor && cargo check --locked", - "test": "vitest run", + "test": "LOG_LEVEL=warn vitest run", "test:watch": "vitest", "coverage": "vitest run --coverage", "coverage:ui": "vitest test --ui --coverage.enabled=true", diff --git a/packages/core/src/blockchain/head-state.ts b/packages/core/src/blockchain/head-state.ts index 815f4c4c..df8989e7 100644 --- a/packages/core/src/blockchain/head-state.ts +++ b/packages/core/src/blockchain/head-state.ts @@ -1,5 +1,3 @@ -import { stringToHex } from '@polkadot/util' - import { Block } from './block.js' import { defaultLogger } from '../logger.js' @@ -45,18 +43,6 @@ export class HeadState { delete this.#storageListeners[id] } - async subscrubeRuntimeVersion(cb: (block: Block) => void) { - const id = randomId() - const codeKey = stringToHex(':code') - this.#storageListeners[id] = [[codeKey], cb] - this.#oldValues[codeKey] = await this.#head.get(codeKey).then((val) => val || null) - return id - } - - unsubscribeRuntimeVersion(id: string) { - delete this.#storageListeners[id] - } - async setHead(head: Block) { this.#head = head diff --git a/packages/core/src/rpc/substrate/state.ts b/packages/core/src/rpc/substrate/state.ts index 2f1ce230..e3786e6d 100644 --- a/packages/core/src/rpc/substrate/state.ts +++ b/packages/core/src/rpc/substrate/state.ts @@ -1,6 +1,7 @@ -import { Block } from '../../blockchain/block.js' import { HexString } from '@polkadot/util/types' +import { stringToHex } from '@polkadot/util' +import { Block } from '../../blockchain/block.js' import { Handler, ResponseError } from '../shared.js' import { RuntimeVersion } from '../../wasm-executor/index.js' import { defaultLogger } from '../../logger.js' @@ -110,12 +111,15 @@ export const state_call: Handler<[HexString, HexString, HexString], HexString> = */ export const state_subscribeRuntimeVersion: Handler<[], string> = async (context, _params, { subscribe }) => { let update = (_block: Block) => {} - const id = await context.chain.headState.subscrubeRuntimeVersion((block) => update(block)) - const callback = subscribe('state_runtimeVersion', id) + + const id = await context.chain.headState.subscribeStorage([stringToHex(':code')], (block) => update(block)) + const callback = subscribe('state_runtimeVersion', id, () => context.chain.headState.unsubscribeStorage(id)) + update = async (block) => callback(await block.runtimeVersion) - setTimeout(() => { - context.chain.head.runtimeVersion.then(callback) - }, 50) + ;(async () => { + update(context.chain.head) + })() + return id } diff --git a/packages/e2e/src/author.test.ts b/packages/e2e/src/author.test.ts index 63e5ac05..9d6f9d92 100644 --- a/packages/e2e/src/author.test.ts +++ b/packages/e2e/src/author.test.ts @@ -27,10 +27,10 @@ describe('author rpc', async () => { it('works', async () => { { const { callback, next } = mockCallback() + const tick = next() await api.tx.balances.transfer(bob.address, 100).signAndSend(alice, callback) await dev.newBlock() - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() @@ -42,10 +42,10 @@ describe('author rpc', async () => { { const { callback, next } = mockCallback() + const tick = next() await api.tx.balances.transfer(bob.address, 200).signAndSend(alice, callback) await dev.newBlock() - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() @@ -57,10 +57,10 @@ describe('author rpc', async () => { { const { callback, next } = mockCallback() + const tick = next() await api.tx.balances.transfer(bob.address, 300).signAndSend(alice, callback) await dev.newBlock() - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() diff --git a/packages/e2e/src/chain.test.ts b/packages/e2e/src/chain.test.ts index a6deef26..afd487ba 100644 --- a/packages/e2e/src/chain.test.ts +++ b/packages/e2e/src/chain.test.ts @@ -66,16 +66,17 @@ describe('chain rpc', () => { it('subscribeNewHeads', async () => { const { callback, next } = mockCallback() + let tick = next() const unsub = await api.rpc.chain.subscribeNewHeads(callback) + await tick - await next() expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() + tick = next() expect(await dev.newBlock()).toMatchSnapshot() - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() diff --git a/packages/e2e/src/helper.ts b/packages/e2e/src/helper.ts index 3ee73422..56383976 100644 --- a/packages/e2e/src/helper.ts +++ b/packages/e2e/src/helper.ts @@ -169,10 +169,8 @@ export const dev = { export const mockCallback = () => { let next = defer() const callback = vi.fn((...args) => { - delay(100).then(() => { - next.resolve(args) - next = defer() - }) + next.resolve(args) + next = defer() }) return { diff --git a/packages/e2e/src/import-storage/index.test.ts b/packages/e2e/src/import-storage/index.test.ts index 84879de7..d0b43a7c 100644 --- a/packages/e2e/src/import-storage/index.test.ts +++ b/packages/e2e/src/import-storage/index.test.ts @@ -41,6 +41,8 @@ describe('import-storage', () => { await expect(overrideStorage(chain, path.join(__dirname, 'storage.error.storage.yml'))).rejects.toThrowError( 'Cannot find storage MMembers in pallet TechnicalCommittee', ) + // fixes api runtime disconnect warning + await new Promise((r) => setTimeout(r, 50)) }) it('wasm override works', async () => { diff --git a/packages/e2e/src/resume.test.ts b/packages/e2e/src/resume.test.ts index 52bfd6ef..07bb49e5 100644 --- a/packages/e2e/src/resume.test.ts +++ b/packages/e2e/src/resume.test.ts @@ -52,6 +52,8 @@ describe('resume', async () => { const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) expect(loadedHead?.hash).toEqual(savedHeadHash) + // fixes api runtime disconnect warning + await new Promise((r) => setTimeout(r, 50)) await newAcala.teardown() }) @@ -63,6 +65,8 @@ describe('resume', async () => { const newHeadNumber = newAcala.chain.head.number expect(newHeadNumber).toEqual(3000001) + // fixes api runtime disconnect warning + await new Promise((r) => setTimeout(r, 50)) await newAcala.teardown() }) @@ -75,6 +79,8 @@ describe('resume', async () => { const loadedHead = await newAcala.chain.getBlockAt(newHeadNumber) expect(loadedHead?.hash).toEqual(savedHeadHash) + // fixes api runtime disconnect warning + await new Promise((r) => setTimeout(r, 50)) await newAcala.teardown() }) @@ -96,6 +102,9 @@ describe('resume', async () => { expect(loadedAcalaHead.hash).toEqual(savedAcalaHash) expect(loadedPolkadotHead.hash).toEqual(savedPolkadotHash) + // fixes api runtime disconnect warning + await new Promise((r) => setTimeout(r, 50)) + await acala.teardown() await polkadot.teardown() }) diff --git a/packages/e2e/src/state.test.ts b/packages/e2e/src/state.test.ts index e266cbc2..9a215c88 100644 --- a/packages/e2e/src/state.test.ts +++ b/packages/e2e/src/state.test.ts @@ -1,6 +1,9 @@ import { describe, expect, it } from 'vitest' +import { readFileSync } from 'node:fs' +import path from 'node:path' -import { api, check, checkHex, env, setupApi } from './helper.js' +import { api, check, checkHex, env, mockCallback, setupApi, testingPairs } from './helper.js' +import networks from './networks.js' setupApi(env.acala) @@ -12,5 +15,170 @@ describe('state rpc', () => { expect(await api.rpc.state.getMetadata(genesisHash)).to.not.be.eq(await api.rpc.state.getMetadata()) }) - it.todo('subscribeRuntimeVersion') + it('subscribeRuntimeVersion', async () => { + const { api, dev, teardown } = await networks.acala({ + blockNumber: 2000000, + }) + + const { alice } = testingPairs() + + await dev.setStorage({ + Sudo: { + Key: alice.address, + }, + System: { + Account: [[[alice.address], { data: { free: 1000 * 1e12 } }]], + }, + }) + + const { callback, next } = mockCallback() + + const currentVersion = next() + const unsub = await api.rpc.state.subscribeRuntimeVersion((version) => callback(version.toHuman())) + expect(await currentVersion).toMatchInlineSnapshot(` + [ + { + "apis": [ + [ + "0xdf6acb689907609b", + "4", + ], + [ + "0x37e397fc7c91f5e4", + "1", + ], + [ + "0x40fe3ad401f8959a", + "6", + ], + [ + "0xd2bc9897eed08f15", + "3", + ], + [ + "0xf78b278be53f454c", + "2", + ], + [ + "0xdd718d5cc53262d4", + "1", + ], + [ + "0xab3c0572291feb8b", + "1", + ], + [ + "0xbc9d89904f5b923f", + "1", + ], + [ + "0x37c8bb1350a9a2a8", + "1", + ], + [ + "0x6ef953004ba30e59", + "1", + ], + [ + "0x955e168e0cfb3409", + "1", + ], + [ + "0xe3df3f2aa8a5cc57", + "2", + ], + [ + "0xea93e3f16f3d6962", + "2", + ], + ], + "authoringVersion": "1", + "implName": "acala", + "implVersion": "0", + "specName": "acala", + "specVersion": "2,096", + "stateVersion": "0", + "transactionVersion": "1", + }, + ] + `) + + const newVersion = next() + + const runtime = readFileSync(path.join(__dirname, '../blobs/acala-runtime-2101.txt')).toString().trim() + await api.tx.sudo.sudoUncheckedWeight(api.tx.system.setCode(runtime), '0').signAndSend(alice) + await dev.newBlock({ count: 3 }) + + expect(await newVersion).toMatchInlineSnapshot(` + [ + { + "apis": [ + [ + "0xdf6acb689907609b", + "4", + ], + [ + "0x37e397fc7c91f5e4", + "1", + ], + [ + "0x40fe3ad401f8959a", + "6", + ], + [ + "0xd2bc9897eed08f15", + "3", + ], + [ + "0xf78b278be53f454c", + "2", + ], + [ + "0xdd718d5cc53262d4", + "1", + ], + [ + "0xab3c0572291feb8b", + "1", + ], + [ + "0xbc9d89904f5b923f", + "1", + ], + [ + "0x37c8bb1350a9a2a8", + "1", + ], + [ + "0x6ef953004ba30e59", + "1", + ], + [ + "0x955e168e0cfb3409", + "1", + ], + [ + "0xe3df3f2aa8a5cc57", + "2", + ], + [ + "0xea93e3f16f3d6962", + "2", + ], + ], + "authoringVersion": "1", + "implName": "acala", + "implVersion": "0", + "specName": "acala", + "specVersion": "2,101", + "stateVersion": "0", + "transactionVersion": "1", + }, + ] + `) + + unsub() + + await teardown() + }) }) diff --git a/packages/e2e/src/storage.test.ts b/packages/e2e/src/storage.test.ts index 8d9e9142..a377db3f 100644 --- a/packages/e2e/src/storage.test.ts +++ b/packages/e2e/src/storage.test.ts @@ -65,18 +65,18 @@ describe('storage', () => { it('subscription', async () => { const { callback, next } = mockCallback() + let tick = next() const unsub = await api.query.timestamp.now(callback) - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() + tick = next() expect(await dev.newBlock()).toMatchInlineSnapshot( '"0xa08ebd83c5b4d941bf9c6853c0af2bc6620a1878a5d2ce302d09f40feea8ef98"', ) - - await next() + await tick expect(callback.mock.calls).toMatchSnapshot() callback.mockClear() diff --git a/vitest.config.mts b/vitest.config.mts index 72dbe9f4..c2b11608 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -13,6 +13,7 @@ export default defineConfig({ include: ['packages/chopsticks/**/*.ts', 'packages/core/**/*.ts'], reporter: ['text', 'json-summary', 'json', 'html'], }, + reporters: process.env.GITHUB_ACTIONS ? ['basic', 'github-actions'] : ['verbose', 'hanging-process'], }, plugins: [swc.vite(), tsconfigPaths()], })