Skip to content

perf(test): run MCP lifecycle checks in process - #6373

Merged
cv merged 2 commits into
mainfrom
codex/perf-mcp-lifecycle-tests
Jul 7, 2026
Merged

perf(test): run MCP lifecycle checks in process#6373
cv merged 2 commits into
mainfrom
codex/perf-mcp-lifecycle-tests

Conversation

@cv

@cv cv commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Run four MCP lifecycle and runtime-capability suites directly in Vitest instead of cold-starting a Node process for every scenario. The focused local run falls from 14.25 seconds to 2.97 seconds wall-clock while preserving all 52 behaviors, real registry filesystem state, and the two real OpenShell fixture version probes.

Related Issue

Contributes to #6245.

Changes

  • Replace 55 outer Node isolation children across the destroy, Hermes startup, and Deep Agents lifecycle/capability suites with direct source calls and hoisted Vitest mocks.
  • Preserve per-scenario provider, attachment, adapter, policy, recovery, and environment isolation.
  • Keep real temporary registry and lifecycle-lock filesystem behavior plus the two nested OpenShell fixture version checks.
  • Leave production modules and genuine crash/process-contract tests unchanged.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: This only changes internal test isolation mechanics; MCP commands, configuration, registry semantics, and runtime behavior are unchanged.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification:
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: npx vitest run --project integration test/mcp-destroy-lifecycle.test.ts test/hermes-mcp-startup-probe.test.ts test/deepagents-mcp-legacy-lifecycle.test.ts test/deepagents-mcp-runtime-capability.test.ts (4 files, 52/52 tests passed; 2.65s Vitest / 2.97s wall-clock); npm run test-conditionals:scan -- --top 25 (completed; changed files contain zero if statements).
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result:
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • Tests
    • Improved coverage for lifecycle, startup, and runtime capability-probe flows.
    • Reworked multiple suites to run in-process with mocked dependencies for faster, more reliable execution.
    • Expanded assertions around destroy/rebuild/restore behaviors, including rollback/reattach paths and durable-marker handling.
    • Strengthened validation of cleanup and state transitions, and improved error-message checking to better prevent regressions.

@cv cv self-assigned this Jul 7, 2026
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Four MCP-related test files now run in-process under Vitest instead of spawning child processes. The tests use hoisted mocks, direct bridge/probe calls, and shared mock state to assert lifecycle, capability, startup, and destroy behaviors.

Changes

Test Harness Migration

Layer / File(s) Summary
Legacy lifecycle setup and mocks
test/deepagents-mcp-legacy-lifecycle.test.ts
Vitest imports, hoisted mocks, environment setup, direct module imports, shared lifecycle state, and reset hooks replace the subprocess harness.
Legacy lifecycle case conversions
test/deepagents-mcp-legacy-lifecycle.test.ts
Teardown, ownership failure, rebuild reattachment, and destroy-abort restore cases now call bridge methods directly and assert lifecycle state.
Runtime capability probe conversion
test/deepagents-mcp-runtime-capability.test.ts
runDeepAgentsProbe now runs the capability assertion in-process and reports calls and messages from mock history.
Hermes startup probe conversion
test/hermes-mcp-startup-probe.test.ts
runHermesProbe uses in-process mocks for provider commands, gateway recovery, wait behavior, and shields checks, with mock resets between tests.
Destroy lifecycle setup and mocks
test/mcp-destroy-lifecycle.test.ts
Hoisted shared state, module mocks, environment helpers, and in-process command setup replace the CommonJS subprocess harness.
Destroy lifecycle scenario conversions
test/mcp-destroy-lifecycle.test.ts
Providerless cleanup, durable-marker rejection, rebuild/destroy flows, restore paths, retries, interruptions, and drift protection now assert against in-process state.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Suggested labels: refactor

Suggested reviewers: jyaunches, ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: moving MCP lifecycle checks to in-process test execution for performance.
✨ 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 codex/perf-mcp-lifecycle-tests

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

@github-code-quality

github-code-quality Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the codex/perf-mcp-lifec... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main codex/perf-mcp-lifec... ca5f191 +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

TypeScript / code-coverage/cli

The overall coverage in the codex/perf-mcp-lifec... branch is 75%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main codex/perf-mcp-lifec... ca5f191 +/-
src/lib/shields...nsition-lock.ts 85%
src/lib/onboard/preflight.ts 83%
src/lib/actions...all/run-plan.ts 81%
src/lib/state/o...oard-session.ts 81%
src/lib/actions...licy-channel.ts 79%
src/lib/state/sandbox.ts 75%
src/lib/onboard...er-gpu-patch.ts 69%
src/lib/shields/index.ts 68%
src/lib/policy/index.ts 66%
src/lib/onboard.ts 28%

Updated July 07, 2026 02:43 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: None
Optional E2E: None

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • None. No E2E is required because this PR is tests-only. The modified files refactor existing MCP lifecycle/runtime unit-style tests and do not change runtime code paths, sandbox lifecycle behavior, credentials/security boundaries, network policy assets, inference routing, deployment, onboarding, or real assistant user flows.

Optional E2E

  • None.

New E2E recommendations

  • None.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: None
Optional E2E targets: None

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • None. Only non-E2E test files outside test/e2e/ changed, so there is no NemoClaw E2E target workflow dispatch required.

Optional E2E targets

  • None.

Relevant changed files

  • None.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Consider PRA-1: Shared testState object via vi.hoisted() creates implicit cross-test coupling.
Open items: 0 required · 0 warnings · 4 suggestions · 0 test follow-ups
Since last review: 0 prior items resolved · 2 still apply · 0 new items found

Action checklist

  • PRA-1 In-scope improvement: Shared testState object via vi.hoisted() creates implicit cross-test coupling in test/mcp-destroy-lifecycle.test.ts:13
  • PRA-2 In-scope improvement: Shared TMP_HOME temp directory relies on afterAll cleanup in test/deepagents-mcp-legacy-lifecycle.test.ts:27
  • PRA-3 In-scope improvement: testState object is large (18 fields) — consider splitting into focused mock groups in test/mcp-destroy-lifecycle.test.ts:13
  • PRA-4 In-scope improvement: waitUntil mock implementation reimplements retry logic inline in test/hermes-mcp-startup-probe.test.ts:28

Findings index

ID Severity Category Location Required action
PRA-1 Improvement correctness test/mcp-destroy-lifecycle.test.ts:13 Add a comment documenting the reset contract in beforeEach, or migrate to per-test factory functions for stronger isolation. Defer with rationale if preferred.
PRA-2 Improvement correctness test/deepagents-mcp-legacy-lifecycle.test.ts:27 No action needed — uniqueness via process.pid + Date.now is sufficient. Consider vi.beforeAll/afterAll with unique suffix per test file if stronger isolation desired.
PRA-3 Improvement scope test/mcp-destroy-lifecycle.test.ts:13 Optional: group related mocks into separate vi.hoisted() factories (e.g., providerMocks, adapterMocks, policyMocks) for maintainability.
PRA-4 Improvement scope test/hermes-mcp-startup-probe.test.ts:28 Consider extracting waitUntil mock factory to a shared helper if multiple probe test files need it. Currently only used here.
Review findings by urgency: 0 required fixes, 0 items to resolve/justify, 4 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

  • None.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-1 Improvement — Shared testState object via vi.hoisted() creates implicit cross-test coupling

  • Location: test/mcp-destroy-lifecycle.test.ts:13
  • Category: correctness
  • Problem: The testState object holds 18 fields (mock state, providers, adapters, env). While beforeEach resets all fields comprehensively (lines 85-180), any future test that omits a reset could leak state across tests. Previous review (PRA-1) noted this; unchanged in current diff.
  • Impact: Potential cross-test pollution if a future test omits a reset; current tests all pass and isolation works correctly.
  • Suggested action: Add a comment documenting the reset contract in beforeEach, or migrate to per-test factory functions for stronger isolation. Defer with rationale if preferred.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review beforeEach in mcp-destroy-lifecycle.test.ts lines 85-180 — confirm all testState fields reset.
  • Missing regression test: No regression test needed — test architecture observation; existing 52 tests validate current isolation.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: testState defined via vi.hoisted() at line 13; beforeEach resets all fields at lines 85-180

PRA-2 Improvement — Shared TMP_HOME temp directory relies on afterAll cleanup

  • Location: test/deepagents-mcp-legacy-lifecycle.test.ts:27
  • Category: correctness
  • Problem: Environment variable management uses shared TMP_HOME with afterAll cleanup. If the test process crashes mid-run, TMP_HOME persists. Uses process.pid + Date.now for uniqueness which mitigates collision risk. Previous review (PRA-2) noted this; unchanged in current diff.
  • Impact: Minor temp directory leakage on test process crash; no correctness impact on test results.
  • Suggested action: No action needed — uniqueness via process.pid + Date.now is sufficient. Consider vi.beforeAll/afterAll with unique suffix per test file if stronger isolation desired.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Verify TMP_HOME uses process.pid + Date.now for uniqueness at line 27; afterAll cleans up at lines 65-70.
  • Missing regression test: No regression test needed.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: const TMP_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nemoclaw-deepagents-mcp-legacy-')) at line 27; afterAll cleans up at lines 65-70

PRA-3 Improvement — testState object is large (18 fields) — consider splitting into focused mock groups

  • Location: test/mcp-destroy-lifecycle.test.ts:13
  • Category: scope
  • Problem: The testState object spans providers, adapters, policies, sandbox exec, and environment. Not a correctness issue, but readability and maintainability would improve with separation.
  • Impact: Harder to navigate and maintain; related mocks are interleaved.
  • Suggested action: Optional: group related mocks into separate vi.hoisted() factories (e.g., providerMocks, adapterMocks, policyMocks) for maintainability.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Inspect testState structure — 18 fields spanning providers, adapters, policies, sandbox exec.
  • Missing regression test: Not applicable — code organization.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: testState object at lines 13-40 contains adapterCalls, adapterRegistered, applyPresetContent, calls, executeGatewaySupervisorAction, executeSandboxCommand, executeSandboxExecCommand, failProviderDelete, failProviderDetach, getPresetContentGatewayState, home, originalEnv, policyApplyCalls, providers, attachedProviders, recoverNamedGatewayRuntime, removePreset, runOpenshellProviderCommand

PRA-4 Improvement — waitUntil mock implementation reimplements retry logic inline

  • Location: test/hermes-mcp-startup-probe.test.ts:28
  • Category: scope
  • Problem: The mock waitUntil implementation contains inline retry logic (while loop with attempts/maxAttempts). Could extract to a shared test helper if reused across probe tests.
  • Impact: Duplicated logic if multiple test files need similar waitUntil mocking; harder to maintain consistent behavior.
  • Suggested action: Consider extracting waitUntil mock factory to a shared helper if multiple probe test files need it. Currently only used here.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check if other test files mock waitUntil similarly.
  • Missing regression test: Not applicable — test helper design.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: waitUntil mock implementation at lines 28-45 reimplements retry loop with attempts/maxAttempts logic
Simplification opportunities: 2 possible cuts, net -2 lines possible

These are safe simplification checks only. Do not remove validation, security controls, data-loss prevention, or required tests.

  • PRA-3 shrink (test/mcp-destroy-lifecycle.test.ts:13): Single large testState object with 18 mixed-concern fields
    • Replacement: Multiple focused vi.hoisted() factories: providerMocks, adapterMocks, policyMocks, envMocks
    • Net: 10 lines
    • Safety boundary: All mocks must still be reset in beforeEach; no change to test behavior or isolation guarantees
  • PRA-4 native (test/hermes-mcp-startup-probe.test.ts:28): Inline waitUntil mock with retry logic (18 lines)
    • Replacement: Shared test helper: mockWaitUntil(condition, maxAttempts?) returning boolean
    • Net: -12 lines
    • Safety boundary: Must preserve exact retry semantics (attempts counting, maxAttempts handling, condition evaluation)
Since last review details

Current findings, using the urgency labels above:

PRA-1 Improvement — Shared testState object via vi.hoisted() creates implicit cross-test coupling

  • Location: test/mcp-destroy-lifecycle.test.ts:13
  • Category: correctness
  • Problem: The testState object holds 18 fields (mock state, providers, adapters, env). While beforeEach resets all fields comprehensively (lines 85-180), any future test that omits a reset could leak state across tests. Previous review (PRA-1) noted this; unchanged in current diff.
  • Impact: Potential cross-test pollution if a future test omits a reset; current tests all pass and isolation works correctly.
  • Suggested action: Add a comment documenting the reset contract in beforeEach, or migrate to per-test factory functions for stronger isolation. Defer with rationale if preferred.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Review beforeEach in mcp-destroy-lifecycle.test.ts lines 85-180 — confirm all testState fields reset.
  • Missing regression test: No regression test needed — test architecture observation; existing 52 tests validate current isolation.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: testState defined via vi.hoisted() at line 13; beforeEach resets all fields at lines 85-180

PRA-2 Improvement — Shared TMP_HOME temp directory relies on afterAll cleanup

  • Location: test/deepagents-mcp-legacy-lifecycle.test.ts:27
  • Category: correctness
  • Problem: Environment variable management uses shared TMP_HOME with afterAll cleanup. If the test process crashes mid-run, TMP_HOME persists. Uses process.pid + Date.now for uniqueness which mitigates collision risk. Previous review (PRA-2) noted this; unchanged in current diff.
  • Impact: Minor temp directory leakage on test process crash; no correctness impact on test results.
  • Suggested action: No action needed — uniqueness via process.pid + Date.now is sufficient. Consider vi.beforeAll/afterAll with unique suffix per test file if stronger isolation desired.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Verify TMP_HOME uses process.pid + Date.now for uniqueness at line 27; afterAll cleans up at lines 65-70.
  • Missing regression test: No regression test needed.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: const TMP_HOME = fs.mkdtempSync(path.join(os.tmpdir(), 'nemoclaw-deepagents-mcp-legacy-')) at line 27; afterAll cleans up at lines 65-70

PRA-3 Improvement — testState object is large (18 fields) — consider splitting into focused mock groups

  • Location: test/mcp-destroy-lifecycle.test.ts:13
  • Category: scope
  • Problem: The testState object spans providers, adapters, policies, sandbox exec, and environment. Not a correctness issue, but readability and maintainability would improve with separation.
  • Impact: Harder to navigate and maintain; related mocks are interleaved.
  • Suggested action: Optional: group related mocks into separate vi.hoisted() factories (e.g., providerMocks, adapterMocks, policyMocks) for maintainability.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Inspect testState structure — 18 fields spanning providers, adapters, policies, sandbox exec.
  • Missing regression test: Not applicable — code organization.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: testState object at lines 13-40 contains adapterCalls, adapterRegistered, applyPresetContent, calls, executeGatewaySupervisorAction, executeSandboxCommand, executeSandboxExecCommand, failProviderDelete, failProviderDetach, getPresetContentGatewayState, home, originalEnv, policyApplyCalls, providers, attachedProviders, recoverNamedGatewayRuntime, removePreset, runOpenshellProviderCommand

PRA-4 Improvement — waitUntil mock implementation reimplements retry logic inline

  • Location: test/hermes-mcp-startup-probe.test.ts:28
  • Category: scope
  • Problem: The mock waitUntil implementation contains inline retry logic (while loop with attempts/maxAttempts). Could extract to a shared test helper if reused across probe tests.
  • Impact: Duplicated logic if multiple test files need similar waitUntil mocking; harder to maintain consistent behavior.
  • Suggested action: Consider extracting waitUntil mock factory to a shared helper if multiple probe test files need it. Currently only used here.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check if other test files mock waitUntil similarly.
  • Missing regression test: Not applicable — test helper design.
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: waitUntil mock implementation at lines 28-45 reimplements retry loop with attempts/maxAttempts logic

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Add or justify PRA-T1 and any related test follow-ups.
Open items: 0 required · 0 warnings · 0 suggestions · 3 test follow-ups
Since last review: 0 prior items resolved · 0 still apply · 0 new items found

Action checklist

  • PRA-T1 Add or justify test follow-up: Acceptance clause
  • PRA-T2 Add or justify test follow-up: Acceptance clause
  • PRA-T3 Add or justify test follow-up: Acceptance clause
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Acceptance clause — the two real OpenShell fixture version probes — add test evidence or identify existing coverage. The lifecycle suites still set NEMOCLAW_OPENSHELL_BIN to test/fixtures/openshell-v0.0.72 before importing runtime modules; the diff itself does not independently prove the fixture probes execute because OpenShell provider command calls are mocked in these suites.
  • PRA-T2 Acceptance clause — Keep real temporary registry and lifecycle-lock filesystem behavior plus the two nested OpenShell fixture version checks. — add test evidence or identify existing coverage. Temporary HOME/registry filesystem behavior remains real in the lifecycle tests, including import-time HOME handling for registry.REGISTRY_FILE. The OpenShell fixture path is preserved, but nested fixture version execution cannot be confirmed from static diff alone.
  • PRA-T3 Acceptance clause — Contributes to perf(test): reduce test I/O to restore 2–5 minute full-suite runs #6245. — add test evidence or identify existing coverage. The deterministic validation context did not include linked issue perf(test): reduce test I/O to restore 2–5 minute full-suite runs #6245 clauses or comments, so this review could not map issue-level acceptance beyond the PR body's test-performance scope.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

Comment thread test/deepagents-mcp-legacy-lifecycle.test.ts Fixed
Comment thread test/deepagents-mcp-legacy-lifecycle.test.ts Fixed

@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.

🧹 Nitpick comments (5)
test/deepagents-mcp-legacy-lifecycle.test.ts (1)

161-214: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce script-fragment coupling in the legacy Deep Agents lifecycle mock.
These mocks still key off literal snippets from the generated teardown/probe scripts, so harmless refactors can make the test silently fall through. The ownership-matching path already has direct coverage elsewhere, so this suite can focus on lifecycle behavior instead of re-encoding script internals.

🤖 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 `@test/deepagents-mcp-legacy-lifecycle.test.ts` around lines 161 - 214, The
legacy Deep Agents lifecycle mocks are too tightly coupled to exact script
fragments in executeSandboxCommand and executeSandboxExecCommand, which makes
the test brittle to harmless script refactors. Update the mock branching in this
test to assert lifecycle outcomes through stable signals from the lifecycle flow
instead of matching literal teardown/probe snippets, using the
executeSandboxCommand and executeSandboxExecCommand handlers as the main
touchpoints. Keep the existing ownership-matching coverage elsewhere and
simplify this suite to validate registration/removal/rollback behavior without
depending on embedded script internals.

Source: Path instructions

test/mcp-destroy-lifecycle.test.ts (4)

277-277: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test titles lack the required (#1234) issue-ref suffix.

None of the it(...) titles in this file (e.g. `clears a providerless preflighted add during ${method}`, "prepares an absent-sandbox rebuild without adapter exec or provider detach") end with a local issue reference in a (#1234) suffix.

As per coding guidelines, "Root-level integration tests under test/ should import source code, use ESM imports, and use behavior-oriented titles with local issue refs in a final (#1234) suffix."

Also applies to: 306-306, 329-329, 351-351, 368-368, 393-393, 416-416, 449-449, 475-475, 506-506, 529-529, 557-557, 590-590, 628-628, 660-660

🤖 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 `@test/mcp-destroy-lifecycle.test.ts` at line 277, Update the affected
`it(...)` titles in `mcp-destroy-lifecycle.test.ts` so each behavior-oriented
test name ends with the required local issue reference suffix in the form
`(`#1234`)`. Keep the existing intent of titles like `clears a providerless
preflighted add during ${method}` and the other listed cases, but append the
suffix consistently across all matching tests in this file.

Source: Coding guidelines


660-693: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hand-built preparation object bypasses prepareMcpBridgesForDestroy/prepareMcpBridgesForRebuild.

This test constructs the preparation object literal directly (Lines 674-680) instead of deriving it from an actual prepare call. It couples the test to the internal shape of the preparation type and doesn't verify that a real prepareMcpBridgesForDestroy/rebuild call would ever produce this specific combination of fields (e.g., destroyAlreadyPending: true with empty detachedProviderEntries).

🤖 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 `@test/mcp-destroy-lifecycle.test.ts` around lines 660 - 693, The test is
relying on a manually constructed preparation object instead of validating the
real output from the preparation flow. Update the “does not let force delete a
drifted global provider” test to build `preparation` by calling the actual
`prepareMcpBridgesForDestroy` or `prepareMcpBridgesForRebuild` path used by
`finalizeMcpBridgesAfterSandboxDelete`, and then assert the same forced-delete
behavior on that returned object. Keep the assertions on
`finalizeMcpBridgesAfterSandboxDelete`, but avoid hardcoding internal
`preparation` fields so the test stays aligned with the real contract.

157-213: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Mocks reimplement production CLI/credential-resolution logic rather than stubbing it.

runOpenshellProviderCommand.mockImplementation (Lines 157-213) re-derives provider/attach/detach/delete state transitions and status codes that mirror the real OpenShell CLI's behavior, and executeSandboxExecCommand.mockImplementation (Lines 239-264) decodes base64 "proof" scripts and re-implements the credential-resolution/observation logic that production code (buildMcpCredentialRevisionObservationCommand) generates. Because both sides of the test now encode the same assumptions about command shape and decoding, a bug or format drift in the production command-builders could go undetected — the mock will "agree" with whatever it's told to check, rather than exercising the real behavior that the previous subprocess-based tests exercised end-to-end.

Consider trimming these mocks to the minimal surface needed per test (return canned responses per scenario) rather than a full state-machine reimplementation, or add a companion contract test that exercises the actual command-builder output against this mock's parsing to catch drift.

As per path instructions, "Flag copied production algorithms, broad mocks that bypass the behavior under test, and conditionals that make a test pass without exercising its claim."

Also applies to: 239-264

🤖 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 `@test/mcp-destroy-lifecycle.test.ts` around lines 157 - 213, The test mocks
are reimplementing production CLI and credential-resolution behavior instead of
stubbing outcomes, which can hide drift in the real command builders. Simplify
runOpenshellProviderCommand and executeSandboxExecCommand to return canned
responses per scenario and avoid state-machine/base64 parsing logic; keep only
the minimal branching needed for each test case. If you need coverage for the
command shape, add a separate contract test around
buildMcpCredentialRevisionObservationCommand rather than duplicating production
algorithms in the mock implementations.

Source: Path instructions


67-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove unused OpenShell fixture setup Remove MATCHING_OPENSHELL and the process.env.NEMOCLAW_OPENSHELL_BIN assignment here; this test fully mocks runOpenshellProviderCommand, so the fixture binary never gets used.

🤖 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 `@test/mcp-destroy-lifecycle.test.ts` at line 67, Remove the unused OpenShell
fixture setup in this test: the MATCHING_OPENSHELL constant and the
process.env.NEMOCLAW_OPENSHELL_BIN assignment are unnecessary because
mcp-destroy-lifecycle.test.ts fully mocks runOpenshellProviderCommand. Clean up
the test setup so it no longer resolves the fixture binary or mutates the env
var, and keep the mock-based lifecycle assertions intact.
🤖 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.

Nitpick comments:
In `@test/deepagents-mcp-legacy-lifecycle.test.ts`:
- Around line 161-214: The legacy Deep Agents lifecycle mocks are too tightly
coupled to exact script fragments in executeSandboxCommand and
executeSandboxExecCommand, which makes the test brittle to harmless script
refactors. Update the mock branching in this test to assert lifecycle outcomes
through stable signals from the lifecycle flow instead of matching literal
teardown/probe snippets, using the executeSandboxCommand and
executeSandboxExecCommand handlers as the main touchpoints. Keep the existing
ownership-matching coverage elsewhere and simplify this suite to validate
registration/removal/rollback behavior without depending on embedded script
internals.

In `@test/mcp-destroy-lifecycle.test.ts`:
- Line 277: Update the affected `it(...)` titles in
`mcp-destroy-lifecycle.test.ts` so each behavior-oriented test name ends with
the required local issue reference suffix in the form `(`#1234`)`. Keep the
existing intent of titles like `clears a providerless preflighted add during
${method}` and the other listed cases, but append the suffix consistently across
all matching tests in this file.
- Around line 660-693: The test is relying on a manually constructed preparation
object instead of validating the real output from the preparation flow. Update
the “does not let force delete a drifted global provider” test to build
`preparation` by calling the actual `prepareMcpBridgesForDestroy` or
`prepareMcpBridgesForRebuild` path used by
`finalizeMcpBridgesAfterSandboxDelete`, and then assert the same forced-delete
behavior on that returned object. Keep the assertions on
`finalizeMcpBridgesAfterSandboxDelete`, but avoid hardcoding internal
`preparation` fields so the test stays aligned with the real contract.
- Around line 157-213: The test mocks are reimplementing production CLI and
credential-resolution behavior instead of stubbing outcomes, which can hide
drift in the real command builders. Simplify runOpenshellProviderCommand and
executeSandboxExecCommand to return canned responses per scenario and avoid
state-machine/base64 parsing logic; keep only the minimal branching needed for
each test case. If you need coverage for the command shape, add a separate
contract test around buildMcpCredentialRevisionObservationCommand rather than
duplicating production algorithms in the mock implementations.
- Line 67: Remove the unused OpenShell fixture setup in this test: the
MATCHING_OPENSHELL constant and the process.env.NEMOCLAW_OPENSHELL_BIN
assignment are unnecessary because mcp-destroy-lifecycle.test.ts fully mocks
runOpenshellProviderCommand. Clean up the test setup so it no longer resolves
the fixture binary or mutates the env var, and keep the mock-based lifecycle
assertions intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f7f65c28-0d95-4c63-9bca-072c85898ea5

📥 Commits

Reviewing files that changed from the base of the PR and between 7d50eb3 and 21d3ebf.

📒 Files selected for processing (4)
  • test/deepagents-mcp-legacy-lifecycle.test.ts
  • test/deepagents-mcp-runtime-capability.test.ts
  • test/hermes-mcp-startup-probe.test.ts
  • test/mcp-destroy-lifecycle.test.ts

adapterCalls.push(command);
switch (true) {
case command === "/usr/local/bin/deepagents-code --nemoclaw-mcp-capability":
return deepAgentsCapability
adapterCalls.push(command);
switch (true) {
case command === "/usr/local/bin/deepagents-code --nemoclaw-mcp-capability":
return deepAgentsCapability
@cv

cv commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator Author

Advisor follow-up rationale:

  • PRA-T1 / PRA-T2: the two active-restoration cases call restoreMcpBridgesAfterRebuild, which reaches restoreExistingMcpBridgeRuntime and then assertMcpCredentialBoundaryRuntimeVersion. Its default runVersionCommand is the real spawnSync(binary, ["--version"]) implementation. These suites set NEMOCLAW_OPENSHELL_BIN to test/fixtures/openshell-v0.0.72 and do not mock node:child_process, so the destroy and Deep Agents replacement-image cases retain the two real fixture executions. The detailed parser/failure matrix remains covered by mcp-bridge-input-validation.test.ts.
  • The lifecycle suites set their temporary HOME before importing the real registry module, perform normal registry/lock reads and writes, clear that on-disk state before each case, and remove it after the suite. Only OpenShell, policy, gateway, and sandbox-exec boundaries are mocked.
  • PRA-T3: perf(test): reduce test I/O to restore 2–5 minute full-suite runs #6245 is the test-runtime tracking issue. The final focused command passes 52/52 assertions in 2.97 seconds wall-clock versus the 14.25-second local baseline.
  • The four Nemotron items are non-blocking organization suggestions. The shared state is comprehensively reset in beforeEach and environment state is restored after the suite; two independent semantic reviews found no isolation gaps after the OPENSHELL_GATEWAY restoration fix. Splitting the hoisted state or extracting a one-use wait mock would widen the harness without changing coverage or correctness, so no further code churn is planned in this PR.

@cv
cv merged commit 8a55bd5 into main Jul 7, 2026
54 of 63 checks passed
@cv
cv deleted the codex/perf-mcp-lifecycle-tests branch July 7, 2026 06:47
@wscurran wscurran added area: ci CI workflows, checks, release automation, or GitHub Actions area: e2e End-to-end tests, nightly failures, or validation infrastructure chore Build, CI, dependency, or tooling maintenance integration: dcode LangChain Deep Code integration behavior labels Jul 7, 2026
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Run four MCP lifecycle and runtime-capability suites directly in Vitest
instead of cold-starting a Node process for every scenario. The focused
local run falls from 14.25 seconds to 2.97 seconds wall-clock while
preserving all 52 behaviors, real registry filesystem state, and the two
real OpenShell fixture version probes.

## Related Issue

Contributes to NVIDIA#6245.

## Changes

- Replace 55 outer Node isolation children across the destroy, Hermes
startup, and Deep Agents lifecycle/capability suites with direct source
calls and hoisted Vitest mocks.
- Preserve per-scenario provider, attachment, adapter, policy, recovery,
and environment isolation.
- Keep real temporary registry and lifecycle-lock filesystem behavior
plus the two nested OpenShell fixture version checks.
- Leave production modules and genuine crash/process-contract tests
unchanged.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: This only changes internal
test isolation mechanics; MCP commands, configuration, registry
semantics, and runtime behavior are unchanged.
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run --project integration test/mcp-destroy-lifecycle.test.ts
test/hermes-mcp-startup-probe.test.ts
test/deepagents-mcp-legacy-lifecycle.test.ts
test/deepagents-mcp-runtime-capability.test.ts` (4 files, 52/52 tests
passed; 2.65s Vitest / 2.97s wall-clock); `npm run
test-conditionals:scan -- --top 25` (completed; changed files contain
zero `if` statements).
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Carlos Villela <cvillela@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Improved coverage for lifecycle, startup, and runtime capability-probe
flows.
* Reworked multiple suites to run in-process with mocked dependencies
for faster, more reliable execution.
* Expanded assertions around destroy/rebuild/restore behaviors,
including rollback/reattach paths and durable-marker handling.
* Strengthened validation of cleanup and state transitions, and improved
error-message checking to better prevent regressions.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: ci CI workflows, checks, release automation, or GitHub Actions area: e2e End-to-end tests, nightly failures, or validation infrastructure chore Build, CI, dependency, or tooling maintenance integration: dcode LangChain Deep Code integration behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants