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>
78 lines
2.7 KiB
Python
78 lines
2.7 KiB
Python
"""Integration: the network-cleanup primitives the start-flow trap
|
|
depends on are idempotent. The original orphan-network bug was a
|
|
trap-ordering issue; the fix moved the install earlier. The trap
|
|
is only safe if network_remove is a no-op against missing
|
|
resources.
|
|
|
|
The PipelockProxy.stop idempotency case that used to live here was
|
|
removed in PRD 0024 chunk 3 when the per-container .stop method
|
|
went away — gateway teardown is now compose's responsibility, and
|
|
`compose down` already no-ops on missing containers."""
|
|
|
|
import os
|
|
import subprocess
|
|
import unittest
|
|
|
|
from bot_bottle.backend.docker.network import (
|
|
network_create_egress,
|
|
network_create_internal,
|
|
network_remove,
|
|
)
|
|
from tests._backend import skip_unless_backend
|
|
|
|
|
|
@skip_unless_backend("docker")
|
|
class TestOrphanCleanup(unittest.TestCase):
|
|
def setUp(self):
|
|
self.slug = f"cb-test-orphan-{os.getpid()}"
|
|
self.internal_name = ""
|
|
self.egress_name = ""
|
|
|
|
def tearDown(self):
|
|
for n in (self.internal_name, self.egress_name):
|
|
if n:
|
|
subprocess.run(
|
|
["docker", "network", "rm", n],
|
|
stdout=subprocess.DEVNULL,
|
|
stderr=subprocess.DEVNULL,
|
|
check=False,
|
|
)
|
|
|
|
def test_remove_missing_is_noop(self):
|
|
# Returning True == idempotent success.
|
|
self.assertTrue(network_remove(f"bot-bottle-net-{self.slug}-does-not-exist"))
|
|
|
|
@unittest.skipIf(
|
|
os.environ.get("GITEA_ACTIONS") == "true",
|
|
"skipped under act_runner: docker socket mount topology breaks "
|
|
"in-process visibility of networks created on the host daemon",
|
|
)
|
|
def test_create_and_remove(self):
|
|
self.internal_name = network_create_internal(self.slug)
|
|
self.egress_name = network_create_egress(self.slug)
|
|
|
|
nets = subprocess.run(
|
|
["docker", "network", "ls", "--format", "{{.Name}}"],
|
|
capture_output=True, text=True, check=True,
|
|
).stdout.splitlines()
|
|
self.assertIn(self.internal_name, nets)
|
|
self.assertIn(self.egress_name, nets)
|
|
|
|
self.assertTrue(network_remove(self.internal_name))
|
|
self.assertTrue(network_remove(self.egress_name))
|
|
|
|
nets_after = subprocess.run(
|
|
["docker", "network", "ls", "--format", "{{.Name}}"],
|
|
capture_output=True, text=True, check=True,
|
|
).stdout.splitlines()
|
|
self.assertNotIn(self.internal_name, nets_after)
|
|
self.assertNotIn(self.egress_name, nets_after)
|
|
|
|
# Idempotent on already-removed.
|
|
self.assertTrue(network_remove(self.internal_name))
|
|
self.assertTrue(network_remove(self.egress_name))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|