Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions sandboxes/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ async def _test():
providers_to_test = [provider] if provider else ["e2b", "modal", "daytona"]

results = []
exit_code = 0
for p_name in providers_to_test:
click.echo(f"\n🔬 Testing {p_name}...")

Expand All @@ -461,19 +462,23 @@ async def _test():
# Test execute
click.echo(" Executing command...")
result = await p.execute_command(sandbox.id, "echo 'Hello from CLI test'")
if result.success and "Hello from CLI test" in result.stdout:
working = result.success and "Hello from CLI test" in result.stdout
if working:
click.echo(" ✅ Execution successful")
else:
click.echo(" ❌ Execution failed", err=True)
exit_code = 1

# Test destroy
click.echo(" Destroying sandbox...")
await p.destroy_sandbox(sandbox.id)
if not await p.destroy_sandbox(sandbox.id):
raise RuntimeError("Failed to destroy probe sandbox")
click.echo(" ✅ Destroyed")

results.append((p_name, "✅ Working"))
results.append((p_name, "✅ Working" if working else "❌ Failed: command test"))

except Exception as e:
exit_code = 1
click.echo(f" ❌ Error: {e}", err=True)
results.append((p_name, f"❌ Failed: {str(e)[:50]}"))

Expand All @@ -484,7 +489,9 @@ async def _test():
for p_name, status in results:
click.echo(f"{p_name:10} {status}")

asyncio.run(_test())
return exit_code

sys.exit(asyncio.run(_test()))


@cli.command()
Expand Down
48 changes: 48 additions & 0 deletions tests/test_cli_test_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import pytest
from click.testing import CliRunner

from sandboxes.base import ExecutionResult, Sandbox, SandboxState
from sandboxes.cli import cli


class ProbeProvider:
def __init__(self, exit_code, stdout, destroyed=True):
self.result = ExecutionResult(exit_code, stdout, "")
self.destroyed = destroyed

async def create_sandbox(self, config):
return Sandbox(id="probe", provider="probe", state=SandboxState.RUNNING)

async def execute_command(self, *args):
return self.result

async def destroy_sandbox(self, sandbox_id):
return self.destroyed


@pytest.mark.parametrize("exit_code,stdout", [(1, ""), (0, "wrong output")])
def test_failed_provider_probe_is_reported_as_failure(monkeypatch, exit_code, stdout):
monkeypatch.setattr("sandboxes.cli.get_provider", lambda name: ProbeProvider(exit_code, stdout))
result = CliRunner().invoke(cli, ["test", "--provider", "e2b"])
assert result.exit_code == 1
assert "Working" not in result.stdout


def test_successful_provider_probe_returns_zero(monkeypatch):
monkeypatch.setattr(
"sandboxes.cli.get_provider", lambda name: ProbeProvider(0, "Hello from CLI test")
)
result = CliRunner().invoke(cli, ["test", "--provider", "e2b"])
assert result.exit_code == 0
assert "Working" in result.stdout


def test_failed_probe_cleanup_returns_nonzero(monkeypatch):
monkeypatch.setattr(
"sandboxes.cli.get_provider",
lambda name: ProbeProvider(0, "Hello from CLI test", destroyed=False),
)
result = CliRunner().invoke(cli, ["test", "--provider", "e2b"])
assert result.exit_code == 1
assert "Destroyed" not in result.stdout
assert "Working" not in result.stdout