From 1769c8f024f3d29fad58f0ec98e62ba4a4875ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Volf?= Date: Tue, 10 Nov 2026 15:15:42 +0100 Subject: [PATCH 1/2] fix: use run token in metamorph test, and metamorphing from outside is not allowed anymore --- test/integration/_fixtures.ts | 6 ++-- test/integration/run.test.ts | 65 ++++++++++++++++++++++++++--------- 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/test/integration/_fixtures.ts b/test/integration/_fixtures.ts index 064759e39..237be09fe 100644 --- a/test/integration/_fixtures.ts +++ b/test/integration/_fixtures.ts @@ -6,9 +6,9 @@ const TOKEN_ENV_VAR = 'APIFY_TEST_USER_API_TOKEN'; /** Overrides the API the suite runs against, so it can be pointed at a staging deployment. */ const API_URL_ENV_VAR = 'APIFY_INTEGRATION_TESTS_API_URL'; -/** Client authenticated as the primary test user. */ -export function makeClient(): ApifyClient { - const token = process.env[TOKEN_ENV_VAR]; +/** Client authenticated as the primary test user, or with the given `token` instead. */ +export function makeClient(token?: string): ApifyClient { + token ??= process.env[TOKEN_ENV_VAR] if (!token) { throw new Error(`${TOKEN_ENV_VAR} environment variable is missing, cannot run tests!`); } diff --git a/test/integration/run.test.ts b/test/integration/run.test.ts index 0a1f54ad1..62203c0bf 100644 --- a/test/integration/run.test.ts +++ b/test/integration/run.test.ts @@ -240,26 +240,59 @@ test('resurrect() restarts a finished run, which then succeeds again', async () }); test('metamorph() transforms a started run into another Actor, keeping the run id', async () => { - const run = await client.actor(HELLO_WORLD_ACTOR).start(); - const runClient = client.run(run.id); + // Metamorphing the run from outside is not allowed, so we cheat a bit: the Actor stores its own run token in its key-value store, + // and we construct a new client with this token, and test that. + const actor = await client.actors().create({ + name: getRandomResourceName('actor'), + versions: [ + { + versionNumber: '0.0', + sourceType: ActorSourceType.SourceFiles, + buildTag: 'latest', + sourceFiles: [ + { + name: 'Dockerfile', + format: 'TEXT', + content: 'FROM apify/actor-node:22\nCOPY . ./\nCMD ["node", "main.js"]\n', + }, + { + name: 'main.js', + format: 'TEXT', + content: ` + const { Actor } = require('apify'); + Actor.main(async () => { + await Actor.setValue('TOKEN', process.env.APIFY_TOKEN, { contentType: 'text/plain' }); + await new Promise((resolve) => setTimeout(resolve, 60000)); + }); +`, + }, + ], + }, + ], + }); + const actorClient = client.actor(actor.id); + let runClient: RunClient | undefined; try { - await waitUntilStarted(runClient); - - try { - const metamorphedRun = await runClient.metamorph(HELLO_WORLD_ACTOR, { - message: 'Hello from metamorph!', - }); - expect(metamorphedRun.id).toBe(run.id); + await client.build((await actorClient.build('0.0')).id).waitForFinish(); + const run = await actorClient.start(); + runClient = client.run(run.id); + + // Only the run's own API token may metamorph it, so take it from the Actor's key-value store. + const record = await pollUntilCondition( + async () => client.keyValueStore(run.defaultKeyValueStoreId).getRecord('TOKEN'), + (rec) => rec !== undefined, + { timeoutSecs: 120, backoffFactor: 2 }, + ); + const runTokenClient = makeClient(record!.value as string); - await runClient.waitForFinish(); - } catch (err) { - // hello-world can finish before the metamorph lands; the API call was still exercised. - rethrowUnlessAlreadyFinished(err); - } + const metamorphedRun = await runTokenClient.run(run.id).metamorph(HELLO_WORLD_ACTOR); + expect(metamorphedRun.id).toBe(run.id); } finally { - await runClient.waitForFinish(); - await runClient.delete(); + await runClient?.abort(); + await runClient?.waitForFinish(); + await runClient?.delete(); + await actorClient.delete(); } }); From a2d32a881405a4f51a361a0e0d9e9dfa8b7e102d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Volf?= Date: Tue, 10 Nov 2026 15:22:45 +0100 Subject: [PATCH 2/2] format --- test/integration/_fixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/_fixtures.ts b/test/integration/_fixtures.ts index 237be09fe..bf2b240f8 100644 --- a/test/integration/_fixtures.ts +++ b/test/integration/_fixtures.ts @@ -8,7 +8,7 @@ const API_URL_ENV_VAR = 'APIFY_INTEGRATION_TESTS_API_URL'; /** Client authenticated as the primary test user, or with the given `token` instead. */ export function makeClient(token?: string): ApifyClient { - token ??= process.env[TOKEN_ENV_VAR] + token ??= process.env[TOKEN_ENV_VAR]; if (!token) { throw new Error(`${TOKEN_ENV_VAR} environment variable is missing, cannot run tests!`); }