Skip to content

fix: align supported-versions with the real /api/info contract; exception-aware fallback#3406

Merged
jeanfbrito merged 2 commits into
masterfrom
fix/supported-versions-fixture-realism
Jul 10, 2026
Merged

fix: align supported-versions with the real /api/info contract; exception-aware fallback#3406
jeanfbrito merged 2 commits into
masterfrom
fix/supported-versions-fixture-realism

Conversation

@jeanfbrito

@jeanfbrito jeanfbrito commented Jul 9, 2026

Copy link
Copy Markdown
Member

Jira: CORE-2409

Stacked on #3405 (which stacks on #3404) — retarget to master as the stack merges.

What

Closes the contract-drift gap that let the exception-scope defect (#3404) pass CI for 2.5 years, and fixes an exception-loss window introduced by the fallback source selection in #3388.

Contract alignment (verified against the server source, apps/meteor/server/api/lib/getServerInfo.ts)

The desktop's ServerInfo type declared fields the unauthenticated /api/info response never contains:

  • uniqueId removed — no server version returns it (the field does not exist in the server's IWorkspaceInfo). The workspace uniqueID comes from settings.public and is resolved on demand. The dead serverInfoResult.uniqueId fallback is removed.
  • commit / build / marketplaceApiVersion marked optional and documented as authenticated-only (view-statistics); the desktop always calls /api/info unauthenticated. sha-exception matching uses the persisted server.gitCommitHash pushed by the injected script (Meteor.gitCommitHash), which is its only real data source.
  • Default test fixtures rebuilt to the real wire shape (trimmed major.minor version, no uniqueId/commit/info). The previous fixtures mirrored the fictional type, so CI validated the desktop against itself. Switching beforeEach to jest.resetAllMocks() fixed latent cross-test mock leakage the honest fixtures surfaced.

Live wire-contract test

New apiContract.main.spec.ts asserts the unauthenticated /api/info shape against open.rocket.chat (6 assertions). Skips only on genuine network failure or SKIP_CONTRACT_TESTS=1; a reachable server with a drifted contract fails the suite.

Exception-aware offline fallback

#3388 made the offline fallback prefer the bundled builtin payload when fresher than the cache. The builtin never carries tenant exceptions, so a workspace kept usable by a cached exception could lose it to a fresher builtin and be blocked. The fallback now tries BOTH sources in freshness order and the first one that supports the server wins — blocking requires every available source to fail. This preserves #3388's stale-cache rescue while never losing a cached exception to it. Covered by three new tests (cache-exception rescue, builtin rescue, certain block).

Tests

  • Touched suites: 148 tests pass; contract spec 6/6 live against open.rocket.chat.
  • Full repo suite: 153 suites / 1615 tests pass. tsc --noEmit and yarn lint clean.

Summary by CodeRabbit

  • Bug Fixes
    • Improved supported-version detection when server or cloud sources are unavailable.
    • Chooses the freshest available version data and tries alternate sources when needed.
    • Preserves correct exception handling across server, cloud, and bundled version data.
    • Refreshes supported-version information when the application resumes.
    • Improved compatibility with server information returned without authenticated build and commit details.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7218acd6-6871-4d81-8022-6360d6c320ad

📥 Commits

Reviewing files that changed from the base of the PR and between db94a64 and 2518825.

📒 Files selected for processing (4)
  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
  • src/servers/supportedVersions/main.ts
  • src/servers/supportedVersions/types.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/servers/supportedVersions/types.ts
  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
  • src/servers/supportedVersions/main.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: CodeRabbit / Review
  • GitHub Check: check (ubuntu-latest)
  • GitHub Check: check (windows-latest)

Walkthrough

Adds unauthenticated /api/info contract coverage, updates server-info types, changes supported-version fallback validation to try freshness-ordered cache and builtin candidates, preserves persisted identity, and refreshes configured servers after system resume.

Changes

Supported versions handling

Layer / File(s) Summary
Server-info contract and test fixtures
src/servers/supportedVersions/types.ts, src/servers/supportedVersions/apiContract.main.spec.ts, src/servers/supportedVersions/main.main.spec.ts
Models authentication-dependent fields, validates the unauthenticated /api/info response, and updates mocked response and dispatch expectations.
Freshness-ordered fallback validation
src/servers/supportedVersions/main.ts, src/servers/supportedVersions/main.main.spec.ts
Orders cached and builtin payloads by timestamp, validates candidates sequentially with source-aware exception handling, and adds precedence regression coverage.
Identity, error handling, and refresh integration
src/servers/supportedVersions/main.ts
Uses persisted server identity, handles refresh errors, guards missing translation languages, and refreshes supported-version state after system resume.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant powerMonitor
  participant checkSupportedVersionServers
  participant updateSupportedVersionsData
  participant buildFallbackCandidates
  participant validateFallbackAndDispatch
  powerMonitor->>checkSupportedVersionServers: resume event
  checkSupportedVersionServers->>updateSupportedVersionsData: refresh configured servers
  updateSupportedVersionsData->>buildFallbackCandidates: cached and builtin payloads
  buildFallbackCandidates-->>updateSupportedVersionsData: ordered candidates
  updateSupportedVersionsData->>validateFallbackAndDispatch: validate candidates
  validateFallbackAndDispatch-->>updateSupportedVersionsData: dispatch selected verdict and source
Loading

Possibly related PRs

Suggested labels: type: bug

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: aligning /api/info contract handling and updating fallback behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • CORE-2409: Request failed with status code 401

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/servers/supportedVersions/apiContract.main.spec.ts`:
- Around line 53-55: In the retry logic surrounding the contract request, update
the catch block that assigns `lastError` so it immediately rethrows errors that
are not network failures; only retain and retry errors classified as
network-related, preventing reachable 4xx contract failures from being masked by
later responses.

In `@src/servers/supportedVersions/main.ts`:
- Around line 617-636: Candidate validation in the supported-version selection
logic currently aborts on the first malformed payload, preventing fallback
evaluation. Update the candidate evaluation around isServerVersionSupported so
each candidate’s validation failure is caught independently, allowing the next
candidate to be tried; dispatch the error only when all candidates fail to
produce a verdict. Add a regression test covering a malformed cache candidate
followed by a valid builtin candidate.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9147c4d8-0684-4bef-aa6f-5fd0df3ced02

📥 Commits

Reviewing files that changed from the base of the PR and between 92b3e1b and 47f9d10.

📒 Files selected for processing (4)
  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
  • src/servers/supportedVersions/main.ts
  • src/servers/supportedVersions/types.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Use TypeScript for all new code unless explicitly told otherwise.
Use Fuselage components for all UI work; only create custom components when Fuselage does not provide what is needed.
Import UI components from @rocket.chat/fuselage.
Check Theme.d.ts for valid color tokens before using Fuselage theme colors.
Use optional chaining with fallbacks for platform-specific APIs, especially Linux-only process APIs like process.getuid(), process.getgid(), process.geteuid(), and process.getegid().
Only mock platform-specific APIs when defensive coding is not possible.
Use TypeScript strict mode.
Use React functional components with hooks.
Redux actions must follow the Flux Standard Action (FSA) pattern.
Use camelCase for file names and PascalCase for components.

**/*.{ts,tsx}: Use TypeScript for new code unless explicitly told otherwise.
Use Fuselage components from @rocket.chat/fuselage for UI work unless the design requires something Fuselage does not provide.
Check Theme.d.ts for valid color tokens before using Fuselage colors.
Verify library props, APIs, and tokens against official docs or local .d.ts files instead of assuming.
Use React functional components with hooks.
Redux actions follow FSA shape.
Use camelCase for file names and PascalCase for components.
Prefer clear names over unnecessary comments.
Prefer editing existing files over creating new abstractions unless the new abstraction removes real complexity or matches an existing pattern.

Files:

  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/types.ts
  • src/servers/supportedVersions/main.ts
  • src/servers/supportedVersions/main.main.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.spec.ts for renderer process tests.

Files:

  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
**/*.main.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Use *.main.spec.ts for main process tests.

Main-process specs use *.main.spec.ts.

Files:

  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Renderer specs use *.spec.ts / *.spec.tsx.

Files:

  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
src/*/*/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Renderer specs must live in a Jest-matched nested path, such as src/<module>/<subdir>/*.spec.ts(x); flat src/<module>/*.spec.ts files are not discovered by the current testMatch.

Files:

  • src/servers/supportedVersions/apiContract.main.spec.ts
  • src/servers/supportedVersions/main.main.spec.ts
🔇 Additional comments (4)
src/servers/supportedVersions/types.ts (1)

57-93: LGTM!

src/servers/supportedVersions/apiContract.main.spec.ts (1)

1-52: LGTM!

Also applies to: 56-127

src/servers/supportedVersions/main.main.spec.ts (1)

1-2: LGTM!

Also applies to: 46-62, 101-104, 164-194, 294-297, 617-620, 834-839, 1821-2093, 2521-2522

src/servers/supportedVersions/main.ts (1)

100-129: LGTM!

Also applies to: 589-616, 637-648, 694-704, 820-840

Comment on lines +53 to +55
} catch (error) {
lastError = error;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not retry contract failures as network failures.

Because only the final error is classified, a reachable 4xx can be followed by a 5xx and incorrectly skip the suite. Immediately rethrow non-network errors.

Proposed fix
     } catch (error) {
       lastError = error;
+      if (!isNetworkFailure(error)) {
+        throw error;
+      }
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (error) {
lastError = error;
}
} catch (error) {
lastError = error;
if (!isNetworkFailure(error)) {
throw error;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/servers/supportedVersions/apiContract.main.spec.ts` around lines 53 - 55,
In the retry logic surrounding the contract request, update the catch block that
assigns `lastError` so it immediately rethrows errors that are not network
failures; only retain and retry errors classified as network-related, preventing
reachable 4xx contract failures from being masked by later responses.

Comment on lines +617 to +636
let chosen = candidates[0];
let verdict = await isServerVersionSupported(
server,
fallbackVersions,
chosen.versions,
freshCommitHash,
fallbackSource
chosen.source
);
const other = candidates[1];
if (!verdict.supported && other) {
const otherVerdict = await isServerVersionSupported(
server,
other.versions,
freshCommitHash,
other.source
);
if (otherVerdict.supported) {
chosen = other;
verdict = otherVerdict;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Continue when an individual fallback candidate is malformed.

A malformed first candidate throws out of the shared try, so the second candidate is never evaluated. A corrupted fresher cache can therefore prevent a valid builtin payload from rescuing the server. Catch validation failures per candidate and dispatch an error only if no candidate yields a verdict; add a malformed-cache/builtin-rescue regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/servers/supportedVersions/main.ts` around lines 617 - 636, Candidate
validation in the supported-version selection logic currently aborts on the
first malformed payload, preventing fallback evaluation. Update the candidate
evaluation around isServerVersionSupported so each candidate’s validation
failure is caught independently, allowing the next candidate to be tried;
dispatch the error only when all candidates fail to produce a verdict. Add a
regression test covering a malformed cache candidate followed by a valid builtin
candidate.

@jeanfbrito jeanfbrito changed the base branch from fix/supported-versions-hardening to master July 10, 2026 12:52
@jeanfbrito jeanfbrito force-pushed the fix/supported-versions-fixture-realism branch from 47f9d10 to db94a64 Compare July 10, 2026 12:53
…nfo contract; try both fallback sources

The desktop's ServerInfo type declared fields the server never returns
to unauthenticated callers (verified against the server source,
apps/meteor/server/api/lib/getServerInfo.ts):

- Remove the fictional uniqueId field. No server version includes it in
  /api/info; the workspace uniqueID comes from settings.public and is
  resolved on demand by withExceptionScopeUniqueId. Remove the dead
  serverInfoResult.uniqueId fallback in updateSupportedVersionsData.
- Mark build, marketplaceApiVersion, and commit optional and document
  that they only appear for authenticated view-statistics callers; the
  desktop always calls /api/info unauthenticated. The sha-exception
  path uses the persisted server.gitCommitHash pushed by the injected
  script instead.
- Rebuild the default test fixture to mirror the real unauthenticated
  wire shape (trimmed major.minor version, no uniqueId/commit/info) so
  the suite can no longer validate assumptions against fields real
  servers do not send — the gap that let the exception-scope defect
  pass CI. Switch beforeEach to jest.resetAllMocks(), fixing latent
  cross-test mock leakage the honest fixtures surfaced.

Also rework the offline fallback to try BOTH remaining sources instead
of picking one by timestamp: the bundled builtin payload never carries
tenant exceptions, so preferring a fresher builtin over a cache that
holds the tenant's exception blocked a workspace that should stay
usable. Candidates are checked in freshness order and the first source
that supports the server wins; blocking requires every available source
to fail. This keeps the stale-cache rescue from #3388 while never
losing a cached exception to it.
Asserts the unauthenticated /api/info response shape against a real
server (open.rocket.chat): trimmed major.minor version, no uniqueId,
no commit/info blocks, string minimumClientVersions, JWT-shaped
supportedVersions.signed. Skips only on genuine network failure or
SKIP_CONTRACT_TESTS=1, so contract drift between the desktop and the
server cannot hide behind mocks that agree with themselves.
@jeanfbrito jeanfbrito force-pushed the fix/supported-versions-fixture-realism branch from db94a64 to 2518825 Compare July 10, 2026 13:38
@jeanfbrito jeanfbrito merged commit afe13eb into master Jul 10, 2026
9 checks passed
@jeanfbrito jeanfbrito deleted the fix/supported-versions-fixture-realism branch July 10, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant