Files
bot-bottle/tests
didericis d75cc9325f
test / run tests/run_tests.py (pull_request) Successful in 16s
feat(bottles): implement bottle factory abstraction per PRD 0003
Introduce claude_bottle/bottles/ with a Bottle Protocol and a
get_bottle_factory() that dispatches on CLAUDE_BOTTLE_PLATFORM
(default "docker"). Move every Docker-specific subprocess.run call
from cli/start.py, plus the orchestration of build, networks, the
pipelock sidecar, container launch, and per-container provisioning
(prompt, skills, ssh, .git), into create_docker_bottle.

Drop bottles[].runtime from the manifest schema. Auto-detect whether
gVisor is registered with the daemon and pass --runtime=runsc when it
is; the preflight shows the resolved runtime so the choice is visible.
Manifests still carrying 'runtime' get a clear error pointing at the
auto-detect behavior, rather than silent ignore.

Out of scope: cli/cleanup.py and cli/list.py still call docker
directly. They enumerate active bottles across the host, which is a
separate concern from "create a bottle" and is left for a follow-up
that introduces a list_active/cleanup primitive on the factory.
2026-05-10 22:15:05 -04:00
..

Tests

Plain-Python test suite using stdlib unittest. No external dependencies. Unit tests run anywhere Python 3 is present; integration tests need Docker and skip cleanly otherwise.

Layout

tests/
  run_tests.py                    # entry point
  fixtures.py                     # JSON manifest builders
  _docker.py                      # docker-availability skip helper
  test_pipelock_naming.py         # unit
  test_pipelock_classify.py       # unit
  test_pipelock_allowlist.py      # unit
  test_pipelock_yaml.py           # unit
  test_pipelock_image.py          # integration
  test_pipelock_sidecar_smoke.py  # integration
  test_dry_run_plan.py            # integration
  test_orphan_cleanup.py          # integration

Running

tests/run_tests.py                                  # everything
tests/run_tests.py unit                             # unit only
tests/run_tests.py integration                      # integration only
tests/run_tests.py tests/test_pipelock_yaml.py      # one file

You can also run via python -m unittest:

python -m unittest discover -s tests
python -m unittest tests.test_pipelock_yaml

What the integration tests cover

  • test_pipelock_image.py — the pinned digest is reachable, ENTRYPOINT is /pipelock, and CMD includes run.
  • test_pipelock_sidecar_smoke.pydocker create + docker cp the generated YAML to /etc/pipelock.yaml + docker start, then probe /health.
  • test_dry_run_plan.pycli.py start --dry-run shows the resolved egress allowlist and creates zero docker resources.
  • test_orphan_cleanup.py — network_remove and pipelock_stop are idempotent against missing resources, so the EXIT trap can call them unconditionally.

What's NOT covered

  • claude_bottle/ssh.py end-to-end (would need a fake SSH host inside the container).
  • A live SSH-through-pipelock tunnel against a real Tailscale-style IP.
  • DLP false-positive measurements.
  • TLS handling / cert pinning behavior.

Adding a test

  1. Pick a filename: test_<topic>.py. Add it to INTEGRATION_NAMES in run_tests.py if it needs Docker.
  2. Boilerplate:
    import unittest
    
    from claude_bottle.<module> import <symbol>
    
    class TestThing(unittest.TestCase):
        def test_x(self):
            ...
    
    if __name__ == "__main__":
        unittest.main()
    
  3. For Docker-dependent tests, decorate the class with @skip_unless_docker() from tests._docker.