b0ee7da5be
Adds tests/ with a tiny bash assert harness, manifest fixtures, and a
runner. No framework dependency — each test file is self-contained
and exits 0 on pass / 1 on fail; tests/run_tests.sh aggregates.
Unit tests (no docker):
- pipelock_naming: container_name, proxy_url, proxy_host_port shape
- pipelock_classify: _pipelock_is_ipv4_literal classifier coverage
- pipelock_allowlist: bottle_allowlist + ssh hostnames/ip_cidrs/
trusted_domains + effective_allowlist union/dedup/sort, plus
rejection of non-string entries
- pipelock_yaml: emitter shape (mode/enforce/api_allowlist/forward_proxy/
dlp), conditional ssrf+trusted_domains blocks, secret hygiene
(manifest env values must not appear in YAML), file mode 600
Integration tests (require docker, skip cleanly otherwise):
- pipelock_image: pinned digest's ENTRYPOINT is /pipelock and CMD
contains 'run' and the binary --version succeeds — would catch a
future image bump that changes the launcher's argv contract
- pipelock_sidecar_smoke: docker create + cp YAML to /etc/pipelock.yaml
+ start, then probe /health — the regression test for the bug
where the YAML was written to /etc/pipelock/ (parent dir absent in
the distroless image)
- dry_run_plan: cli.sh start --dry-run shows the egress line,
counts the bottle's entry into the effective allowlist, prints
the dry-run banner, and creates zero docker resources
- orphan_cleanup: the cleanup primitives the start-flow trap depends
on (network_remove, pipelock_stop) are idempotent against
missing/never-existed resources, so the trap is safe even if
pipelock_start dies before everything is wired up
Assisted-by: Claude Code
75 lines
2.9 KiB
Bash
Executable File
75 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Integration: the cleanup primitives the start-flow trap depends on
|
|
# are idempotent. The original orphan-network bug was a trap-ordering
|
|
# issue (cleanup_all installed AFTER networks were created); the fix
|
|
# moved the install earlier. The trap is only safe if the helpers it
|
|
# calls — network_remove, pipelock_stop — are no-ops against
|
|
# already-missing or never-existed resources. We test that here.
|
|
#
|
|
# (The full end-to-end "cli.sh dies mid-run, networks gone" flow needs
|
|
# a TTY and is documented as a manual verification step in tests/README.md.)
|
|
TEST_NAME="orphan_cleanup"
|
|
|
|
. "$(dirname "$0")/../lib/common.sh"
|
|
# shellcheck source=../../lib/log.sh
|
|
. "${REPO_ROOT}/lib/log.sh"
|
|
# shellcheck source=../../lib/docker.sh
|
|
. "${REPO_ROOT}/lib/docker.sh"
|
|
# shellcheck source=../../lib/network.sh
|
|
. "${REPO_ROOT}/lib/network.sh"
|
|
# shellcheck source=../../lib/pipelock.sh
|
|
. "${REPO_ROOT}/lib/pipelock.sh"
|
|
|
|
skip_test_if_no_docker
|
|
|
|
slug="cb-test-orphan-$$"
|
|
internal_name=""
|
|
egress_name=""
|
|
|
|
cleanup() {
|
|
for n in "$internal_name" "$egress_name"; do
|
|
[ -n "$n" ] && docker network rm "$n" >/dev/null 2>&1 || true
|
|
done
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# 1. network_remove against a name that doesn't exist returns 0
|
|
# (the trap can call it eagerly without crashing on the first run
|
|
# where the network was never created).
|
|
assert_exit_zero "network_remove: missing network is a no-op" \
|
|
network_remove "claude-bottle-net-${slug}-does-not-exist"
|
|
|
|
# 2. Create both networks the way cli.sh does, then remove them with
|
|
# network_remove. Both should succeed and the networks should be
|
|
# gone afterwards.
|
|
internal_name="$(network_create_internal "$slug")"
|
|
egress_name="$(network_create_egress "$slug")"
|
|
|
|
assert_match "$(docker network ls --format '{{.Name}}')" "^${internal_name}$" \
|
|
"internal network was created"
|
|
assert_match "$(docker network ls --format '{{.Name}}')" "^${egress_name}$" \
|
|
"egress network was created"
|
|
|
|
assert_exit_zero "network_remove: removes existing internal network" \
|
|
network_remove "$internal_name"
|
|
assert_exit_zero "network_remove: removes existing egress network" \
|
|
network_remove "$egress_name"
|
|
|
|
nets_after="$(docker network ls --format '{{.Name}}')"
|
|
assert_not_contains "$nets_after" "$internal_name" "internal network gone after removal"
|
|
assert_not_contains "$nets_after" "$egress_name" "egress network gone after removal"
|
|
|
|
# 3. Removing a second time is still safe — the trap may run after a
|
|
# clean exit, where the resources are already gone.
|
|
assert_exit_zero "network_remove: idempotent on already-removed internal" \
|
|
network_remove "$internal_name"
|
|
assert_exit_zero "network_remove: idempotent on already-removed egress" \
|
|
network_remove "$egress_name"
|
|
|
|
# 4. pipelock_stop against a slug whose sidecar was never started must
|
|
# also be a no-op — same reason.
|
|
assert_exit_zero "pipelock_stop: missing sidecar is a no-op" \
|
|
pipelock_stop "missing-${slug}"
|
|
|
|
test_summary
|