Skip to content

fix(test): fix parallel test server isolation — remove shared ctx.port fallback#743

Open
asclearuc wants to merge 2 commits intodevelopfrom
fix/parallel-test-server-isolation
Open

fix(test): fix parallel test server isolation — remove shared ctx.port fallback#743
asclearuc wants to merge 2 commits intodevelopfrom
fix/parallel-test-server-isolation

Conversation

@asclearuc
Copy link
Copy Markdown
Collaborator

@asclearuc asclearuc commented May 1, 2026

Summary

  • Replace || ctx.port shared-context fallback with a hard assertion in all four test modules (client-typescript, client-python, client-mcp, nodes); each module already starts its own server on a dynamically assigned port (--port=0), but the fallback silently routed subprocesses to whichever module's port was written last to the shared Listr2 context
  • Remove the -s sequential workaround from CI (added in ci(test): run builder test sequentially to isolate cross-suite flakes #734) — parallel execution is now safe by construction
  • Update stale :5565 comment in _build.yaml to reflect dynamic per-module port allocation

Type

fix

Testing

  • Tests added or updated
  • Tested locally
  • ./builder test passes

Checklist

  • Commit messages follow conventional commits
  • No secrets or credentials included
  • Wiki updated (if applicable)
  • Breaking changes documented (if applicable)

Linked Issue

Fixes #741

Summary by CodeRabbit

  • Tests
    • Test runners now fail fast if the test server does not start and always use the server-provided URI, improving reliability and error clarity.
  • Chores
    • Adjusted CI test invocation behavior for test jobs.
  • Style
    • Updated formatter ignore configuration to exclude additional script files from automated formatting.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

📝 Walkthrough

Walkthrough

Removed the -s/--sequential flag from the Test job in the reusable workflow and tightened test-script server handling: test task scripts now require a started test-server bracket with a port (or serverUri) and will throw an explicit error if missing; ROCKETRIDE_URI is constructed from bracket.serverUri or http://localhost:${bracket.port}.

Changes

Cohort / File(s) Summary
CI Workflow
\.github/workflows/_build.yaml
Removed the -s (--sequential) option from ${{ matrix.builder_cmd }} test invocation.
Node test task
nodes/scripts/tasks.js
makeRunPytestAction now fails if ctx.brackets['node-test-server'] has no port; ROCKETRIDE_URI uses bracket.serverUri or http://localhost:${bracket.port}.
Client MCP test task
packages/client-mcp/scripts/tasks.js
Removed fallback to ctx.port; require mcp-test-server bracket port and build serverUri from bracket.serverUri or http://localhost:${bracket.port}.
Client Python test task
packages/client-python/scripts/tasks.js
client-python:run-pytest now throws if py-test-server bracket lacks port; ROCKETRIDE_URI derived from bracket only.
Client TypeScript test task
packages/client-typescript/scripts/tasks.js
makeRunJestAction now requires ts-test-server bracket port; ROCKETRIDE_URI prefers bracket.serverUri or http://localhost:${bracket.port}.
Formatting ignore
.prettierignore
Added nodes/scripts/tasks.js and packages/client-mcp/scripts/tasks.js to Prettier ignore.

Sequence Diagram

sequenceDiagram
    actor Workflow as GitHub Workflow
    participant Launcher as Test Server Launcher
    participant Bracket as Bracket Registry
    participant Runner as Test Runner (pytest/jest)
    participant Server as Test Server

    Workflow->>Launcher: start test server (--port=0)
    Launcher->>Bracket: register server (port, serverUri)
    alt bracket contains port/serverUri
        Runner->>Bracket: read bracket for module
        Runner->>Runner: compute ROCKETRIDE_URI = bracket.serverUri or http://localhost:port
        Runner->>Server: connect and run tests
    else missing port
        Runner->>Runner: throw Error("... bracket missing — server did not start")
    end
    Server-->>Runner: test responses
    Runner-->>Workflow: report results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

module:nodes, module:client-python, module:client-typescript

Suggested reviewers

  • jmaionchi
  • kwit75
  • stepmikhaylov
  • Rod-Christensen

Poem

🐰 I hopped through scripts and CI lanes,
Ports assigned, no silent plains.
Brackets checked, I'll loudly shout,
"If no port—no tests—easy rout!"
A tiny hop for safer runs.

🚥 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
Title check ✅ Passed The title accurately describes the main change: removing the shared ctx.port fallback to fix parallel test server isolation.
Linked Issues check ✅ Passed The PR addresses the core requirement from #741: replacing shared ctx.port fallback with hard assertions in four test modules to enable safe parallel execution.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: test module isolation fixes, removal of sequential workaround, and Prettier configuration updates for modified files.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/parallel-test-server-isolation

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 and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 1, 2026

No description provided.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/client-python/scripts/tasks.js (1)

188-195: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Normalize ROCKETRIDE_URI before reusing it here.

makeStartTestServerAction still accepts a port-less ROCKETRIDE_URI and falls back to 5565, but this code forwards the raw serverUri unchanged. That sends pytest to port 80 when the debug URI has no explicit port.

🔧 Proposed fix
-			const serverUri = bracket.serverUri || `http://localhost:${bracket.port}`;
+			const serverUri =
+				bracket.serverUri && new URL(bracket.serverUri).port
+					? bracket.serverUri
+					: `http://localhost:${bracket.port}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-python/scripts/tasks.js` around lines 188 - 195, The code
forwards a raw serverUri into testEnv.ROCKETRIDE_URI which can be a
hostname-only URI (causing pytest to use port 80); update the logic around
bracket/serverUri (in the makeStartTestServerAction flow) to normalize the URI:
parse serverUri (or process.env.ROCKETRIDE_URI if set), ensure it includes a
scheme and an explicit port (default to 5565 when none is present), and then set
testEnv.ROCKETRIDE_URI to the normalized URI so consumers always get a host:port
URL; change references to bracket.serverUri / serverUri accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client-mcp/scripts/tasks.js`:
- Around line 111-133: The code forwards bracket.serverUri directly to
ROCKETRIDE_URI, which can be missing a port and cause pytest to target port 80;
before setting serverUri (the variable used in the env passed to execCommand),
normalize it by ensuring it has a scheme (prefix with "http://" if missing),
parse it with URL, and if url.port is empty set url.port = "5565" (the same
fallback used by makeStartTestServerAction), then use url.toString() for
ROCKETRIDE_URI; update the code around the bracket/serverUri handling and the
env passed to execCommand so the normalized serverUri is used.

---

Outside diff comments:
In `@packages/client-python/scripts/tasks.js`:
- Around line 188-195: The code forwards a raw serverUri into
testEnv.ROCKETRIDE_URI which can be a hostname-only URI (causing pytest to use
port 80); update the logic around bracket/serverUri (in the
makeStartTestServerAction flow) to normalize the URI: parse serverUri (or
process.env.ROCKETRIDE_URI if set), ensure it includes a scheme and an explicit
port (default to 5565 when none is present), and then set testEnv.ROCKETRIDE_URI
to the normalized URI so consumers always get a host:port URL; change references
to bracket.serverUri / serverUri accordingly.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 932cde7d-ee3a-4b9c-b046-ce90aa40675b

📥 Commits

Reviewing files that changed from the base of the PR and between 26d17ff and 1aa0c6d.

📒 Files selected for processing (5)
  • .github/workflows/_build.yaml
  • nodes/scripts/tasks.js
  • packages/client-mcp/scripts/tasks.js
  • packages/client-python/scripts/tasks.js
  • packages/client-typescript/scripts/tasks.js

Comment thread packages/client-mcp/scripts/tasks.js Outdated
@kwit75
Copy link
Copy Markdown
Collaborator

kwit75 commented May 1, 2026

Heads up — this branch has a merge conflict against develop. PUT /pulls/743/update-branch returned merge conflict between base and head, so CI hasn't been triggered yet.

Once you rebase / merge develop in (it now has both #734's -s flag and #742's literal-APIKEY workflow fix, in addition to your earlier #740 changes) CI should run cleanly. Worth keeping in mind that this PR's substantive parallel-isolation refactor is the proper deeper fix for what #734 was a one-line workaround for — see issue #741.

@asclearuc asclearuc force-pushed the fix/parallel-test-server-isolation branch from 1aa0c6d to 4e00ce9 Compare May 1, 2026 22:31
@asclearuc asclearuc force-pushed the fix/parallel-test-server-isolation branch from 1a9d3a5 to 788d812 Compare May 1, 2026 22:36
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/client-mcp/scripts/tasks.js (1)

153-190: 🧹 Nitpick | 🔵 Trivial

client-mcp and client-python share basePort: 20000 — potential remaining parallelism contention.

This PR removes the ctx.port cross-module fallback and claims parallel execution is now safe, but both client-mcp (makeStartTestServerAction line 177) and packages/client-python/scripts/tasks.js (makeStartTestServerAction line 149) start their engines with --base_port=20000. client-typescript uses 30000 and nodes uses 40000 — explicitly separated ranges. If the engine allocates internal sub-services sequentially from --base_port, the two modules sharing 20000 can still collide during parallel runs.

Consider assigning a distinct base port (e.g. 25000) to one of the two modules before removing -s, or verifying in CI that no sub-service conflicts occur.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-mcp/scripts/tasks.js` around lines 153 - 190,
makeStartTestServerAction starts the engine with basePort: 20000 which conflicts
with packages/client-python that uses the same base; change the base port here
(in makeStartTestServerAction in packages/client-mcp/scripts/tasks.js) to a
different non-overlapping value (e.g. 25000) or otherwise choose a distinct
range, and ensure the corresponding counterpart (makeStartTestServerAction in
packages/client-python/scripts/tasks.js) is adjusted or CI assertions are added
to verify no sub-service port collisions; update any documentation/tests that
assume basePort 20000.
.github/workflows/_build.yaml (1)

127-128: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale :5565 comment was not updated — this was explicitly called out in the PR description.

The comment still reads "Integration tests boot a local server on :5565" but each module now dynamically binds to an OS-assigned port (--port=0). This should reflect the per-module dynamic allocation introduced by this PR.

📝 Proposed fix
-        # Integration tests boot a local server on :5565 and connect a test
-        # client. Both sides need the same shared secret to authenticate;
+        # Integration tests boot per-module servers on dynamically assigned
+        # ports and connect a test client. Both sides need the same shared secret to authenticate;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/_build.yaml around lines 127 - 128, Update the stale
comment that mentions a fixed port ":5565" to reflect the new per-module dynamic
port allocation (e.g., note that modules bind with "--port=0" / OS-assigned
ports); specifically edit the comment text in .github/workflows/_build.yaml
replacing the phrase "Integration tests boot a local server on :5565" with
wording that indicates each module dynamically binds to an OS-assigned port (or
uses "--port=0") and ensure it still explains both server and test client must
share the same secret.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/client-python/scripts/tasks.js`:
- Around line 189-191: The current serverUri selection uses bracket.serverUri
truthiness which lets port-less URIs (like "http://localhost") silently target
port 80; update the logic in makeStartTestServerAction to detect an explicit
port on bracket.serverUri (use new URL(bracket.serverUri).port) and only accept
bracket.serverUri when URL.port is non-empty; otherwise fall back to
`http://localhost:${bracket.port}` so pytest uses the actual bracket.port.
Ensure you handle invalid URL parsing (try/catch) and still fall back to
bracket.port if parsing fails.

---

Outside diff comments:
In @.github/workflows/_build.yaml:
- Around line 127-128: Update the stale comment that mentions a fixed port
":5565" to reflect the new per-module dynamic port allocation (e.g., note that
modules bind with "--port=0" / OS-assigned ports); specifically edit the comment
text in .github/workflows/_build.yaml replacing the phrase "Integration tests
boot a local server on :5565" with wording that indicates each module
dynamically binds to an OS-assigned port (or uses "--port=0") and ensure it
still explains both server and test client must share the same secret.

In `@packages/client-mcp/scripts/tasks.js`:
- Around line 153-190: makeStartTestServerAction starts the engine with
basePort: 20000 which conflicts with packages/client-python that uses the same
base; change the base port here (in makeStartTestServerAction in
packages/client-mcp/scripts/tasks.js) to a different non-overlapping value (e.g.
25000) or otherwise choose a distinct range, and ensure the corresponding
counterpart (makeStartTestServerAction in
packages/client-python/scripts/tasks.js) is adjusted or CI assertions are added
to verify no sub-service port collisions; update any documentation/tests that
assume basePort 20000.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: f118979b-1f89-4e62-9687-47027632c772

📥 Commits

Reviewing files that changed from the base of the PR and between 1aa0c6d and 4e00ce9.

📒 Files selected for processing (6)
  • .github/workflows/_build.yaml
  • .prettierignore
  • nodes/scripts/tasks.js
  • packages/client-mcp/scripts/tasks.js
  • packages/client-python/scripts/tasks.js
  • packages/client-typescript/scripts/tasks.js

Comment on lines +189 to +191
if (!bracket?.port) throw new Error('py-test-server bracket missing — server did not start');
// Use existing server URI when set (e.g. ROCKETRIDE_URI=http://localhost:5678 for debugging)
const serverUri = bracket?.serverUri || `http://localhost:${port}`;
const serverUri = bracket.serverUri || `http://localhost:${bracket.port}`;
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Port-less bracket.serverUri silently routes pytest to port 80.

makeStartTestServerAction returns { ..., serverUri: envUri } verbatim from process.env.ROCKETRIDE_URI (lines 134–139). If a developer passes ROCKETRIDE_URI=http://localhost (no explicit port), bracket.serverUri is a truthy non-empty string, so the || \http://localhost:${bracket.port}\`` fallback is never reached. Pytest ends up targeting port 80 instead of the bracket.port value (e.g. 5565) that the setup action correctly derived.

The same pattern was flagged for client-mcp in a prior review; client-python has the identical bug.

🛡️ Proposed fix
-			const serverUri = bracket.serverUri || `http://localhost:${bracket.port}`;
+			const serverUri =
+				bracket.serverUri && new URL(bracket.serverUri).port
+					? bracket.serverUri
+					: `http://localhost:${bracket.port}`;

URL.port returns an empty string when the port is the default for the protocol, so new URL('http://localhost').port is '' (falsy), correctly triggering the fallback for any port-less URI.

📝 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
if (!bracket?.port) throw new Error('py-test-server bracket missing — server did not start');
// Use existing server URI when set (e.g. ROCKETRIDE_URI=http://localhost:5678 for debugging)
const serverUri = bracket?.serverUri || `http://localhost:${port}`;
const serverUri = bracket.serverUri || `http://localhost:${bracket.port}`;
if (!bracket?.port) throw new Error('py-test-server bracket missing — server did not start');
// Use existing server URI when set (e.g. ROCKETRIDE_URI=http://localhost:5678 for debugging)
const serverUri =
bracket.serverUri && new URL(bracket.serverUri).port
? bracket.serverUri
: `http://localhost:${bracket.port}`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/client-python/scripts/tasks.js` around lines 189 - 191, The current
serverUri selection uses bracket.serverUri truthiness which lets port-less URIs
(like "http://localhost") silently target port 80; update the logic in
makeStartTestServerAction to detect an explicit port on bracket.serverUri (use
new URL(bracket.serverUri).port) and only accept bracket.serverUri when URL.port
is non-empty; otherwise fall back to `http://localhost:${bracket.port}` so
pytest uses the actual bracket.port. Ensure you handle invalid URL parsing
(try/catch) and still fall back to bracket.port if parsing fails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

builder ci/cd CI/CD and build system

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Investigate parallel test-module contention (root cause behind sequential-test workaround)

2 participants