test(sandbox): cover the e2b and runloop cloud bucket mount strategies - #4240
test(sandbox): cover the e2b and runloop cloud bucket mount strategies#4240abhay-codes07 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cc9b8bf2b5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| _assert_runloop_session, | ||
| _ensure_fuse_support, | ||
| ) | ||
| from agents.extensions.sandbox.runloop.sandbox import RunloopSandboxSessionState |
There was a problem hiding this comment.
Avoid requiring Runloop's optional SDK for mount tests
Importing RunloopSandboxSessionState pulls in agents.extensions.sandbox.runloop.sandbox, which imports runloop_api_client.types at module import time. In environments that run these mount tests without installing the optional [runloop] extra, collection now fails before any test can run, even though the mount strategy under test does not need the provider SDK; keep the fake state as a lightweight object or otherwise avoid importing runloop.sandbox here.
Useful? React with 👍 / 👎.
seratch
left a comment
There was a problem hiding this comment.
Thanks for adding coverage for these provider mount lifecycles. The direction is good, but before merging, please make the lifecycle tests prove the ordering they claim. Currently session.exec_calls and delegate.calls are recorded separately, so moving delegation before FUSE/rclone preparation would still leave the tests green.
Please use a shared event timeline, or assert the completed preparation state from inside the delegate, for both E2B and Runloop activate and restore_after_snapshot. Please also assert that the NFS activation path still reaches the delegate with the NFS pattern.
Adds lifecycle and error-branch coverage for the e2b and runloop cloud bucket mount strategies, taking both mounts.py files to 100%. The strategy lifecycle was previously untested. Nothing pinned what activate, deactivate, teardown_for_snapshot or restore_after_snapshot actually do, and those paths normally need a live provider account to reach. Now pinned, for both providers: - The session guard runs before any command is issued, for all four lifecycle methods, so a foreign session cannot reach the sandbox. - activate prepares FUSE and rclone before delegating, and hands the delegate the session-resolved pattern carrying --allow-other. - nfs skips FUSE preparation, still requires rclone, and reaches the delegate with the nfs pattern left untouched. - restore_after_snapshot re-runs preparation, since a restored sandbox may be a fresh container. - deactivate and teardown_for_snapshot issue no setup commands. - Every failure branch of runloop's _ensure_fuse_support, each asserting the error context so the diagnostics stay useful. Ordering is asserted on a single timeline shared by the fake session and the delegate recorder, so moving delegation ahead of preparation fails the tests rather than passing silently. Verified by mutating each call site and confirming the tests go red. Additive only: every pre-existing test in test_runloop_mounts.py is retained, and the new cases reuse that file's existing fake session helpers so the optional runloop_api_client dependency is not imported. No source changes.
cc9b8bf to
5f1ef3a
Compare
|
Thanks both, and apologies: the review caught a real problem, but there was a worse one underneath it that I should flag first. I deleted nine existing tests. The branch is now rebased on On the ordering, @seratch: you were right, and the tests were passing for the wrong reason.
assert session.events[-1] == "delegate:activate"
prepared = session.events[:-1]
assert any("/dev/fuse" in event for event in prepared)
assert any("rclone" in event for event in prepared)I confirmed this is load-bearing by mutating the source rather than trusting it. Moving delegation ahead of preparation in
Under the old assertions all of those stayed green, which is exactly the gap you described. NFS now asserts it reaches the delegate carrying the NFS pattern, rather than only asserting FUSE setup was skipped: assert session.events[-1] == "delegate:activate"
assert [name for name, _ in delegate.calls] == ["activate"]
delegated_pattern = delegate.calls[0][1]
assert delegated_pattern.mode == "nfs"
assert "--allow-other" not in delegated_pattern.extra_argsThe last line pins the other half of On the optional import, @chatgpt-codex-connector: correct, and restoring the original file resolves it. Verification, on the rebased branch:
Happy to split the coverage additions out from the ordering rework if that is easier to review. |
|
Note To use Codex here, create an environment for this repo. |
|
The requested changes are pushed. Saw your note on #4246 about bandwidth, so no rush here either. Happy to leave this until you have time. |
Summary
e2b/mounts.pyandrunloop/mounts.pywere the two least covered files in the sandbox providers. Both are now at 100%.extensions/sandbox/e2b/mounts.pyextensions/sandbox/runloop/mounts.pyThe gap was the strategy lifecycle. Only the session guards, the e2b FUSE happy path and rclone installation were exercised, so nothing pinned what
activate,deactivate,teardown_for_snapshotorrestore_after_snapshotactually do. These are also the hardest paths to exercise by hand, since reaching them normally needs a live provider account.Behaviour now pinned, for both providers:
activateprepares FUSE and rclone before delegating, and hands the delegate the session-resolved pattern carrying--allow-otherrather than the raw pattern.nfsmode skips FUSE preparation, still requires rclone, and reaches the delegate with the nfs pattern left untouched.restore_after_snapshotre-runs preparation, since a restored sandbox may be a fresh container.deactivateandteardown_for_snapshotissue no setup commands, so unmounting never tries to install tooling on the way out._ensure_fuse_support: missing/dev/fuse, missing kernel module, missingapt-get, a failed install, fusermount still absent after installing, and a failed chmod. Each asserts the errorcontextso the diagnostics stay useful.No source changes. Every assertion matches current behaviour, so this is a characterisation of what the code does today.
Ordering is asserted on a shared timeline
Recording delegate calls separately from exec calls would not have constrained their relative order. Both fakes record into one ordered
session.eventslist, and the delegate hook appends to that same list, so delegation has to be the final entry.I checked that this is load-bearing by mutating the source and confirming the tests go red:
activatedelegates before preparationrestore_after_snapshotdelegates before preparationactivate+restore_after_snapshotdelegate before preparationTest plan
Additions to
tests/extensions/sandbox/test_runloop_mounts.pyandtests/extensions/sandbox/test_e2b.py. Every pre-existing test in both files is retained, and the new cases reuse the existing fake session helpers, so the optionalrunloop_api_clientdependency is not imported. I verified that by blocking the module with asys.meta_pathhook and confirming all added cases still pass. The delegate is stubbed viamonkeypatchonInContainerMountStrategyso the tests assert sequencing without needing a real mount.make formatmake lintmake mypymain, none in the touched filesmake pyrightuv run pytest tests/extensions/sandbox/make testsThe full suite run was done on Windows, where a set of sandbox symlink and tracing timing tests fail independently of this change. I diffed the failing set against clean
mainin the same environment: identical, 54 either way, with 34 more tests passing on this branch.Issue number
None. This is coverage work on the sandbox providers rather than a reported defect.
Checks
.agents/skills/code-change-verification/scripts/run.sh/reviewbefore submitting this PRThe verification script is a bash script that shells out to
make. I ran the underlying steps individually instead, with the results above.