9fdaba4bd4
tracker-policy-pr / check-pr (pull_request) Successful in 15s
test / integration-docker (pull_request) Successful in 20s
lint / lint (push) Successful in 1m5s
test / unit (pull_request) Successful in 1m49s
test / integration-firecracker (pull_request) Successful in 2m0s
test / coverage (pull_request) Successful in 16s
test / publish-infra (pull_request) Has been skipped
Integration tests now select their backend from BOT_BOTTLE_BACKEND and
skip on the capability that backend actually needs, instead of gating
every backend on unrelated Docker availability.
Task 1 — backend-agnostic guards (tests/_backend.py):
- Capability probes: docker_capability() (reachable daemon) and
firecracker_capability() (accessible /dev/kvm + firecracker on PATH,
Docker-independent). backend_capability()/selected_backend() resolve
the target from BOT_BOTTLE_BACKEND (default docker).
- skip_unless_selected_backend_available() for backend-agnostic tests
(test_sandbox_escape) — runs through whichever backend is selected and
checks that backend's real capability.
- skip_unless_backend("docker") for Docker-implementation tests
(DockerBroker, DockerGateway, backend.docker.*) — they no-op under a
non-Docker run rather than testing internals that run doesn't target.
- Retires tests/_docker.py; the KVM job no longer needs SKIP_DOCKER_TESTS
to steer Docker-only classes.
Task 2 — explicit per-backend skip visibility:
- tests/backend_preflight.py prints a clear PASS/FAIL capability line and
exits non-zero when the selected backend is missing.
- Both integration jobs run it as a preflight, so absent infrastructure
is surfaced at the job level instead of hidden among unittest.skip
lines. The docker job replaces its soft "Show environment" step; the
firecracker job keeps its richer backend-status check.
Docs (tests/README.md, docs/ci.md) updated; unit coverage for the probes,
guards, and preflight in test_backend_skip_guards.py.
Closes #414
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
57 lines
1.9 KiB
Python
57 lines
1.9 KiB
Python
"""Integration: the Docker launch broker starts and removes a real container.
|
|
|
|
Gated on a reachable Docker daemon (skips cleanly otherwise). Uses a tiny
|
|
image; the container may exit immediately — we only assert it exists after
|
|
launch and is gone after teardown.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import secrets
|
|
import subprocess
|
|
import unittest
|
|
|
|
from bot_bottle.orchestrator.broker import LaunchRequest, sign_request
|
|
from bot_bottle.orchestrator.docker_broker import DockerBroker, container_name
|
|
from tests._backend import skip_unless_backend
|
|
|
|
IMAGE = "busybox"
|
|
|
|
|
|
@skip_unless_backend("docker")
|
|
class TestDockerBrokerIntegration(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.secret = secrets.token_bytes(16)
|
|
self.broker = DockerBroker(self.secret)
|
|
self.bottle_id = "itest" + secrets.token_hex(4)
|
|
self.name = container_name(self.bottle_id)
|
|
self.addCleanup(
|
|
lambda: subprocess.run(
|
|
["docker", "rm", "--force", self.name],
|
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=False,
|
|
)
|
|
)
|
|
|
|
def _exists(self) -> bool:
|
|
proc = subprocess.run(
|
|
["docker", "ps", "-a", "--filter", f"name=^{self.name}$",
|
|
"--format", "{{.Names}}"],
|
|
stdout=subprocess.PIPE, text=True, check=False,
|
|
)
|
|
return self.name in proc.stdout.split()
|
|
|
|
def _submit(self, req: LaunchRequest) -> None:
|
|
self.broker.submit(sign_request(req, self.secret))
|
|
|
|
def test_launch_creates_then_teardown_removes(self) -> None:
|
|
self._submit(
|
|
LaunchRequest(op="launch", bottle_id=self.bottle_id, image_ref=IMAGE)
|
|
)
|
|
self.assertTrue(self._exists(), "container should exist after launch")
|
|
self._submit(LaunchRequest(op="teardown", bottle_id=self.bottle_id))
|
|
self.assertFalse(self._exists(), "container should be gone after teardown")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|