From 44d11ccb73d6828f51828880ce61510194907acd Mon Sep 17 00:00:00 2001 From: Reflex Date: Fri, 7 Aug 2026 23:10:25 +0000 Subject: [PATCH] fix(smoketests): align blueprint test timeouts with long-poll budgets The blueprint smoketests wrapped 20-30 minute long-poll budgets in a 120s jest timeout, so any build slower than 120s aborted the test before the SDK could report why, and left the blueprint behind because the timed-out call never returned an id for the cleanup path to use. Cap the long-poll budgets at MEDIUM_TIMEOUT inside a LONG_TIMEOUT jest budget, and clean blueprints up by name so cleanup runs even when the await times out. --- tests/smoketests/blueprints.test.ts | 28 ++++++++++++---------------- tests/smoketests/utils.ts | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/tests/smoketests/blueprints.test.ts b/tests/smoketests/blueprints.test.ts index 0b73f4685..86e56fea9 100644 --- a/tests/smoketests/blueprints.test.ts +++ b/tests/smoketests/blueprints.test.ts @@ -1,5 +1,4 @@ -import { BlueprintView } from '@runloop/api-client/resources/blueprints'; -import { makeClient, SHORT_TIMEOUT, uniqueName } from './utils'; +import { cleanUpBlueprintsByName, LONG_TIMEOUT, makeClient, MEDIUM_TIMEOUT, uniqueName } from './utils'; import { DevboxView } from '@runloop/api-client/resources/devboxes'; const client = makeClient(); @@ -13,7 +12,7 @@ describe('smoketest: blueprints', () => { let blueprintName = uniqueName('bp'); afterAll(async () => { - await client.blueprints.delete(blueprintId!); + await cleanUpBlueprintsByName(client, blueprintName); }); test( @@ -24,13 +23,13 @@ describe('smoketest: blueprints', () => { name: blueprintName, }, { - longPoll: { timeoutMs: 30 * 60 * 1000 }, + longPoll: { timeoutMs: MEDIUM_TIMEOUT }, }, ); expect(created.status).toBe('build_complete'); blueprintId = created.id; }, - SHORT_TIMEOUT, + LONG_TIMEOUT, ); test( @@ -44,7 +43,7 @@ describe('smoketest: blueprints', () => { launch_parameters: { resource_size_request: 'X_SMALL', keep_alive_time_seconds: 60 * 5 }, // 5 minutes }, { - longPoll: { timeoutMs: 20 * 60 * 1000 }, + longPoll: { timeoutMs: MEDIUM_TIMEOUT }, }, ); expect(devbox.blueprint_id).toBe(blueprintId); @@ -54,7 +53,7 @@ describe('smoketest: blueprints', () => { } } }, - SHORT_TIMEOUT, + LONG_TIMEOUT, ); test( @@ -68,7 +67,7 @@ describe('smoketest: blueprints', () => { launch_parameters: { resource_size_request: 'X_SMALL', keep_alive_time_seconds: 60 * 5 }, // 5 minutes }, { - longPoll: { timeoutMs: 20 * 60 * 1000 }, + longPoll: { timeoutMs: MEDIUM_TIMEOUT }, }, ); expect(devbox.blueprint_id).toBeTruthy(); @@ -78,7 +77,7 @@ describe('smoketest: blueprints', () => { } } }, - SHORT_TIMEOUT, + LONG_TIMEOUT, ); }); @@ -89,9 +88,8 @@ describe('smoketest: blueprints', () => { test.concurrent( 'create blueprint with secret in Dockerfile and await build', async () => { - let bpt: BlueprintView | undefined; try { - bpt = await client.blueprints.createAndAwaitBuildCompleted( + const bpt = await client.blueprints.createAndAwaitBuildCompleted( { name: secretsBlueprintName, dockerfile: @@ -101,19 +99,17 @@ describe('smoketest: blueprints', () => { }, }, { - longPoll: { timeoutMs: 30 * 60 * 1000 }, + longPoll: { timeoutMs: MEDIUM_TIMEOUT }, }, ); expect(bpt.status).toBe('build_complete'); expect(bpt.parameters.secrets?.['GITHUB_TOKEN']).toBe('GITHUB_TOKEN_FOR_SMOKETESTS'); } finally { - if (bpt) { - await client.blueprints.delete(bpt.id); - } + await cleanUpBlueprintsByName(client, secretsBlueprintName); } }, - SHORT_TIMEOUT, + LONG_TIMEOUT, ); }); }); diff --git a/tests/smoketests/utils.ts b/tests/smoketests/utils.ts index d58b7cf5e..df57f3e89 100644 --- a/tests/smoketests/utils.ts +++ b/tests/smoketests/utils.ts @@ -39,6 +39,22 @@ export const SHORT_TIMEOUT = 120_000; export const MEDIUM_TIMEOUT = 300_000; export const LONG_TIMEOUT = 600_000; +/** + * Helper to clean up every blueprint with the given name, ignoring errors if + * already deleted. Looks blueprints up by name rather than taking an ID so that + * cleanup still runs when a create-and-await call timed out before returning one. + */ +export async function cleanUpBlueprintsByName(client: Runloop, name: string): Promise { + try { + const page = await client.blueprints.list({ name }); + for (const blueprint of page.blueprints) { + await client.blueprints.delete(blueprint.id); + } + } catch { + // Already deleted or never created, ignore + } +} + /** * Helper to clean up a network policy, ignoring errors if already deleted. */