Files
ops-warden/workplans/WARDEN-WP-0003-test-coverage-and-quality.md
tegwick c66cb1b0fe chore(workplans): add WARDEN-WP-0002 and WARDEN-WP-0003
WP-0002 — Correctness and Operational Completeness (priority: high)
  T1: TTL max enforcement per ActorType
  T2: Stale cert cleanup command (warden cleanup)
  T3: Outgoing signatures log (warden log)

WP-0003 — Test Coverage and Code Quality (priority: medium)
  T1: VaultCA tests
  T2: LocalCA.generate_keypair tests
  T3: CLI tests (test_cli.py)
  T4: Real ssh-keygen integration test
  T5: File permissions enforcement (mode 600)
  T6: warden status --state-dir override

Both registered in Custodian State Hub under ops-warden repo (74df727e).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:28:31 +02:00

7.7 KiB

id, type, title, domain, repo, status, owner, topic_slug, created, updated, state_hub_workstream_id
id type title domain repo status owner topic_slug created updated state_hub_workstream_id
WARDEN-WP-0003 workplan OpsWarden Test Coverage and Code Quality custodian ops-warden active Bernd custodian 2026-05-15 2026-05-15 cb2bbf3c-848a-4af6-ba64-8361e64cd4d7

WARDEN-WP-0003 — Test Coverage and Code Quality

Scope: Close the test coverage gaps left after WARDEN-WP-0001: VaultCA has zero tests, generate_keypair is untested, no CLI tests exist, and no real ssh-keygen integration test was written. Also fix file permission enforcement (security) and add --state-dir override to warden status (usability).

Out of scope: Functional behaviour changes (WARDEN-WP-0002), Vault cluster setup, host-side tooling.


Goal

After this workplan:

  1. VaultCA has a full unit test suite covering success, auth failure, network failure, and role-map misconfiguration.
  2. generate_keypair has direct unit tests alongside the existing sign tests.
  3. A tests/test_cli.py covers every command's exit codes and output shape.
  4. A tests/test_integration.py marked @pytest.mark.integration exercises LocalCA.sign() against a real ssh-keygen without any mocking.
  5. Cert and key files written by warden are always mode 600; a scorecard check flags world-readable files.
  6. warden status --state-dir <path> works without a warden.yaml.

Reference Documents

Document Location
WARDEN-WP-0001 workplans/WARDEN-WP-0001-initial-implementation.md
WARDEN-WP-0002 workplans/WARDEN-WP-0002-correctness-and-completeness.md
CertCommandInterface wiki/CertCommandInterface.md

Design Decisions

Integration tests: separate marker, not separate directory

Use @pytest.mark.integration and skip when ssh-keygen is not in PATH. Tests live in tests/test_integration.py. The unit suite (uv run pytest) excludes integration tests via pytest.ini_options addopts = "-m 'not integration'"; uv run pytest -m integration runs them explicitly. This keeps CI fast while making the real-ssh-keygen path easy to invoke manually.

File permissions: at write time, not at read time

os.chmod(path, 0o600) is called immediately after each path.write_text() or shutil.copy2() that writes a key or cert. No deferred or scheduled chmod. The scorecard check catches files that were written by older versions of warden or by external tools.

warden status --state-dir: config bypass, not config optional

When --state-dir is provided, skip load_config() entirely — don't try to load a partial config. This makes the flag useful on remote machines that have received a cert via ops-bridge but have no warden installation.


Tasks

T1 — VaultCA tests

id: WARDEN-WP-0003-T1
state_hub_task_id: eff074ce-c027-4df5-8006-0990296592ac
status: todo
priority: high
  • Create tests/test_vault.py
  • Test VaultCA.sign() success: mock httpx.post returning a valid signed_key; assert CertRecord fields; assert cert file written to state_dir
  • Test HTTP 403: httpx.HTTPStatusErrorCAError with status code in message
  • Test unreachable Vault: httpx.RequestErrorCAError with fallback hint
  • Test missing VAULT_TOKEN: _token() raises CAError before HTTP call
  • Test missing role in role_map: CAError before HTTP call
  • Test missing pubkey file: CAError before HTTP call

T2 — LocalCA.generate_keypair tests

id: WARDEN-WP-0003-T2
state_hub_task_id: ddfe5331-0a3b-4783-bdf4-f5ebcdf7965c
status: todo
priority: medium
  • Add TestGenerateKeypair class to tests/test_ca.py
  • Test success: mock subprocess.run; assert privkey and pubkey paths returned
  • Test ssh-keygen called with -t ed25519, -N "", -C actor_name
  • Test existing files are unlinked before generation (write dummy files first)
  • Test CAError raised on non-zero ssh-keygen exit code
  • Test output files land in state_dir/keys/

T3 — CLI tests

id: WARDEN-WP-0003-T3
state_hub_task_id: 040ce3a1-0efb-4816-a2d9-357162dd1612
status: todo
priority: high
  • Create tests/test_cli.py using typer.testing.CliRunner
  • warden sign: exits 0 and stdout is cert text (mock CA); exits 1 on unknown actor; exits 1 on config error
  • warden issue: exits 1 on vault backend; exits 0 on local backend (mock CA)
  • warden status: exits 0 and prints "no cert" message when state_dir empty; exits 1 when cert is expired (mock parse_cert_metadata)
  • warden scorecard: exits 0 on clean inventory + empty state_dir; exits 1 when a check fails
  • warden inventory add / list / remove: round-trip via tmp inventory file
  • warden log: exits 0 with empty output when no log; --json is valid JSON (after WARDEN-WP-0002 T3 adds the log command)
  • warden cleanup --dry-run: exits 0, no files deleted (after WARDEN-WP-0002 T2 adds cleanup)

T4 — Real ssh-keygen integration test

id: WARDEN-WP-0003-T4
state_hub_task_id: 434fb008-103f-410c-85fd-e77b33e61fe4
status: todo
priority: medium
  • Create tests/test_integration.py
  • Mark all tests @pytest.mark.integration
  • Add pytest.ini_options to pyproject.toml: addopts = "-m 'not integration'" so unit suite skips them by default
  • Test LocalCA.sign() end-to-end: generate a real CA keypair and actor keypair via subprocess ssh-keygen in tmp_path; call LocalCA.sign(); assert CertRecord.valid_before > datetime.now(utc); assert cert file exists; assert parse_cert_metadata() succeeds on it without mocking
  • Skip test if shutil.which("ssh-keygen") is None
  • Document in README: uv run pytest -m integration to run real-CA tests

T5 — File permissions enforcement (mode 600)

id: WARDEN-WP-0003-T5
state_hub_task_id: ac146fe6-d1fd-4186-91bd-6f098de72449
status: todo
priority: medium
  • ca.py LocalCA.sign(): call os.chmod(dest, 0o600) after shutil.copy2
  • ca.py LocalCA.generate_keypair(): call os.chmod(privkey, 0o600) and os.chmod(pubkey, 0o644) after generation
  • vault.py VaultCA.sign(): call os.chmod(dest, 0o600) after dest.write_text
  • scorecard.py: add check_file_permissions(state_dir) — flag any *-cert.pub or keys/* file where stat().st_mode & 0o044 != 0
  • Add check_file_permissions to run_scorecard()
  • Update test_ca.py: assert os.chmod called with correct mode after sign and generate_keypair (patch os.chmod or check stat on actual files in tmp_path)

T6 — warden status --state-dir override

id: WARDEN-WP-0003-T6
state_hub_task_id: 1c9f1987-7b11-43c1-a5e3-c2fd8d1c1589
status: todo
priority: low
  • cli.py status(): add state_dir_override: Annotated[Optional[Path], typer.Option("--state-dir")] = None
  • When --state-dir is provided: use it directly, skip _load_cfg() entirely
  • When absent: load config as today
  • Add test in test_cli.py: invoke warden status --state-dir <tmp_path> without a config file; assert exit 0

Acceptance Criteria

  • uv run pytest runs unit suite only; all pass; VaultCA and generate_keypair covered
  • uv run pytest -m integration succeeds (requires ssh-keygen in PATH)
  • test_cli.py covers all commands; no mocked subprocess in CLI tests where avoidable (use tmp inventory files and mocked CA)
  • ls -la ~/.local/state/warden/*.pub shows mode 600 for newly signed certs
  • Scorecard file_permissions check passes on a clean state dir; fails on a world-readable cert
  • warden status --state-dir /tmp/some-dir runs without a warden.yaml
  • All lints pass: uv run ruff check .