Files
ops-bridge/tests/conftest.py
tegwick bd169a07e2 feat(directive): implement BRIDGE-WP-0004 AccessManagementDirective alignment
- ActorType enum (adm/agt/atm) replaces actor_class string; config validates
  naming convention (adm-*/agt-*/atm-*) with hard ConfigError on mismatch;
  legacy 'human'/'automation' values accepted with DeprecationWarning
- cert_command: pluggable shell string run before each SSH launch; cert written
  to state dir; -i cert appended to SSH command alongside -i key
- TTL-aware cert refresh: parses Valid-to via ssh-keygen -L; pre-emptive restart
  5 min before expiry (no backoff, no attempt increment); CERT_EXPIRING logged
- CertAcquisitionError: cert failures trigger normal backoff/retry loop
- cert_identity: Key ID parsed from cert and recorded in BRIDGE_CONNECTED event
- bridge cert-status: new CLI command; exit 1 on expired cert; --json flag
- 233 tests passing, ruff clean

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

155 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Shared pytest configuration for OpsBridge tests.
Registers capability and access_mode marks, and provides the
collect_capability_coverage() helper used by the cross-mode meta-test.
"""
from __future__ import annotations
import textwrap
from typing import Iterable
import pytest
# ---------------------------------------------------------------------------
# Shared fixtures
# ---------------------------------------------------------------------------
VALID_CONFIG = textwrap.dedent("""\
tunnels:
test-tunnel:
host: host.local
remote_port: 18000
local_port: 8000
ssh_user: ubuntu
ssh_key: ~/.ssh/id_ops
actor: adm-bernd
actors:
adm-bernd:
class: adm
description: Bernd
""")
VALID_CONFIG_WITH_CATALOG = textwrap.dedent("""\
tunnels:
test-tunnel:
host: host.local
remote_port: 18000
local_port: 8000
ssh_user: ubuntu
ssh_key: ~/.ssh/id_ops
actor: adm-bernd
actors:
adm-bernd:
class: adm
description: Bernd
catalog_path: {catalog_path}
""")
@pytest.fixture
def config_file(tmp_path):
f = tmp_path / "tunnels.yaml"
f.write_text(VALID_CONFIG)
return f
@pytest.fixture
def state_dir(tmp_path):
d = tmp_path / "state"
d.mkdir()
return d
@pytest.fixture
def catalog_dir(tmp_path):
"""Minimal catalog directory with one domain, target, and bridge."""
cat = tmp_path / "catalog"
domain_dir = cat / "domains" / "coulombcore"
domain_dir.mkdir(parents=True)
(domain_dir / "domain.yaml").write_text(textwrap.dedent("""\
type: domain
id: coulombcore
name: CoulombCore Infrastructure
description: Core infrastructure domain
environment: production
"""))
targets_dir = domain_dir / "targets"
targets_dir.mkdir()
(targets_dir / "state-hub.yaml").write_text(textwrap.dedent("""\
type: target
id: state-hub
domain: coulombcore
kind: service
description: Infrastructure state coordination service
reachable_via:
- state-hub-coulombcore
"""))
bridges_dir = domain_dir / "bridges"
bridges_dir.mkdir()
(bridges_dir / "state-hub-coulombcore.yaml").write_text(textwrap.dedent("""\
type: bridge
id: state-hub-coulombcore
domain: coulombcore
target: state-hub
description: Bridge to state hub
access_method: ssh-reverse
host: coulombcore.local
remote_port: 18000
local_port: 8000
ssh_user: ubuntu
ssh_key: ~/.ssh/id_ops
actor: agent.claude-coulombcore
reconnect:
max_attempts: 0
backoff_initial: 5
backoff_max: 60
"""))
actors_dir = cat / "actors"
actors_dir.mkdir()
(actors_dir / "agent.yaml").write_text(textwrap.dedent("""\
type: actor
id: agent.claude-coulombcore
class: automation
description: Claude Code agent on CoulombCore
"""))
return cat
@pytest.fixture
def config_file_with_catalog(tmp_path, catalog_dir):
f = tmp_path / "tunnels.yaml"
f.write_text(VALID_CONFIG_WITH_CATALOG.format(catalog_path=str(catalog_dir)))
return f
# ---------------------------------------------------------------------------
# Coverage collector helper
# ---------------------------------------------------------------------------
def collect_capability_coverage(items: Iterable) -> set[tuple[str, str]]:
"""Walk pytest items and return set of (capability_name, access_mode) pairs.
Each test item is inspected for `capability` and `access_mode` markers.
A pair is added for every combination of capability × access_mode marks
found on a single item.
Args:
items: Iterable of pytest.Item objects (from session.items or similar).
Returns:
Set of (capability_name, access_mode) tuples found across all items.
"""
covered: set[tuple[str, str]] = set()
for item in items:
capabilities = [
m.args[0] for m in item.iter_markers("capability") if m.args
]
modes = [
m.args[0] for m in item.iter_markers("access_mode") if m.args
]
for cap in capabilities:
for mode in modes:
covered.add((cap, mode))
return covered