From 4a63bb468a438aae338fddb422f1c872b525048e Mon Sep 17 00:00:00 2001 From: Qiwei Yang Date: Wed, 31 Jan 2024 00:21:07 -0800 Subject: [PATCH] tests: add test for options parsing (#651) * tests: add test for options parsing * fix: remove snapshot and test '4500000' * chore: update dev container * tests: improve setup block test * fix: wording * fix: lint * fix: tests --- .devcontainer.json | 3 - .devcontainer/devcontainer.json | 26 ++++++++ .github/dependabot.yml | 11 ++-- packages/chopsticks/src/schema/index.ts | 15 ++++- packages/chopsticks/src/schema/parse.test.ts | 69 ++++++++++++++++++++ packages/core/src/setup.test.ts | 28 +++++++- packages/core/src/setup.ts | 28 ++++---- 7 files changed, 157 insertions(+), 23 deletions(-) delete mode 100644 .devcontainer.json create mode 100644 .devcontainer/devcontainer.json create mode 100644 packages/chopsticks/src/schema/parse.test.ts diff --git a/.devcontainer.json b/.devcontainer.json deleted file mode 100644 index 7313a52e..00000000 --- a/.devcontainer.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "postCreateCommand": "git submodule update --init" -} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..9f9bddf2 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,26 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node +{ + "name": "Node.js & TypeScript", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye", + "features": { + "ghcr.io/devcontainers/features/rust:1": {}, + "ghcr.io/lee-orr/rusty-dev-containers/wasm32-unknown-unknown:0": {}, + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "yarn install", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f597142a..f33a02cd 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,12 @@ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: +# Please see the documentation for more information: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot version: 2 updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "monthly" + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/packages/chopsticks/src/schema/index.ts b/packages/chopsticks/src/schema/index.ts index 0a6e06a5..2c68ed47 100644 --- a/packages/chopsticks/src/schema/index.ts +++ b/packages/chopsticks/src/schema/index.ts @@ -14,9 +14,18 @@ export const configSchema = z.object({ port: z.number({ description: 'Port to listen on' }).default(8000), endpoint: z.union([z.string(), z.array(z.string())], { description: 'Endpoint to connect to' }).optional(), block: z - .union([z.string(), z.number(), z.null()], { - description: 'Block hash or block number. Default to latest block', - }) + .union( + [ + z.string(), + z + .number() + .max(Number.MAX_SAFE_INTEGER, 'Number is too big, please make it a string if you are uing a hex string'), + z.null(), + ], + { + description: 'Block hash or block number. Default to latest block', + }, + ) .optional(), 'build-block-mode': z.nativeEnum(BuildBlockMode).default(BuildBlockMode.Batch), 'import-storage': z.any({ description: 'Pre-defined JSON/YAML storage file path' }).optional(), diff --git a/packages/chopsticks/src/schema/parse.test.ts b/packages/chopsticks/src/schema/parse.test.ts new file mode 100644 index 00000000..244f2b4a --- /dev/null +++ b/packages/chopsticks/src/schema/parse.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest' +import { readdirSync } from 'fs' +import _ from 'lodash' +import path from 'path' + +import { configSchema, fetchConfig } from './index.js' + +function getAllFiles(dirPath: string) { + const files = readdirSync(dirPath) + const arrayOfFiles: string[] = [] + files.forEach(function (file) { + arrayOfFiles.push(path.join(dirPath, '/', file)) + }) + + return arrayOfFiles +} + +describe('Existing configs', async () => { + const configPaths = getAllFiles(path.join(__dirname, '../../../../configs')) + it.each(configPaths.map((p) => ({ name: _.last(p.split('/')), path: p })))( + '$name config parsing is correct', + async ({ path }) => { + const config = await fetchConfig(path) + expect(() => configSchema.parse(config)).not.toThrow() + }, + ) +}) + +describe('Parsed options', () => { + const defaults = { + port: 8000, + 'build-block-mode': 'Batch', + } + it('parsed multi type options should work', () => { + expect( + configSchema.parse({ + block: 4500000, + }), + ).toEqual({ + block: 4500000, + ...defaults, + }) + + expect( + configSchema.parse({ + block: '4500000', + }), + ).toEqual({ + block: '4500000', + ...defaults, + }) + + expect( + configSchema.parse({ + block: '0xb10f03bbc183da4d26e27528d28f6a73ddaf182fb6400ca363b77d2411ea5b0c', + }), + ).toEqual({ + block: '0xb10f03bbc183da4d26e27528d28f6a73ddaf182fb6400ca363b77d2411ea5b0c', + ...defaults, + }) + + expect(() => + configSchema.parse({ + // eslint-disable-next-line @typescript-eslint/no-loss-of-precision + block: 0xb10f03bbc183da4d26e27528d28f6a73ddaf182fb6400ca363b77d2411ea5b0c, + }), + ).toThrowError(/you are uing a hex string/) + }) +}) diff --git a/packages/core/src/setup.test.ts b/packages/core/src/setup.test.ts index 53430635..51f90580 100644 --- a/packages/core/src/setup.test.ts +++ b/packages/core/src/setup.test.ts @@ -1,5 +1,5 @@ import { expect, test } from 'vitest' -import { setup } from './setup.js' +import { processOptions, setup } from './setup.js' test('handle invalid block ', async () => { await expect(setup({ endpoint: 'wss://acala-rpc.aca-api.network', block: '0x' })).rejects.toThrow('invalid length') @@ -13,3 +13,29 @@ test('handle invalid block ', async () => { }), ).rejects.toThrow('Cannot find header for 0xc87ae632b2cc4583a37659785f5098947acfdc6a36dbb07abcfa6ad694f97c5d') }) + +test('block option type processing is correct', async () => { + const nullBlock = await processOptions({ + endpoint: 'wss://acala-rpc.aca-api.network', + }) + expect(nullBlock.block).toBeUndefined() + expect(nullBlock.blockHash).toBeDefined() + + const hexStringBlock = await processOptions({ + endpoint: 'wss://acala-rpc.aca-api.network', + block: '0x3a9a2d71537ceedff1a3895d68456f4a870bb89ab649fd47c6cf9c4f9731d580', + }) + expect(hexStringBlock.blockHash).toBe('0x3a9a2d71537ceedff1a3895d68456f4a870bb89ab649fd47c6cf9c4f9731d580') + + const hexNumberBlock = await processOptions({ + endpoint: 'wss://acala-rpc.aca-api.network', + block: 0x44aa20, + }) + expect(hexNumberBlock.block).toBe(4500000) + + const integerBlock = await processOptions({ + endpoint: 'wss://acala-rpc.aca-api.network', + block: 4500000, + }) + expect(integerBlock.block).toBe(4500000) +}) diff --git a/packages/core/src/setup.ts b/packages/core/src/setup.ts index 849bc185..caa43650 100644 --- a/packages/core/src/setup.ts +++ b/packages/core/src/setup.ts @@ -67,7 +67,7 @@ export const genesisSetup = async (chain: Blockchain, genesis: GenesisProvider) await chain.newBlock() } -export const setup = async (options: SetupOptions) => { +export const processOptions = async (options: SetupOptions) => { defaultLogger.debug(options, 'Setup options') let provider: ProviderInterface @@ -105,6 +105,12 @@ export const setup = async (options: SetupOptions) => { defaultLogger.debug({ ...options, blockHash }, 'Args') + return { ...options, blockHash, api } +} + +export const setup = async (options: SetupOptions) => { + const { api, blockHash, ...opts } = await processOptions(options) + const header = await api.getHeader(blockHash) if (!header) { throw new Error(`Cannot find header for ${blockHash}`) @@ -112,23 +118,23 @@ export const setup = async (options: SetupOptions) => { const chain = new Blockchain({ api, - buildBlockMode: options.buildBlockMode, + buildBlockMode: opts.buildBlockMode, inherentProviders, - db: options.db, + db: opts.db, header: { hash: blockHash as HexString, number: Number(header.number), }, - mockSignatureHost: options.mockSignatureHost, - allowUnresolvedImports: options.allowUnresolvedImports, - runtimeLogLevel: options.runtimeLogLevel, - registeredTypes: options.registeredTypes || {}, - offchainWorker: options.offchainWorker, - maxMemoryBlockCount: options.maxMemoryBlockCount, + mockSignatureHost: opts.mockSignatureHost, + allowUnresolvedImports: opts.allowUnresolvedImports, + runtimeLogLevel: opts.runtimeLogLevel, + registeredTypes: opts.registeredTypes || {}, + offchainWorker: opts.offchainWorker, + maxMemoryBlockCount: opts.maxMemoryBlockCount, }) - if (options.genesis) { - await genesisSetup(chain, options.genesis) + if (opts.genesis) { + await genesisSetup(chain, opts.genesis) } return chain