generated from coulomb/repo-seed
feat: reachability and consumer profiles (SAND-WP-0011)
Add reachability enrichment (tunnel metadata, ops-bridge pointer), secret_refs boundary resolution, profile.agent-dev and profile.build, CLI reachability show, API endpoint, consumer smoke scripts, and tests.
This commit is contained in:
20
tests/test_consumer_profiles.py
Normal file
20
tests/test_consumer_profiles.py
Normal file
@@ -0,0 +1,20 @@
|
||||
"""Consumer profile loader smoke tests."""
|
||||
|
||||
from sandboxer.profiles.loader import load_profile
|
||||
|
||||
|
||||
def test_profile_agent_dev_loads() -> None:
|
||||
profile = load_profile("profile.agent-dev")
|
||||
assert profile.id == "profile.agent-dev"
|
||||
assert profile.extension == "ext.compose-ssh"
|
||||
assert profile.scope_default == "agent"
|
||||
assert profile.route is not None
|
||||
assert profile.ttl.default == "8h"
|
||||
|
||||
|
||||
def test_profile_build_loads() -> None:
|
||||
profile = load_profile("profile.build")
|
||||
assert profile.id == "profile.build"
|
||||
assert profile.extension == "ext.vm-packer"
|
||||
assert "build-registry-token" in profile.setup.secret_refs
|
||||
assert profile.reachability.tunnel == "ops-bridge"
|
||||
78
tests/test_reachability.py
Normal file
78
tests/test_reachability.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Reachability enrichment tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from sandboxer.models import (
|
||||
ActorType,
|
||||
Consumer,
|
||||
Profile,
|
||||
Reachability,
|
||||
ReachabilitySpec,
|
||||
SandboxState,
|
||||
SandboxStatus,
|
||||
)
|
||||
from sandboxer.reachability.enrich import (
|
||||
build_reachability_report,
|
||||
enrich_reachability,
|
||||
ssh_one_liner,
|
||||
)
|
||||
|
||||
|
||||
def _profile() -> Profile:
|
||||
return Profile.model_validate(
|
||||
{
|
||||
"id": "profile.agent-dev",
|
||||
"version": "1.0.0",
|
||||
"extension": "ext.compose-ssh",
|
||||
"reachability": ReachabilitySpec(
|
||||
tunnel="ops-bridge", identity="ops-warden"
|
||||
).model_dump(),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def test_enrich_adds_tunnel_and_identity(monkeypatch) -> None:
|
||||
monkeypatch.setenv("SANDBOXER_TUNNEL_PORT", "12222")
|
||||
reach = enrich_reachability(
|
||||
{"ssh": "build@localhost", "remote_dir": "/build/sbx-1", "host": "localhost"},
|
||||
_profile(),
|
||||
{"ssh_port": "12222"},
|
||||
)
|
||||
assert reach["identity"] == "ops-warden"
|
||||
assert reach["tunnel"] == "localhost:12222"
|
||||
assert reach["tunnel_via"] == "ops-bridge"
|
||||
|
||||
|
||||
def test_ssh_one_liner() -> None:
|
||||
reach = Reachability(ssh="user@host", remote_dir="/tmp/ws")
|
||||
line = ssh_one_liner(reach)
|
||||
assert line is not None
|
||||
assert "user@host" in line
|
||||
assert "/tmp/ws" in line
|
||||
|
||||
|
||||
def test_build_reachability_report() -> None:
|
||||
now = datetime.now(UTC)
|
||||
status = SandboxStatus(
|
||||
sandbox_id="abc12345",
|
||||
profile_id="profile.agent-dev",
|
||||
extension_id="ext.compose-ssh",
|
||||
state=SandboxState.READY,
|
||||
consumer=Consumer(actor=ActorType.AGT, project="glas-harness"),
|
||||
host="coulombcore",
|
||||
reachability=Reachability(
|
||||
ssh="root@coulombcore",
|
||||
remote_dir="/tmp/sandboxer/abc12345",
|
||||
tunnel="localhost:22",
|
||||
tunnel_via="ops-bridge",
|
||||
identity="ops-warden",
|
||||
),
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
report = build_reachability_report(status)
|
||||
assert report["sandbox_id"] == "abc12345"
|
||||
assert report["ssh_one_liner"] is not None
|
||||
assert "ops_bridge" in report
|
||||
51
tests/test_secrets.py
Normal file
51
tests/test_secrets.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Setup secret resolution tests."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from sandboxer.models import Profile, SetupSpec
|
||||
from sandboxer.secrets.resolver import resolve_secret_ref, resolve_setup_secrets
|
||||
|
||||
|
||||
def test_resolve_secret_ref_from_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("SANDBOXER_SECRET_BUILD_REGISTRY_TOKEN", "tok123")
|
||||
assert resolve_secret_ref("build-registry-token") == "tok123"
|
||||
|
||||
|
||||
def test_resolve_setup_secrets_success(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.setenv("SANDBOXER_SECRET_BUILD_REGISTRY_TOKEN", "tok123")
|
||||
profile = Profile.model_validate(
|
||||
{
|
||||
"id": "profile.build",
|
||||
"version": "1.0.0",
|
||||
"extension": "ext.vm-packer",
|
||||
"setup": SetupSpec(secret_refs=["build-registry-token"]).model_dump(),
|
||||
}
|
||||
)
|
||||
secrets = resolve_setup_secrets(profile)
|
||||
assert secrets["build-registry-token"] == "tok123"
|
||||
|
||||
|
||||
def test_resolve_setup_secrets_missing_raises() -> None:
|
||||
profile = Profile.model_validate(
|
||||
{
|
||||
"id": "profile.build",
|
||||
"version": "1.0.0",
|
||||
"extension": "ext.vm-packer",
|
||||
"setup": SetupSpec(secret_refs=["missing-ref"]).model_dump(),
|
||||
}
|
||||
)
|
||||
with pytest.raises(ValueError, match="Unresolved secret_refs"):
|
||||
resolve_setup_secrets(profile)
|
||||
|
||||
|
||||
def test_empty_secret_refs() -> None:
|
||||
profile = Profile.model_validate(
|
||||
{
|
||||
"id": "profile.compose-e2e",
|
||||
"version": "1.0.0",
|
||||
"extension": "ext.compose-ssh",
|
||||
}
|
||||
)
|
||||
assert resolve_setup_secrets(profile) == {}
|
||||
Reference in New Issue
Block a user