From ce438341cedd2187173b339fef7196fdfae6d34d Mon Sep 17 00:00:00 2001 From: Brendan Ryan Date: Tue, 24 Mar 2026 08:19:34 -0700 Subject: [PATCH 1/3] feat: add configurable startup timeout pattern for testcontainers --- .changeset/wild-buses-guess.md | 9 +++++ src/Pool.test.ts | 5 +-- src/Server.test.ts | 5 +-- src/instances/alto.test.ts | 3 +- src/instances/tempo.ts | 15 +++++--- src/testcontainers/Instance.ts | 47 +++++++++++++++----------- src/testcontainers/containerOptions.ts | 20 +++++++++++ 7 files changed, 74 insertions(+), 30 deletions(-) create mode 100644 .changeset/wild-buses-guess.md create mode 100644 src/testcontainers/containerOptions.ts diff --git a/.changeset/wild-buses-guess.md b/.changeset/wild-buses-guess.md new file mode 100644 index 0000000..ace166f --- /dev/null +++ b/.changeset/wild-buses-guess.md @@ -0,0 +1,9 @@ +--- +'prool': patch +--- + +Make the testcontainers Tempo instance startup timeout configurable via `startupTimeout`. + +This also introduces a shared internal container-options shape for testcontainers-backed instances, so future container adapters can reuse the same option pattern. + +The default timeout remains `10_000ms`. diff --git a/src/Pool.test.ts b/src/Pool.test.ts index e8c74f8..2618352 100644 --- a/src/Pool.test.ts +++ b/src/Pool.test.ts @@ -6,12 +6,13 @@ import { altoOptions } from '../test/utils.js' let pool: ReturnType const port = await getPort() +const forkUrl = process.env['VITE_FORK_URL'] || 'https://eth.merkle.io' beforeAll(() => Server.create({ instance: Instance.anvil({ chainId: 1, - forkUrl: process.env['VITE_FORK_URL'] ?? 'https://eth.merkle.io', + forkUrl, }), port, }).start(), @@ -40,7 +41,7 @@ describe.each([ expect(pool).toBeDefined() }) - test('start', async () => { + test('start', { timeout: 60_000 }, async () => { pool = Pool.define({ instance, }) diff --git a/src/Server.test.ts b/src/Server.test.ts index fba35eb..87ed1ef 100644 --- a/src/Server.test.ts +++ b/src/Server.test.ts @@ -5,12 +5,13 @@ import { type MessageEvent, WebSocket } from 'ws' import { altoOptions } from '../test/utils.js' const port = await getPort() +const forkUrl = process.env['VITE_FORK_URL'] || 'https://eth.merkle.io' beforeAll(async () => { await Server.create({ instance: Instance.anvil({ chainId: 1, - forkUrl: process.env['VITE_FORK_URL'] ?? 'https://eth.merkle.io', + forkUrl, }), port, }).start() @@ -83,7 +84,7 @@ describe.each([ await stop() }) - test('request: /start + /stop', async () => { + test('request: /start + /stop', { timeout: 60_000 }, async () => { const server = Server.create({ instance, }) diff --git a/src/instances/alto.test.ts b/src/instances/alto.test.ts index a7966ad..35ae1d8 100644 --- a/src/instances/alto.test.ts +++ b/src/instances/alto.test.ts @@ -6,6 +6,7 @@ import { altoOptions } from '../../test/utils.js' const instances: Instance.Instance[] = [] const port = await getPort() +const forkUrl = process.env['VITE_FORK_URL'] || 'https://eth.merkle.io' const defineInstance = (parameters: Partial = {}) => { const instance = Instance.alto({ @@ -18,7 +19,7 @@ const defineInstance = (parameters: Partial = {}) => { beforeAll(() => Instance.anvil({ - forkUrl: process.env['VITE_FORK_URL'] ?? 'https://eth.merkle.io', + forkUrl, port, }).start(), ) diff --git a/src/instances/tempo.ts b/src/instances/tempo.ts index dcdd066..39e5079 100644 --- a/src/instances/tempo.ts +++ b/src/instances/tempo.ts @@ -105,11 +105,16 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => { { ...options, // Resolve when the process is listening via consensus engine message. - resolver({ process, reject, resolve }) { - process.stdout.on('data', (data) => { - const message = data.toString() - if (log) console.log(message) - if (message.includes('Received new payload from consensus engine') || message.includes('Received block from consensus engine')) + resolver({ process, reject, resolve }) { + process.stdout.on('data', (data) => { + const message = data.toString() + if (log) console.log(message) + if ( + message.includes( + 'Received new payload from consensus engine', + ) || + message.includes('Received block from consensus engine') + ) resolve() if (message.includes('shutting down')) reject('shutting down') }) diff --git a/src/testcontainers/Instance.ts b/src/testcontainers/Instance.ts index 78efd6f..a83eb7e 100644 --- a/src/testcontainers/Instance.ts +++ b/src/testcontainers/Instance.ts @@ -6,6 +6,7 @@ import { } from 'testcontainers' import * as Instance from '../Instance.js' import { command, type tempo as core_tempo } from '../instances/tempo.js' +import * as ContainerOptions from './containerOptions.js' export type { Instance, InstanceOptions } from '../Instance.js' @@ -25,6 +26,7 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => { containerName = `tempo.${crypto.randomUUID()}`, image = 'ghcr.io/tempoxyz/tempo:latest', log: log_, + startupTimeout, ...args } = parameters || {} @@ -62,7 +64,9 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => { .withEnvironment({ RUST_LOG }) .withCommand(command({ ...args, port })) .withWaitStrategy( - Wait.forLogMessage(/Received (block|new payload) from consensus engine/), + Wait.forLogMessage( + /Received (block|new payload) from consensus engine/, + ), ) .withLogConsumer((stream) => { stream.on('data', (data) => { @@ -80,7 +84,9 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => { promise.reject(new Error(`Failed to start: ${error.message}`)) }) }) - .withStartupTimeout(10_000) + .withStartupTimeout( + ContainerOptions.resolveStartupTimeout(startupTimeout), + ) c.start() .then((c) => { @@ -100,22 +106,23 @@ export const tempo = Instance.define((parameters?: tempo.Parameters) => { }) export declare namespace tempo { - export type Parameters = Omit & { - /** - * Name of the container. - */ - containerName?: string | undefined - /** - * Docker image to use. - */ - image?: string | undefined - /** - * Host the server will listen on. - */ - host?: string | undefined - /** - * Port the server will listen on. - */ - port?: number | undefined - } + export type Parameters = Omit & + ContainerOptions.Parameters & { + /** + * Name of the container. + */ + containerName?: string | undefined + /** + * Docker image to use. + */ + image?: string | undefined + /** + * Host the server will listen on. + */ + host?: string | undefined + /** + * Port the server will listen on. + */ + port?: number | undefined + } } diff --git a/src/testcontainers/containerOptions.ts b/src/testcontainers/containerOptions.ts new file mode 100644 index 0000000..7a7a41e --- /dev/null +++ b/src/testcontainers/containerOptions.ts @@ -0,0 +1,20 @@ +export const defaults = { + startupTimeout: 10_000, +} as const + +export type Parameters = { + /** + * Startup timeout (in milliseconds) for testcontainers readiness checks. + * + * Increase this when running in CI or environments with slow image pulls. + * + * @default 10_000 + */ + startupTimeout?: number | undefined +} + +export function resolveStartupTimeout( + startupTimeout: number | undefined, +): number { + return startupTimeout ?? defaults.startupTimeout +} From 0f6b05cbf94270f90e15ab18fdaf3f9101acfa4b Mon Sep 17 00:00:00 2001 From: Brendan Ryan Date: Tue, 24 Mar 2026 08:37:19 -0700 Subject: [PATCH 2/3] ci: stabilize verify test runtime for slow startups --- .github/workflows/verify.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 48733f5..ec20280 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -37,7 +37,10 @@ jobs: - name: Set up Docker uses: docker/setup-docker-action@v4 + - name: Pre-pull Tempo image + run: docker pull ghcr.io/tempoxyz/tempo:latest + - name: Run tests - run: pnpm test --bail=1 + run: pnpm test --bail=1 --testTimeout=120000 --hookTimeout=120000 env: VITE_FORK_URL: ${{ secrets.VITE_FORK_URL }} From 8423134a311e059c79d5471579f3f3fd5661c1c6 Mon Sep 17 00:00:00 2001 From: Brendan Ryan Date: Tue, 24 Mar 2026 08:38:54 -0700 Subject: [PATCH 3/3] ci: run vitest directly with extended time budgets --- .github/workflows/verify.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index ec20280..63d33d5 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -41,6 +41,6 @@ jobs: run: docker pull ghcr.io/tempoxyz/tempo:latest - name: Run tests - run: pnpm test --bail=1 --testTimeout=120000 --hookTimeout=120000 + run: pnpm exec vitest --bail=1 --testTimeout=120000 --hookTimeout=120000 env: VITE_FORK_URL: ${{ secrets.VITE_FORK_URL }}