secrets-engine/tests/test_integration_bao.py
tegwick a852d3f1ff feat(mvp): working secrets-engine CLI for the whynot-design npm publish lane
Implements SECRETS-WP-0002 end to end as a uv-managed Python package:

- catalog: non-secret lane registry + strict validator (build/test/prod)
- stage roles + OpenBao ACL policies; guards refuse wildcards, sys/, identity/,
  admin names, and cross-stage paths before any backend call
- plan/apply: dry-run-first, idempotent policy + approle apply, decision-gated
- decisions: State Hub lookup with local-fixture fallback; non-secret evidence
  to JSONL + hub progress, scrubbed of any value
- provision/verify: mode-0600 file import + generated test values; positive/
  negative checks that never print the value
- exec delivery: `exec --catalog ... -- npm publish` injects the token via a
  temp .npmrc for the child only, cleaned up on exit/failure/interrupt
- ops-warden routing contract + hardening backlog docs
- 34 tests incl. live OpenBao integration; scripts/demo-e2e.sh runs the full
  chain against a throwaway bao dev server

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:28:45 +02:00

103 lines
3.3 KiB
Python

"""Live integration test against a throwaway OpenBao dev server.
Skipped automatically if the `bao` CLI is not on PATH. Boots an in-memory dev
server on a private port, then drives apply -> provision -> verify(+/-) ->
exec-delivery and asserts the value is reachable by the child but not the parent.
"""
import json
import os
import shutil
import socket
import subprocess
import time
from pathlib import Path
import pytest
from secrets_engine.apply import apply_plan
from secrets_engine.catalog import get_entry
from secrets_engine.config import repo_root
from secrets_engine.exec_delivery import exec_with_secret
from secrets_engine.openbao import OpenBaoClient
from secrets_engine.plan import build_plan
from secrets_engine.provision import provision_from_file
from secrets_engine.verify import verify_negative, verify_positive
pytestmark = pytest.mark.skipif(
shutil.which("bao") is None and shutil.which("vault") is None,
reason="no OpenBao/Vault CLI on PATH",
)
def _free_port() -> int:
s = socket.socket()
s.bind(("127.0.0.1", 0))
port = s.getsockname()[1]
s.close()
return port
@pytest.fixture()
def bao_dev():
bao = shutil.which("bao") or shutil.which("vault")
port = _free_port()
addr = f"http://127.0.0.1:{port}"
token = "se-test-root"
proc = subprocess.Popen(
[bao, "server", "-dev", f"-dev-root-token-id={token}",
f"-dev-listen-address=127.0.0.1:{port}"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
)
client = OpenBaoClient(addr=addr, token=token, bao_bin=bao)
for _ in range(50):
if client.is_reachable():
break
time.sleep(0.2)
else:
proc.kill()
pytest.fail("dev OpenBao did not become reachable")
try:
yield client
finally:
proc.kill()
def test_full_chain(bao_dev, tmp_path):
client = bao_dev
entry = get_entry(repo_root() / "catalog", "whynot-design-npm-publish")
plan = build_plan(entry, "prod", decision_id="test")
apply_plan(client, entry, plan)
# provision from a mode-0600 file outside the repo (tmp_path is outside)
tokenfile = tmp_path / "tok"
tokenfile.write_text("npm_integrationTESTvalue1234567890")
os.chmod(tokenfile, 0o600)
provision_from_file(client, entry, "npm_token", tokenfile)
pos = verify_positive(client, entry, "npm_token")
assert pos.passed, pos.detail
neg = verify_negative(client, entry)
assert neg.passed, neg.detail
# exec delivery: child can resolve token via npmrc; assert via a probe script
probe = tmp_path / "probe.sh"
probe.write_text(
"#!/usr/bin/env bash\n"
'grep -q _authToken "$NPM_CONFIG_USERCONFIG" && echo CHILD_HAS_TOKEN\n'
)
os.chmod(probe, 0o755)
rc = exec_with_secret(client, entry, "npm_token", [str(probe)], mode="npm-config")
assert rc == 0
# the parent process never received the value as an env var
assert "SE_NPM_TOKEN" not in os.environ
def test_idempotent_apply(bao_dev):
client = bao_dev
entry = get_entry(repo_root() / "catalog", "whynot-design-npm-publish")
plan = build_plan(entry, "prod", decision_id="test")
first = apply_plan(client, entry, plan)
second = apply_plan(client, entry, plan)
# policy should be reported unchanged on the second apply
assert any("unchanged" in s for s in second.skipped)