From 3614183200ee5adf44da5b1da0ba5e2996435ca1 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Fri, 10 Jul 2026 18:33:25 +0200 Subject: [PATCH] fix(server): stabilize app install version-gate integration test (#22826) ## Why The `failing-app-installation-workspace-version` integration suite fails on CI (e.g. [this run](https://github.com/twentyhq/twenty/actions/runs/29105697459/job/86405891168)): ``` App requires Twenty server >=2.21.0 but this server is 2.20.0. subCode: SERVER_VERSION_INCOMPATIBLE ``` The test uploads an app requiring `>=${TWENTY_CURRENT_VERSION}` and expects the install to be rejected by the **workspace** version gate. But after the `2.21.0` version bump, `TWENTY_CURRENT_VERSION` (`2.21.0`) moved ahead of the latest instance upgrade command (`2-20`, so `getInferredVersion()` returns `2.20.0`). The tarball upload runs the **instance** server-compat check first, which rejects `>=2.21.0` against a `2.20.0` server before the workspace gate under test is ever reached. The sibling sync test is unaffected because sync only validates workspace compatibility, not the upload-time instance check. ## What Derive the required version range from the version the instance actually reached (the workspace upgrade cursor via `extractVersionFromCommandName`) instead of the drifting `TWENTY_CURRENT_VERSION` constant. This way: - The upload passes the instance server-compat check (server satisfies `>=`). - The workspace, which resolves one version behind after the injected failed cursor, still fails the workspace gate, producing the expected `WORKSPACE_VERSION_INCOMPATIBLE` error. The error assertion keeps using the normalized snapshot (`scrubSemverVersions`), so the concrete version numbers do not leak into the snapshot and future version bumps won't churn it. --- _Generated by [Claude Code](https://claude.ai/code/session_01XqhqQ8VGJZWBuznR8nRviX)_ Review in cubic --- ...tion-workspace-version.integration-spec.ts | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/twenty-server/test/integration/metadata/suites/application/failing-app-installation-workspace-version.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/application/failing-app-installation-workspace-version.integration-spec.ts index 30eecf8509..a916b5a94f 100644 --- a/packages/twenty-server/test/integration/metadata/suites/application/failing-app-installation-workspace-version.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/application/failing-app-installation-workspace-version.integration-spec.ts @@ -8,7 +8,7 @@ import { scrubSemverVersions } from 'test/utils/scrub-semver-versions.util'; import { isDefined } from 'twenty-shared/utils'; import { v4 as uuidv4 } from 'uuid'; -import { TWENTY_CURRENT_VERSION } from 'src/engine/core-modules/upgrade/constants/twenty-current-version.constant'; +import { extractVersionFromCommandName } from 'src/engine/core-modules/upgrade/utils/extract-version-from-command-name.util'; import { SEED_APPLE_WORKSPACE_ID } from 'src/engine/workspace-manager/dev-seeder/core/constants/seeder-workspaces.constant'; // The full install flow runs cache-lock retries with real delays, so fake @@ -70,6 +70,7 @@ const uploadTarballApp = async ({ describe('Install application is gated by the workspace completed upgrade version', () => { let currentVersionCommandName: string; + let currentServerVersion: string; const createdApplicationUniversalIdentifiers: string[] = []; beforeAll(async () => { @@ -93,6 +94,22 @@ describe('Install application is gated by the workspace completed upgrade versio } currentVersionCommandName = workspaceCursor.name; + + // Pin to the version the instance actually reached, not + // TWENTY_CURRENT_VERSION which can be bumped ahead of the latest instance + // command and trip the upload-time server-compat check before the + // workspace gate under test is reached. + const inferredServerVersion = extractVersionFromCommandName( + currentVersionCommandName, + ); + + if (!isDefined(inferredServerVersion)) { + throw new Error( + `Could not extract a server version from upgrade cursor "${currentVersionCommandName}"`, + ); + } + + currentServerVersion = inferredServerVersion; }); afterEach(async () => { @@ -116,7 +133,7 @@ describe('Install application is gated by the workspace completed upgrade versio await uploadTarballApp({ universalIdentifier, roleId, - requiredServerVersion: `>=${TWENTY_CURRENT_VERSION}`, + requiredServerVersion: `>=${currentServerVersion}`, }); createdApplicationUniversalIdentifiers.push(universalIdentifier);