test(daytona): add unit and integration tests for Daytona backend
Unit tests cover cwd resolution, sandbox persistence/resume, cleanup, command execution, resource conversion, interrupt handling, retry exhaustion, and sandbox readiness checks. Integration tests verify basic commands, filesystem ops, session persistence, and task isolation against a live Daytona API. Signed-off-by: rovle <lovre.pesut@gmail.com>
This commit is contained in:
parent
ea2f7ef2f6
commit
d5efb82c7c
2 changed files with 463 additions and 0 deletions
123
tests/integration/test_daytona_terminal.py
Normal file
123
tests/integration/test_daytona_terminal.py
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
"""Integration tests for the Daytona terminal backend.
|
||||
|
||||
Requires DAYTONA_API_KEY to be set. Run with:
|
||||
TERMINAL_ENV=daytona pytest tests/integration/test_daytona_terminal.py -v
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
# Skip entire module if no API key
|
||||
if not os.getenv("DAYTONA_API_KEY"):
|
||||
pytest.skip("DAYTONA_API_KEY not set", allow_module_level=True)
|
||||
|
||||
# Import terminal_tool via importlib to avoid tools/__init__.py side effects
|
||||
import importlib.util
|
||||
|
||||
parent_dir = Path(__file__).parent.parent.parent
|
||||
sys.path.insert(0, str(parent_dir))
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"terminal_tool", parent_dir / "tools" / "terminal_tool.py"
|
||||
)
|
||||
terminal_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(terminal_module)
|
||||
|
||||
terminal_tool = terminal_module.terminal_tool
|
||||
cleanup_vm = terminal_module.cleanup_vm
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _force_daytona(monkeypatch):
|
||||
monkeypatch.setenv("TERMINAL_ENV", "daytona")
|
||||
monkeypatch.setenv("TERMINAL_CONTAINER_DISK", "10240")
|
||||
monkeypatch.setenv("TERMINAL_CONTAINER_PERSISTENT", "false")
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def task_id(request):
|
||||
"""Provide a unique task_id and clean up the sandbox after the test."""
|
||||
tid = f"daytona_test_{request.node.name}"
|
||||
yield tid
|
||||
cleanup_vm(tid)
|
||||
|
||||
|
||||
def _run(command, task_id, **kwargs):
|
||||
result = terminal_tool(command, task_id=task_id, **kwargs)
|
||||
return json.loads(result)
|
||||
|
||||
|
||||
class TestDaytonaBasic:
|
||||
def test_echo(self, task_id):
|
||||
r = _run("echo 'Hello from Daytona!'", task_id)
|
||||
assert r["exit_code"] == 0
|
||||
assert "Hello from Daytona!" in r["output"]
|
||||
|
||||
def test_python_version(self, task_id):
|
||||
r = _run("python3 --version", task_id)
|
||||
assert r["exit_code"] == 0
|
||||
assert "Python" in r["output"]
|
||||
|
||||
def test_nonzero_exit(self, task_id):
|
||||
r = _run("exit 42", task_id)
|
||||
assert r["exit_code"] == 42
|
||||
|
||||
def test_os_info(self, task_id):
|
||||
r = _run("uname -a", task_id)
|
||||
assert r["exit_code"] == 0
|
||||
assert "Linux" in r["output"]
|
||||
|
||||
|
||||
class TestDaytonaFilesystem:
|
||||
def test_write_and_read_file(self, task_id):
|
||||
_run("echo 'test content' > /tmp/daytona_test.txt", task_id)
|
||||
r = _run("cat /tmp/daytona_test.txt", task_id)
|
||||
assert r["exit_code"] == 0
|
||||
assert "test content" in r["output"]
|
||||
|
||||
def test_persistence_within_session(self, task_id):
|
||||
_run("pip install cowsay 2>/dev/null", task_id, timeout=120)
|
||||
r = _run('python3 -c "import cowsay; print(cowsay.__file__)"', task_id)
|
||||
assert r["exit_code"] == 0
|
||||
assert "cowsay" in r["output"]
|
||||
|
||||
|
||||
class TestDaytonaPersistence:
|
||||
def test_filesystem_survives_stop_and_resume(self):
|
||||
"""Write a file, stop the sandbox, resume it, assert the file persists."""
|
||||
task = "daytona_test_persist"
|
||||
try:
|
||||
# Enable persistence for this test
|
||||
os.environ["TERMINAL_CONTAINER_PERSISTENT"] = "true"
|
||||
|
||||
# Write a marker file and stop the sandbox
|
||||
_run("echo 'survive' > /tmp/persist_test.txt", task)
|
||||
cleanup_vm(task) # stops (not deletes) because persistent=true
|
||||
|
||||
# Resume with the same task_id — file should still exist
|
||||
r = _run("cat /tmp/persist_test.txt", task)
|
||||
assert r["exit_code"] == 0
|
||||
assert "survive" in r["output"]
|
||||
finally:
|
||||
# Force-delete so the sandbox doesn't leak
|
||||
os.environ["TERMINAL_CONTAINER_PERSISTENT"] = "false"
|
||||
cleanup_vm(task)
|
||||
|
||||
|
||||
class TestDaytonaIsolation:
|
||||
def test_different_tasks_isolated(self):
|
||||
task_a = "daytona_test_iso_a"
|
||||
task_b = "daytona_test_iso_b"
|
||||
try:
|
||||
_run("echo 'secret' > /tmp/isolated.txt", task_a)
|
||||
r = _run("cat /tmp/isolated.txt 2>&1 || echo NOT_FOUND", task_b)
|
||||
assert "secret" not in r["output"] or "NOT_FOUND" in r["output"]
|
||||
finally:
|
||||
cleanup_vm(task_a)
|
||||
cleanup_vm(task_b)
|
||||
Loading…
Add table
Add a link
Reference in a new issue