Skip to content

test(snapshot): cover create and list flows#5481

Merged
cv merged 1 commit into
mainfrom
test/snapshot-flow-coverage
Jun 16, 2026
Merged

test(snapshot): cover create and list flows#5481
cv merged 1 commit into
mainfrom
test/snapshot-flow-coverage

Conversation

@cv

@cv cv commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds focused snapshot command coverage for successful snapshot creation and list rendering. This improves coverage for the snapshot lifecycle without changing runtime behavior.

Changes

  • Extended src/lib/actions/sandbox/snapshot.test.ts to cover named snapshot creation after liveness and shields checks pass.
  • Covered snapshot list output with virtual versions, names, timestamps, and backup paths.
  • Kept the existing fail-closed shields helper guard coverage.

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)

Verification

  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
    • npx vitest run src/lib/actions/sandbox/snapshot.test.ts --project cli
    • npm run typecheck:cli
  • Full npm test passes (broad runtime changes only)
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • 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)

Note: commit and push hooks were skipped for this test-only branch because the broad local hook suite has unrelated pre-existing CLI failures in this checkout; targeted tests and CLI typecheck passed.


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

Summary by CodeRabbit

  • Tests
    • Comprehensive testing improvements for the sandbox snapshot feature, including expanded mock configurations and new test cases validating named snapshot creation, backup operations, restoration workflows, and output formatting.

@cv cv self-assigned this Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The test file for the sandbox snapshot action is updated to expand Vitest mock coverage. New mocks are introduced for findBackup, listBackups, restoreSandboxState, and isShieldsDown. Two test cases are added: one for named snapshot creation and one for stable snapshot list rendering.

Changes

Sandbox Snapshot Test Expansion

Layer / File(s) Summary
Mock declarations and beforeEach wiring
src/lib/actions/sandbox/snapshot.test.ts
Adds mock variables for findBackup, listBackups, restoreSandboxState, and isShieldsDown; wires them into ../../shields and ../../state/sandbox module mocks; configures concrete beforeEach return values including a structured successful restoreSandboxState response.
Snapshot creation and listing test cases
src/lib/actions/sandbox/snapshot.test.ts
Adjusts the shields-gate-unavailable test to force isShieldsDown to undefined; adds a named-snapshot creation test asserting correct function invocations and console output (version, name, backup path); adds a stable-list test asserting version, name, timestamp, path, and restore-instruction count in combined output.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#5471: Directly connected — both PRs extend sandbox Vitest flows to mock and verify restoreSandboxState and repairMutableConfigPerms as part of backup/restore gating.
  • NVIDIA/NemoClaw#5101: Related at the repairMutableConfigPerms/isShieldsDown mock layer — the retrieved PR introduces calling repairMutableConfigPerms after restoring openclaw.json, which this PR's test mocks wire up.
  • NVIDIA/NemoClaw#5177: Connected through restoreSandboxState mock return values and shields gating, matching the retrieved PR's changes to sandbox/OpenClaw restore behavior around openclaw.json safety decisions.

Suggested labels

area: sandbox, v0.0.66

🐰 A snapshot in time, a backup so bright,
With shields checked and backups in sight,
The mocks are now wired, the tests are aligned,
Each version and name neatly defined —
Hop hop, the list renders just right! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the changeset: adding test coverage for the create and list snapshot flows in the sandbox snapshot feature.
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.

✏️ 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 test/snapshot-flow-coverage

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

@github-code-quality

github-code-quality Bot commented Jun 16, 2026

Copy link
Copy Markdown

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the branch is 96%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File 94a1568 +/-
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 branch is 46%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File 94a1568 +/-
src/lib/state/o...oard-session.ts 90%
src/lib/inference/local.ts 76%
src/lib/sandbox/config.ts 72%
src/lib/onboard/preflight.ts 64%
src/lib/actions...dbox/rebuild.ts 62%
src/lib/state/sandbox.ts 55%
src/lib/actions...licy-channel.ts 52%
src/lib/onboard...er-gpu-patch.ts 50%
src/lib/policy/index.ts 49%
src/lib/onboard.ts 17%

Updated June 16, 2026 00:20 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

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 recommended because this is a tests-only change limited to a unit test file. It cannot affect runtime/user flows unless production code is changed separately.

Optional E2E

  • None.

New E2E recommendations

  • None.

@github-actions

Copy link
Copy Markdown
Contributor

Vitest E2E Scenario Recommendation

Required Vitest E2E scenarios: None
Optional Vitest E2E scenarios: None

Workflow run

Full Vitest E2E advisor summary

Vitest E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required Vitest E2E scenarios

  • None. Only a non-scenario unit test outside test/e2e-scenario/ changed, so this PR cannot affect the Vitest E2E scenario workflow behavior.

Optional Vitest E2E scenarios

  • None.

Relevant changed files

  • None.

@github-actions

Copy link
Copy Markdown
Contributor

PR Review Advisor

Findings: 0 needs attention, 1 worth checking, 0 nice ideas
Top item: PR review advisor unavailable

Review findings

🛠️ Needs attention

  • None.

🔎 Worth checking

  • PR review advisor unavailable: The automated advisor could not complete: PR review advisor SDK provider error: orient-drift: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; security: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; acceptance-correctness-tests: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; synthesize-json: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>
    • Recommendation: Re-run the PR Review Advisor or perform a manual review.
    • Evidence: PR review advisor SDK provider error: orient-drift: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; security: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; acceptance-correctness-tests: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>; synthesize-json: 403 <html> <head><title>403 Forbidden</title></head> <body> <center><h1>403 Forbidden</h1></center> </body> </html>

🌱 Nice ideas

  • None.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@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: 1

Caution

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

⚠️ Outside diff range comments (1)
src/lib/actions/sandbox/snapshot.test.ts (1)

103-116: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Ensure mocked isShieldsDown is restored in a finally block.

Line 104 mutates a shared mocked export and restores it at Line 115. If an assertion throws before Line 115, later tests can inherit undefined and fail for the wrong reason.

Suggested patch
   it("refuses snapshot creation before backup when the shields gate helper is unavailable", async () => {
     const consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
     const shields = await import("../../shields");
-    vi.mocked(shields).isShieldsDown = undefined as never;
-    const { runSandboxSnapshot } = await import("./snapshot");
-
-    await expect(runSandboxSnapshot("alpha", { kind: "create" })).rejects.toMatchObject({
-      exitCode: 1,
-    });
-
-    expect(backupSandboxStateMock).not.toHaveBeenCalled();
-    expect(consoleError.mock.calls.flat().join("\n")).toContain(
-      "Cannot verify shields state. Refusing to create snapshot.",
-    );
-    vi.mocked(shields).isShieldsDown = isShieldsDownMock as never;
+    const originalIsShieldsDown = vi.mocked(shields).isShieldsDown;
+    vi.mocked(shields).isShieldsDown = undefined as never;
+    try {
+      const { runSandboxSnapshot } = await import("./snapshot");
+      await expect(runSandboxSnapshot("alpha", { kind: "create" })).rejects.toMatchObject({
+        exitCode: 1,
+      });
+      expect(backupSandboxStateMock).not.toHaveBeenCalled();
+      expect(consoleError.mock.calls.flat().join("\n")).toContain(
+        "Cannot verify shields state. Refusing to create snapshot.",
+      );
+    } finally {
+      vi.mocked(shields).isShieldsDown = originalIsShieldsDown;
+    }
   });
🤖 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/lib/actions/sandbox/snapshot.test.ts` around lines 103 - 116, The test
mutates the shared `shields.isShieldsDown` mock at the beginning and restores it
at the end, but if any assertion fails before the restoration line, the mock
state is not cleaned up, causing subsequent tests to fail. Wrap the test logic
(from the mock assignment through the assertions) in a try-finally block, moving
the restoration of `vi.mocked(shields).isShieldsDown = isShieldsDownMock as
never;` into the finally block to ensure it always executes regardless of
whether assertions pass or fail.
🤖 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/lib/actions/sandbox/snapshot.test.ts`:
- Around line 148-174: The test fixture for the snapshot list includes timestamp
data for both snapshots (2026-06-01T00:00:00.000Z and 2026-06-02T00:00:00.000Z),
but the test expectations do not validate that these timestamps are actually
rendered in the console output. Add explicit expect assertions to verify that
both timestamp values are included in the output, similar to the existing
assertions that check for version names, snapshot names, and paths. This will
ensure that any regression in timestamp rendering will be caught by the test.

---

Outside diff comments:
In `@src/lib/actions/sandbox/snapshot.test.ts`:
- Around line 103-116: The test mutates the shared `shields.isShieldsDown` mock
at the beginning and restores it at the end, but if any assertion fails before
the restoration line, the mock state is not cleaned up, causing subsequent tests
to fail. Wrap the test logic (from the mock assignment through the assertions)
in a try-finally block, moving the restoration of
`vi.mocked(shields).isShieldsDown = isShieldsDownMock as never;` into the
finally block to ensure it always executes regardless of whether assertions pass
or fail.
🪄 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: CHILL

Plan: Enterprise

Run ID: d4bd4345-ce4a-48da-b95b-4e495ccdc1d7

📥 Commits

Reviewing files that changed from the base of the PR and between 2e8e519 and 94a1568.

📒 Files selected for processing (1)
  • src/lib/actions/sandbox/snapshot.test.ts

Comment on lines +148 to 174
it("renders a stable snapshot list with versions, names, timestamps, and paths", async () => {
const consoleLog = vi.spyOn(console, "log").mockImplementation(() => {});
listBackupsMock.mockReturnValue([
{
snapshotVersion: 1,
name: "initial",
timestamp: "2026-06-01T00:00:00.000Z",
backupPath: "/tmp/alpha/v1",
},
{
snapshotVersion: 2,
name: null,
timestamp: "2026-06-02T00:00:00.000Z",
backupPath: "/tmp/alpha/v2",
},
]);
const { runSandboxSnapshot } = await import("./snapshot");

await runSandboxSnapshot("alpha", { kind: "list" });

const output = consoleLog.mock.calls.flat().join("\n");
expect(output).toContain("Snapshots for 'alpha'");
expect(output).toContain("v1");
expect(output).toContain("initial");
expect(output).toContain("/tmp/alpha/v2");
expect(output).toContain("2 snapshot(s). Restore with:");
});

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

Add explicit timestamp assertions in the snapshot-list test.

The fixtures include timestamps (Lines 154 and 160), but the expectations never validate timestamp rendering. A regression in timestamp output could slip through unnoticed.

Suggested patch
     expect(output).toContain("Snapshots for 'alpha'");
     expect(output).toContain("v1");
     expect(output).toContain("initial");
+    expect(output).toContain("2026-06-01");
+    expect(output).toContain("2026-06-02");
     expect(output).toContain("/tmp/alpha/v2");
     expect(output).toContain("2 snapshot(s). Restore with:");
📝 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
it("renders a stable snapshot list with versions, names, timestamps, and paths", async () => {
const consoleLog = vi.spyOn(console, "log").mockImplementation(() => {});
listBackupsMock.mockReturnValue([
{
snapshotVersion: 1,
name: "initial",
timestamp: "2026-06-01T00:00:00.000Z",
backupPath: "/tmp/alpha/v1",
},
{
snapshotVersion: 2,
name: null,
timestamp: "2026-06-02T00:00:00.000Z",
backupPath: "/tmp/alpha/v2",
},
]);
const { runSandboxSnapshot } = await import("./snapshot");
await runSandboxSnapshot("alpha", { kind: "list" });
const output = consoleLog.mock.calls.flat().join("\n");
expect(output).toContain("Snapshots for 'alpha'");
expect(output).toContain("v1");
expect(output).toContain("initial");
expect(output).toContain("/tmp/alpha/v2");
expect(output).toContain("2 snapshot(s). Restore with:");
});
it("renders a stable snapshot list with versions, names, timestamps, and paths", async () => {
const consoleLog = vi.spyOn(console, "log").mockImplementation(() => {});
listBackupsMock.mockReturnValue([
{
snapshotVersion: 1,
name: "initial",
timestamp: "2026-06-01T00:00:00.000Z",
backupPath: "/tmp/alpha/v1",
},
{
snapshotVersion: 2,
name: null,
timestamp: "2026-06-02T00:00:00.000Z",
backupPath: "/tmp/alpha/v2",
},
]);
const { runSandboxSnapshot } = await import("./snapshot");
await runSandboxSnapshot("alpha", { kind: "list" });
const output = consoleLog.mock.calls.flat().join("\n");
expect(output).toContain("Snapshots for 'alpha'");
expect(output).toContain("v1");
expect(output).toContain("initial");
expect(output).toContain("2026-06-01");
expect(output).toContain("2026-06-02");
expect(output).toContain("/tmp/alpha/v2");
expect(output).toContain("2 snapshot(s). Restore with:");
});
🤖 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/lib/actions/sandbox/snapshot.test.ts` around lines 148 - 174, The test
fixture for the snapshot list includes timestamp data for both snapshots
(2026-06-01T00:00:00.000Z and 2026-06-02T00:00:00.000Z), but the test
expectations do not validate that these timestamps are actually rendered in the
console output. Add explicit expect assertions to verify that both timestamp
values are included in the output, similar to the existing assertions that check
for version names, snapshot names, and paths. This will ensure that any
regression in timestamp rendering will be caught by the test.

@cv cv merged commit d1e0c81 into main Jun 16, 2026
46 checks passed
@cv cv deleted the test/snapshot-flow-coverage branch June 16, 2026 00:42
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.

1 participant