Repo-side rename only (Phases 1-3 of the migration plan); the live box (~/.castle, systemd units, /data/castle, domains) is a separate cutover. - Slug `castle` -> `wildpc`: CLI command, module names (wildpc_core/cli/api), dist names, entry point `wildpc = wildpc_cli.main:main`. - Identifiers: CastleConfig/NATSClient/DirError/MDNS -> Wildpc*. - Env/constants: CASTLE_* -> WILDPC_*; ~/.castle -> ~/.wildpc, castle.yaml -> wildpc.yaml, /data/castle -> /data/wildpc. - Systemd UNIT_PREFIX castle- -> wildpc-; own programs castle-api/gateway/etc. - Display prose "Castle" -> "Wild PC" in docs, agent-guide files, README, frontend. - Package dirs and bootstrap yaml renamed via git mv; lockfiles regenerated; redundant nested uv.lock files dropped (workspace root lock is authoritative). Tests: core 273, cli 47, wildpc-api 120 all pass. Frontend type-checks + builds. Fixed a stale test fixture (secret_env_path kind arg) broken pre-rename.
95 lines
2.7 KiB
Python
95 lines
2.7 KiB
Python
"""Tests for the consumption audit (core/src/wildpc_core/audit.py)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import wildpc_core.config as C
|
|
from wildpc_core import audit
|
|
from wildpc_core.manifest import SystemdDeployment
|
|
|
|
|
|
def _svc(
|
|
program: str,
|
|
*,
|
|
tcp: int | None = None,
|
|
http: int | None = None,
|
|
env: dict | None = None,
|
|
requires: list | None = None,
|
|
) -> SystemdDeployment:
|
|
spec: dict = {
|
|
"manager": "systemd",
|
|
"program": program,
|
|
"run": {"launcher": "command", "argv": [program]},
|
|
}
|
|
if tcp is not None:
|
|
spec["expose"] = {"tcp": {"port": tcp}}
|
|
spec["reach"] = "internal"
|
|
elif http is not None:
|
|
spec["expose"] = {"http": {"internal": {"port": http}}}
|
|
spec["reach"] = "internal"
|
|
if env is not None:
|
|
spec["defaults"] = {"env": env}
|
|
if requires is not None:
|
|
spec["requires"] = requires
|
|
return SystemdDeployment.model_validate(spec)
|
|
|
|
|
|
def _cfg(deployments: dict) -> C.WildpcConfig:
|
|
return C.WildpcConfig(
|
|
root=None,
|
|
gateway=C.GatewayConfig(port=9000),
|
|
repo=None,
|
|
programs={},
|
|
deployments=deployments,
|
|
)
|
|
|
|
|
|
def _pairs(cfg: C.WildpcConfig) -> set[tuple[str, str]]:
|
|
return {(s.consumer, s.provider) for s in audit.suggest_consumption(cfg)}
|
|
|
|
|
|
def test_split_host_port_pair_is_suggested() -> None:
|
|
"""A split X_HOST + X_PORT pair resolves like a single host:port value."""
|
|
cfg = _cfg(
|
|
{
|
|
"broker": _svc("broker", tcp=1883),
|
|
"api": _svc(
|
|
"api",
|
|
http=9020,
|
|
env={"WILDPC_API_MQTT_HOST": "localhost", "WILDPC_API_MQTT_PORT": "1883"},
|
|
),
|
|
}
|
|
)
|
|
assert ("api", "broker") in _pairs(cfg)
|
|
|
|
|
|
def test_single_value_url_is_suggested() -> None:
|
|
cfg = _cfg(
|
|
{
|
|
"db": _svc("db", tcp=5432),
|
|
"app": _svc("app", http=9001, env={"DATABASE_URL": "postgresql://u@localhost:5432/x"}),
|
|
}
|
|
)
|
|
assert ("app", "db") in _pairs(cfg)
|
|
|
|
|
|
def test_bare_port_without_host_is_not_matched() -> None:
|
|
"""A deployment's own listen port (no host) must not become a dependency."""
|
|
cfg = _cfg({"app": _svc("app", http=9001, env={"APP_PORT": "9001"})})
|
|
assert _pairs(cfg) == set()
|
|
|
|
|
|
def test_declared_pair_is_not_suggested() -> None:
|
|
"""Already-declared consumption is not re-suggested."""
|
|
cfg = _cfg(
|
|
{
|
|
"broker": _svc("broker", tcp=1883),
|
|
"api": _svc(
|
|
"api",
|
|
http=9020,
|
|
env={"X_HOST": "localhost", "X_PORT": "1883"},
|
|
requires=[{"ref": "broker"}],
|
|
),
|
|
}
|
|
)
|
|
assert ("api", "broker") not in _pairs(cfg)
|