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
100 lines
1.9 KiB
Bash
100 lines
1.9 KiB
Bash
#!/usr/bin/env bash
|
|
# Manifest fixture builders. Each function prints a JSON manifest on
|
|
# stdout; callers can pipe to a temp file or pass through `write_fixture`.
|
|
|
|
if [ -n "${CLAUDE_BOTTLE_TESTS_FIXTURES_SOURCED:-}" ]; then
|
|
return 0
|
|
fi
|
|
CLAUDE_BOTTLE_TESTS_FIXTURES_SOURCED=1
|
|
|
|
# fixture_minimal — one bottle, one agent, no env / ssh / skills.
|
|
fixture_minimal() {
|
|
cat <<'JSON'
|
|
{
|
|
"bottles": {
|
|
"dev": {}
|
|
},
|
|
"agents": {
|
|
"demo": {
|
|
"skills": [],
|
|
"prompt": "",
|
|
"bottle": "dev"
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
}
|
|
|
|
# fixture_with_egress — bottle declares an egress.allowlist.
|
|
fixture_with_egress() {
|
|
cat <<'JSON'
|
|
{
|
|
"bottles": {
|
|
"dev": {
|
|
"egress": {
|
|
"allowlist": [
|
|
"github.com",
|
|
"gitlab.com",
|
|
"registry.npmjs.org"
|
|
]
|
|
}
|
|
}
|
|
},
|
|
"agents": {
|
|
"demo": {
|
|
"skills": [],
|
|
"prompt": "",
|
|
"bottle": "dev"
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
}
|
|
|
|
# fixture_with_ssh — bottle has both an IPv4-literal SSH host (Tailscale
|
|
# CGNAT range) and a hostname SSH host, exercising both
|
|
# ssrf.ip_allowlist and trusted_domains code paths.
|
|
fixture_with_ssh() {
|
|
cat <<'JSON'
|
|
{
|
|
"bottles": {
|
|
"dev": {
|
|
"ssh": [
|
|
{
|
|
"Host": "tailscale-gitea",
|
|
"IdentityFile": "/dev/null",
|
|
"Hostname": "100.78.141.42",
|
|
"User": "git",
|
|
"Port": 30009
|
|
},
|
|
{
|
|
"Host": "github",
|
|
"IdentityFile": "/dev/null",
|
|
"Hostname": "github.com",
|
|
"User": "git",
|
|
"Port": 22
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"agents": {
|
|
"demo": {
|
|
"skills": [],
|
|
"prompt": "",
|
|
"bottle": "dev"
|
|
}
|
|
}
|
|
}
|
|
JSON
|
|
}
|
|
|
|
# write_fixture <fixture_func> — write fixture to a temp file, print
|
|
# the path. Caller must rm.
|
|
write_fixture() {
|
|
local fn="${1:?write_fixture: missing fixture function}"
|
|
local f
|
|
f="$(mktemp)"
|
|
"$fn" > "$f"
|
|
printf '%s' "$f"
|
|
}
|