fix(tests): run sandbox integration on firecracker
test / integration-firecracker (pull_request) Failing after 5s
test / integration-docker (pull_request) Successful in 9s
tracker-policy-pr / check-pr (pull_request) Successful in 7s
test / unit (pull_request) Successful in 33s
test / coverage (pull_request) Failing after 27s
lint / lint (push) Failing after 42s

This commit is contained in:
2026-07-19 02:34:53 +00:00
parent d117460192
commit 626f07efa6
3 changed files with 53 additions and 7 deletions
+13
View File
@@ -30,3 +30,16 @@ def docker_available() -> bool:
def skip_unless_docker(reason: str = "docker unreachable"): def skip_unless_docker(reason: str = "docker unreachable"):
return unittest.skipUnless(docker_available(), reason) return unittest.skipUnless(docker_available(), reason)
def skip_unless_docker_or_firecracker(
reason: str = "neither Docker nor Firecracker selected",
):
"""Skip a backend-agnostic test unless one supported backend can run.
Firecracker does not require the host Docker daemon. The KVM coverage job
deliberately sets ``SKIP_DOCKER_TESTS`` to exclude Docker-only integration
classes while still exercising this path.
"""
firecracker_selected = os.environ.get("BOT_BOTTLE_BACKEND") == "firecracker"
return unittest.skipUnless(firecracker_selected or docker_available(), reason)
+5 -7
View File
@@ -31,7 +31,7 @@ from pathlib import Path
from bot_bottle.backend import BottleSpec, get_bottle_backend from bot_bottle.backend import BottleSpec, get_bottle_backend
from bot_bottle.bottle_state import cleanup_state from bot_bottle.bottle_state import cleanup_state
from bot_bottle.manifest import ManifestIndex from bot_bottle.manifest import ManifestIndex
from tests._docker import skip_unless_docker from tests._docker import skip_unless_docker_or_firecracker
# Secrets planted in the bottle env as literals (agents substitute via # Secrets planted in the bottle env as literals (agents substitute via
@@ -67,7 +67,7 @@ _DUMMY_HOST_KEY = (
) )
@skip_unless_docker() @skip_unless_docker_or_firecracker()
@unittest.skipIf( @unittest.skipIf(
os.environ.get("GITEA_ACTIONS") == "true" os.environ.get("GITEA_ACTIONS") == "true"
and os.environ.get("BOT_BOTTLE_BACKEND") != "firecracker", and os.environ.get("BOT_BOTTLE_BACKEND") != "firecracker",
@@ -91,11 +91,9 @@ class TestSandboxEscape(unittest.TestCase):
@classmethod @classmethod
def setUpClass(cls) -> None: def setUpClass(cls) -> None:
# Docker is always required (the agent + companion containers run under it, # Pin Docker when BOT_BOTTLE_BACKEND is unset to preserve the
# and VM backends still use it for the gateway); the # Docker-backed CI path. Firecracker uses its persistent infra VM for
# class-level @skip_unless_docker already covers that. Pin # the shared gateway and therefore does not require host Docker.
# Docker when BOT_BOTTLE_BACKEND is unset to preserve the
# Docker-backed CI path.
cls._backend_name = os.environ.get("BOT_BOTTLE_BACKEND", "docker") cls._backend_name = os.environ.get("BOT_BOTTLE_BACKEND", "docker")
# Throwaway static key for the git-gate fixture. It need not # Throwaway static key for the git-gate fixture. It need not
+35
View File
@@ -0,0 +1,35 @@
"""Tests for integration-test backend selection helpers."""
from __future__ import annotations
import os
import unittest
from unittest.mock import patch
from tests._docker import skip_unless_docker_or_firecracker
class TestSkipUnlessDockerOrFirecracker(unittest.TestCase):
def test_firecracker_runs_when_docker_tests_are_disabled(self):
with patch.dict(
os.environ,
{"BOT_BOTTLE_BACKEND": "firecracker", "SKIP_DOCKER_TESTS": "1"},
clear=True,
):
decorated = skip_unless_docker_or_firecracker()(type("Case", (), {}))
self.assertFalse(getattr(decorated, "__unittest_skip__", False))
def test_non_firecracker_still_skips_when_docker_tests_are_disabled(self):
with patch.dict(
os.environ,
{"BOT_BOTTLE_BACKEND": "docker", "SKIP_DOCKER_TESTS": "1"},
clear=True,
):
decorated = skip_unless_docker_or_firecracker()(type("Case", (), {}))
self.assertTrue(decorated.__unittest_skip__)
if __name__ == "__main__":
unittest.main()