Skip to content

chore(test): migrate root test suite from node:test to vitest#653

Open
cv wants to merge 4 commits intoNVIDIA:mainfrom
cv:chore/migrate-tests-to-vitest
Open

chore(test): migrate root test suite from node:test to vitest#653
cv wants to merge 4 commits intoNVIDIA:mainfrom
cv:chore/migrate-tests-to-vitest

Conversation

@cv
Copy link
Contributor

@cv cv commented Mar 22, 2026

Summary

  • Migrate all 21 root test files from node:test + node:assert/strict to Vitest with global expect() matchers
  • Add vitest devDependency and root vitest.config.ts (with globals: true)
  • Update npm test script from node --test to vitest run
  • Update CI workflow (pr.yaml) to use npx vitest run for root tests

What changed in test files

All assertion calls converted via jscodeshift AST codemod (no regex):

Before (node:assert/strict) After (Vitest)
assert.equal(a, b) expect(a).toBe(b)
assert.deepEqual(a, b) expect(a).toEqual(b)
assert.ok(expr) expect(expr).toBeTruthy()
assert.match(s, re) expect(s).toMatch(re)
assert.throws(fn, re) expect(fn).toThrow(re)
assert.fail(msg) expect.unreachable(msg)

What didn't change

  • Zero production code changes (bin/, scripts/, nemoclaw/)
  • Test logic and structure unchanged — only the assertion API surface was swapped
  • The nemoclaw/ plugin keeps its own vitest.config.ts (no overlap)

Motivation

The nemoclaw/ TypeScript plugin already uses Vitest. This unifies the test runner across the project, giving us one expect() API, one reporter, and one config format.

Test plan

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Switched the project to run tests with Vitest, updated the default test script, added a Vitest configuration for consistent discovery/globals, and optimized CI install to use a reproducible install command.
  • Tests

    • Rewrote test suites to use the test runner’s global assertion style (expect) across many tests; test behavior and expectations remain unchanged.

@coderabbitai
Copy link

coderabbitai bot commented Mar 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaces Node's built-in test runner with Vitest: adds vitest.config.ts (globals + test include), changes CI/package scripts to use npm ci and Vitest, and updates test files to remove node:test/node:assert/strict imports and use expect matchers instead.

Changes

Cohort / File(s) Summary
CI & package
\.github/workflows/pr.yaml, package.json
CI step now uses npm ci; root test invocation changed from node --test to npx vitest run / vitest run; vitest@^3.1.1 added to devDependencies.
Vitest config
vitest.config.ts
New Vitest config added: globals: true and test.include set to test/**/*.test.js.
Test suites (assertion API migration)
test/*.test.js, test/.../*.test.js
Removed node:test and node:assert/strict imports across tests and replaced assert.* usages with Vitest/Jest-style expect matchers (toBe, toEqual, toMatch, toBeTruthy, toThrow, unreachable, etc.). Test logic and expected values unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code with nimble feet,

Swapped old assert for expect so neat.
Vitest now hums where tests used to run,
CI claps softly — a job well done. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: migrating the root test suite from Node's built-in test framework to Vitest, which is the primary objective of this pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link

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

🧹 Nitpick comments (2)
test/setup-sandbox-name.test.js (1)

18-20: Consider using toContain() for string inclusion checks.

The pattern expect(content.includes('...')).toBeTruthy() works, but Vitest provides toContain() for more readable string assertions with better error messages on failure.

♻️ Example refactor
   it("accepts sandbox name as $1 with default", () => {
-    expect(content.includes('SANDBOX_NAME="${1:-nemoclaw}"')).toBeTruthy();
+    expect(content).toContain('SANDBOX_NAME="${1:-nemoclaw}"');
   });

This pattern could also apply to lines 47-48.

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

In `@test/setup-sandbox-name.test.js` around lines 18 - 20, Replace the boolean
inclusion assertions with Vitest's clearer string matcher: in the test case
it("accepts sandbox name as $1 with default", ...) change the assertion from
expect(content.includes('SANDBOX_NAME="${1:-nemoclaw}"')).toBeTruthy() to using
expect(content).toContain('SANDBOX_NAME="${1:-nemoclaw}"'); do the same refactor
for the other similar assertions referenced around the test (the lines near the
second occurrence at lines ~47-48) so all string inclusion checks use
toContain() instead of includes(...).toBeTruthy().
test/onboard-readiness.test.js (1)

12-14: Consider using toBeFalsy() instead of negating inside expect().

The pattern expect(!condition).toBeTruthy() works but is less idiomatic than expect(condition).toBeFalsy(). This applies to multiple assertions in this file (lines 13, 17-18, 29, 37, 41-43, 54, 66-68, 83, 116-118, 146-156, 167).

♻️ Example refactor
   it("rejects NotReady sandbox", () => {
-    expect(!isSandboxReady("my-assistant   NotReady   init failed", "my-assistant")).toBeTruthy();
+    expect(isSandboxReady("my-assistant   NotReady   init failed", "my-assistant")).toBeFalsy();
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/onboard-readiness.test.js` around lines 12 - 14, Several tests use the
pattern expect(!isSandboxReady(...)).toBeTruthy(), which is less idiomatic;
update each such assertion to assert the positive condition with toBeFalsy()
instead (e.g., replace expect(!isSandboxReady("my-assistant   NotReady   init
failed", "my-assistant")).toBeTruthy() with expect(isSandboxReady("my-assistant 
NotReady   init failed", "my-assistant")).toBeFalsy()). Apply this change for
all occurrences referencing isSandboxReady in this test file (the instances
noted in the review: lines around 13, 17-18, 29, 37, 41-43, 54, 66-68, 83,
116-118, 146-156, 167) so assertions read
expect(isSandboxReady(...)).toBeFalsy() instead of negating inside expect().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/onboard-readiness.test.js`:
- Around line 12-14: Several tests use the pattern
expect(!isSandboxReady(...)).toBeTruthy(), which is less idiomatic; update each
such assertion to assert the positive condition with toBeFalsy() instead (e.g.,
replace expect(!isSandboxReady("my-assistant   NotReady   init failed",
"my-assistant")).toBeTruthy() with expect(isSandboxReady("my-assistant  
NotReady   init failed", "my-assistant")).toBeFalsy()). Apply this change for
all occurrences referencing isSandboxReady in this test file (the instances
noted in the review: lines around 13, 17-18, 29, 37, 41-43, 54, 66-68, 83,
116-118, 146-156, 167) so assertions read
expect(isSandboxReady(...)).toBeFalsy() instead of negating inside expect().

In `@test/setup-sandbox-name.test.js`:
- Around line 18-20: Replace the boolean inclusion assertions with Vitest's
clearer string matcher: in the test case it("accepts sandbox name as $1 with
default", ...) change the assertion from
expect(content.includes('SANDBOX_NAME="${1:-nemoclaw}"')).toBeTruthy() to using
expect(content).toContain('SANDBOX_NAME="${1:-nemoclaw}"'); do the same refactor
for the other similar assertions referenced around the test (the lines near the
second occurrence at lines ~47-48) so all string inclusion checks use
toContain() instead of includes(...).toBeTruthy().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7b3e343b-cbc7-48fd-9997-d4b1ccb328fc

📥 Commits

Reviewing files that changed from the base of the PR and between 2ce2905 and ab09624.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (24)
  • .github/workflows/pr.yaml
  • package.json
  • test/cli.test.js
  • test/credentials.test.js
  • test/inference-config.test.js
  • test/install-preflight.test.js
  • test/local-inference.test.js
  • test/nim.test.js
  • test/onboard-readiness.test.js
  • test/onboard-selection.test.js
  • test/onboard.test.js
  • test/platform.test.js
  • test/policies.test.js
  • test/preflight.test.js
  • test/registry.test.js
  • test/runner.test.js
  • test/runtime-shell.test.js
  • test/security-c2-dockerfile-injection.test.js
  • test/security-c4-manifest-traversal.test.js
  • test/service-env.test.js
  • test/setup-sandbox-name.test.js
  • test/smoke-macos-install.test.js
  • test/uninstall.test.js
  • vitest.config.ts

@cv cv force-pushed the chore/migrate-tests-to-vitest branch 2 times, most recently from 362ee37 to 0e95f24 Compare March 22, 2026 18:15
cv and others added 2 commits March 22, 2026 11:21
Unify the test runner across the project — the nemoclaw/ TypeScript
plugin already uses Vitest, and this brings the root test/ suite in
line.

Changes:
- Replace node:test describe/it/beforeEach/afterEach with vitest globals
- Replace node:assert/strict with vitest expect() matchers:
  assert.equal        → expect().toBe()
  assert.deepEqual    → expect().toEqual()
  assert.ok           → expect().toBeTruthy()
  assert.match        → expect().toMatch()
  assert.throws       → expect().toThrow()
  assert.fail         → expect.unreachable()
- Add vitest devDependency and root vitest.config.ts
- Update npm test script from `node --test` to `vitest run`

All 21 test files migrated via jscodeshift AST codemod (no regex).
Zero changes to production code in bin/ or scripts/.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The root test suite was migrated from node:test to vitest in the
previous commit. Update the CI workflow to match.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cv cv force-pushed the chore/migrate-tests-to-vitest branch from 0e95f24 to a95e32b Compare March 22, 2026 18:21
Copy link

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

🧹 Nitpick comments (2)
test/onboard-selection.test.js (1)

78-84: Consider toSatisfy() for predicate-based array checks.

The current pattern works but Vitest's toSatisfy() matcher can make the intent clearer:

♻️ Suggested improvement
-    expect(
-      payload.lines.some((line) => line.includes("Detected local inference option"))
-    ).toBeTruthy();
+    expect(payload.lines).toSatisfy((lines) =>
+      lines.some((line) => line.includes("Detected local inference option"))
+    );

Alternatively, keep as-is since .some().toBeTruthy() is a common pattern and the current form is clear enough.

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

In `@test/onboard-selection.test.js` around lines 78 - 84, Replace the three
assertions that use payload.lines.some(...).toBeTruthy() with Vitest's predicate
matcher to make intent clearer: use expect(payload.lines).toSatisfy(lines =>
lines.some(line => line.includes("Detected local inference option"))) and
similarly for "Press Enter to keep the cloud default" and "Cloud models:" so
each check references payload.lines and the predicate string; update the three
assertions in test/onboard-selection.test.js accordingly.
test/nim.test.js (1)

14-17: Consider using Vitest's toBeTypeOf() for cleaner type assertions.

The current pattern expect(typeof x === "number").toBeTruthy() works but is verbose. Vitest provides a more idiomatic matcher:

♻️ Suggested improvement
       for (const m of nim.listModels()) {
         expect(m.name).toBeTruthy();
         expect(m.image).toBeTruthy();
-        expect(typeof m.minGpuMemoryMB === "number").toBeTruthy();
+        expect(m.minGpuMemoryMB).toBeTypeOf("number");
         expect(m.minGpuMemoryMB > 0).toBeTruthy();
       }

The same pattern appears at lines 43-45 for gpu.count, gpu.totalMemoryMB, and gpu.nimCapable.

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

In `@test/nim.test.js` around lines 14 - 17, Replace verbose typeof checks with
Vitest's toBeTypeOf matcher: for the model property m.minGpuMemoryMB, use
expect(m.minGpuMemoryMB).toBeTypeOf("number") and for the GPU properties use
expect(gpu.count).toBeTypeOf("number"),
expect(gpu.totalMemoryMB).toBeTypeOf("number"), and
expect(gpu.nimCapable).toBeTypeOf("boolean"); locate these assertions around the
existing checks for m.minGpuMemoryMB and gpu.count/totalMemoryMB/nimCapable and
swap the typeof-expression + toBeTruthy() patterns for the cleaner toBeTypeOf
calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/nim.test.js`:
- Around line 14-17: Replace verbose typeof checks with Vitest's toBeTypeOf
matcher: for the model property m.minGpuMemoryMB, use
expect(m.minGpuMemoryMB).toBeTypeOf("number") and for the GPU properties use
expect(gpu.count).toBeTypeOf("number"),
expect(gpu.totalMemoryMB).toBeTypeOf("number"), and
expect(gpu.nimCapable).toBeTypeOf("boolean"); locate these assertions around the
existing checks for m.minGpuMemoryMB and gpu.count/totalMemoryMB/nimCapable and
swap the typeof-expression + toBeTruthy() patterns for the cleaner toBeTypeOf
calls.

In `@test/onboard-selection.test.js`:
- Around line 78-84: Replace the three assertions that use
payload.lines.some(...).toBeTruthy() with Vitest's predicate matcher to make
intent clearer: use expect(payload.lines).toSatisfy(lines => lines.some(line =>
line.includes("Detected local inference option"))) and similarly for "Press
Enter to keep the cloud default" and "Cloud models:" so each check references
payload.lines and the predicate string; update the three assertions in
test/onboard-selection.test.js accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 849070db-b767-42f6-a15a-984ef4432cc8

📥 Commits

Reviewing files that changed from the base of the PR and between 0e95f24 and a95e32b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (24)
  • .github/workflows/pr.yaml
  • package.json
  • test/cli.test.js
  • test/credentials.test.js
  • test/inference-config.test.js
  • test/install-preflight.test.js
  • test/local-inference.test.js
  • test/nim.test.js
  • test/onboard-readiness.test.js
  • test/onboard-selection.test.js
  • test/onboard.test.js
  • test/platform.test.js
  • test/policies.test.js
  • test/preflight.test.js
  • test/registry.test.js
  • test/runner.test.js
  • test/runtime-shell.test.js
  • test/security-c2-dockerfile-injection.test.js
  • test/security-c4-manifest-traversal.test.js
  • test/service-env.test.js
  • test/setup-sandbox-name.test.js
  • test/smoke-macos-install.test.js
  • test/uninstall.test.js
  • vitest.config.ts
✅ Files skipped from review due to trivial changes (12)
  • package.json
  • .github/workflows/pr.yaml
  • vitest.config.ts
  • test/service-env.test.js
  • test/local-inference.test.js
  • test/cli.test.js
  • test/onboard.test.js
  • test/preflight.test.js
  • test/policies.test.js
  • test/inference-config.test.js
  • test/runner.test.js
  • test/uninstall.test.js
🚧 Files skipped from review as they are similar to previous changes (9)
  • test/credentials.test.js
  • test/registry.test.js
  • test/platform.test.js
  • test/runtime-shell.test.js
  • test/setup-sandbox-name.test.js
  • test/security-c2-dockerfile-injection.test.js
  • test/onboard-readiness.test.js
  • test/security-c4-manifest-traversal.test.js
  • test/smoke-macos-install.test.js

cv and others added 2 commits March 22, 2026 11:39
npm install with a cached npm store can skip platform-specific optional
dependencies (like @rollup/rollup-linux-x64-gnu) that vitest needs.
npm ci does a clean install from the lockfile, ensuring all platform
binaries are resolved.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The macOS-generated lockfile is missing platform-specific optional
dependencies (@rollup/rollup-linux-x64-gnu) needed by vitest on Linux.
npm 10 on macOS only records lockfile entries for the current platform.

Fix by reverting to the upstream lockfile and using `npm install`
(not `npm ci`) in CI, so npm resolves vitest and its platform-specific
deps fresh on each run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants