Files
sand-boxer/tests/test_manager.py
tegwick 1415e17230 Implement SAND-WP-0006: SaaS payments, routing, and ext.saas-stub
Add credits store, metering on create/destroy, extension routing resolver,
metered SaaS stub extension, burst/saas profiles, credits CLI, docs, and tests.
2026-06-24 07:52:20 +02:00

85 lines
2.6 KiB
Python

"""SandboxManager unit tests with mocked backend."""
from __future__ import annotations
from datetime import UTC, datetime
from pathlib import Path
from unittest.mock import patch
import pytest
from sandboxer.core.manager import SandboxManager
from sandboxer.lifecycle.store import SandboxStore
from sandboxer.models import ActorType, Consumer, SandboxCreateRequest, SandboxState, SandboxStatus
class FakeBackend:
def provision(self, profile, inputs, host):
return {
"sandbox_id": "test1234",
"host": host,
"remote_dir": "/tmp/sandboxer/test1234",
"compose_project": "sbx-e2e-test1234",
"compose_file": "docker-compose.yml",
"ssh_user": "root",
}
def wait_ready(self, handle):
return {
"ssh": f"root@{handle['host']}",
"remote_dir": handle["remote_dir"],
"compose_project": handle["compose_project"],
"host": handle["host"],
}
def teardown(self, handle):
return {
"compose_removed": "True",
"remote_dir_removed": "True",
"remote_dir": handle["remote_dir"],
}
@pytest.fixture
def store(tmp_path: Path) -> SandboxStore:
return SandboxStore(path=tmp_path / "sandboxes.json")
def test_create_and_destroy(store: SandboxStore) -> None:
manager = SandboxManager(store=store)
request = SandboxCreateRequest(
profile="profile.compose-e2e",
inputs={"repo": "/tmp/repo"},
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
)
fake = FakeBackend()
with (
patch("sandboxer.core.manager.resolve_backend", return_value=fake),
patch("sandboxer.core.manager.emit_lifecycle_event", return_value=None),
patch("sandboxer.core.manager.resolve_host", return_value="coulombcore"),
):
status = manager.create(request)
assert status.state == SandboxState.READY
assert status.sandbox_id == "test1234"
destroyed = manager.destroy(status.sandbox_id)
assert destroyed.state == SandboxState.DESTROYED
def test_destroy_idempotent(store: SandboxStore) -> None:
now = datetime.now(UTC)
status = SandboxStatus(
sandbox_id="gone1234",
profile_id="profile.compose-e2e",
extension_id="ext.compose-ssh",
state=SandboxState.DESTROYED,
consumer=Consumer(actor=ActorType.ADM, project="sand-boxer"),
created_at=now,
updated_at=now,
destroyed_at=now,
)
store.save(status)
manager = SandboxManager(store=store)
result = manager.destroy("gone1234")
assert result.state == SandboxState.DESTROYED