refactor(tests): move flat tests/*.py into per-subsystem subfolders
Groups every flat test_*.py under the module it exercises, matching the
existing tests/{profiler,sniffer,prober,collector,correlation,cli,web,
topology,swarm,bus,updater,api,docker,geoip,...} layout. New folders:
services/, fleet/, config/, logging/, db/ (+ db/mysql/), telemetry/,
mutator/, core/.
Path-dependent __file__ references bumped an extra .parent in three
files that moved one level deeper:
- tests/sniffer/test_sniffer_ja3.py (template path)
- tests/services/test_ssh_capture_emit.py (template path)
- tests/cli/test_mode_gating.py (REPO root)
- tests/web/test_env_lazy_jwt.py (repo var)
Also drops two SQLite runtime artifacts (test_decnet.db-{shm,wal}) that
were leaking into the repo from a previous test run.
Fixes two test_service_isolation cases that patched asyncio.sleep (no
longer on the profiler main-loop hot path — same pre-existing bug I
fixed earlier in test_attacker_worker.py) by patching asyncio.wait_for
and passing interval=0.
This commit is contained in:
143
tests/config/test_config.py
Normal file
143
tests/config/test_config.py
Normal file
@@ -0,0 +1,143 @@
|
||||
"""
|
||||
Tests for decnet.config — Pydantic models, save/load/clear state.
|
||||
Covers the uncovered lines: validators, save_state, load_state, clear_state.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import decnet.config as config_module
|
||||
from decnet.config import (
|
||||
DeckyConfig,
|
||||
DecnetConfig,
|
||||
save_state,
|
||||
load_state,
|
||||
clear_state,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DeckyConfig validator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestDeckyConfig:
|
||||
def _base(self, **kwargs):
|
||||
defaults = dict(
|
||||
name="decky-01", ip="192.168.1.10", services=["ssh"],
|
||||
distro="debian", base_image="debian", hostname="host-01",
|
||||
)
|
||||
defaults.update(kwargs)
|
||||
return defaults
|
||||
|
||||
def test_valid_decky(self):
|
||||
d = DeckyConfig(**self._base())
|
||||
assert d.name == "decky-01"
|
||||
|
||||
def test_empty_services_raises(self):
|
||||
with pytest.raises(Exception, match="at least 1 item"):
|
||||
DeckyConfig(**self._base(services=[]))
|
||||
|
||||
def test_multiple_services_ok(self):
|
||||
d = DeckyConfig(**self._base(services=["ssh", "smb", "rdp"]))
|
||||
assert len(d.services) == 3
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# DecnetConfig validator
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestDecnetConfig:
|
||||
def _base_decky(self):
|
||||
return DeckyConfig(
|
||||
name="d", ip="10.0.0.2", services=["ssh"],
|
||||
distro="debian", base_image="debian", hostname="h",
|
||||
)
|
||||
|
||||
def test_valid_config(self):
|
||||
cfg = DecnetConfig(
|
||||
mode="unihost", interface="eth0",
|
||||
subnet="10.0.0.0/24", gateway="10.0.0.1",
|
||||
deckies=[self._base_decky()],
|
||||
)
|
||||
assert cfg.mode == "unihost"
|
||||
|
||||
def test_log_file_field(self):
|
||||
cfg = DecnetConfig(
|
||||
mode="unihost", interface="eth0",
|
||||
subnet="10.0.0.0/24", gateway="10.0.0.1",
|
||||
deckies=[self._base_decky()],
|
||||
log_file="/var/log/decnet/decnet.log",
|
||||
)
|
||||
assert cfg.log_file == "/var/log/decnet/decnet.log"
|
||||
|
||||
def test_log_file_defaults_to_none(self):
|
||||
cfg = DecnetConfig(
|
||||
mode="unihost", interface="eth0",
|
||||
subnet="10.0.0.0/24", gateway="10.0.0.1",
|
||||
deckies=[self._base_decky()],
|
||||
)
|
||||
assert cfg.log_file is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# save_state / load_state / clear_state
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def patch_state_file(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(config_module, "STATE_FILE", tmp_path / "decnet-state.json")
|
||||
|
||||
|
||||
def _sample_config():
|
||||
return DecnetConfig(
|
||||
mode="unihost", interface="eth0",
|
||||
subnet="192.168.1.0/24", gateway="192.168.1.1",
|
||||
deckies=[
|
||||
DeckyConfig(
|
||||
name="decky-01", ip="192.168.1.10", services=["ssh"],
|
||||
distro="debian", base_image="debian", hostname="host-01",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
def test_save_and_load_state(tmp_path):
|
||||
cfg = _sample_config()
|
||||
compose = tmp_path / "docker-compose.yml"
|
||||
save_state(cfg, compose)
|
||||
|
||||
result = load_state()
|
||||
assert result is not None
|
||||
loaded_cfg, loaded_compose = result
|
||||
assert loaded_cfg.mode == "unihost"
|
||||
assert loaded_cfg.deckies[0].name == "decky-01"
|
||||
assert loaded_compose == compose
|
||||
|
||||
|
||||
def test_load_state_returns_none_when_missing(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(config_module, "STATE_FILE", tmp_path / "nonexistent.json")
|
||||
assert load_state() is None
|
||||
|
||||
|
||||
def test_clear_state(tmp_path):
|
||||
cfg = _sample_config()
|
||||
save_state(cfg, tmp_path / "compose.yml")
|
||||
assert config_module.STATE_FILE.exists()
|
||||
|
||||
clear_state()
|
||||
assert not config_module.STATE_FILE.exists()
|
||||
|
||||
|
||||
def test_clear_state_noop_when_missing(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(config_module, "STATE_FILE", tmp_path / "nonexistent.json")
|
||||
clear_state() # should not raise
|
||||
|
||||
|
||||
def test_state_roundtrip_preserves_all_fields(tmp_path):
|
||||
cfg = _sample_config()
|
||||
cfg.deckies[0].archetype = "workstation"
|
||||
cfg.deckies[0].mutate_interval = 45
|
||||
compose = tmp_path / "compose.yml"
|
||||
save_state(cfg, compose)
|
||||
|
||||
loaded_cfg, _ = load_state()
|
||||
assert loaded_cfg.deckies[0].archetype == "workstation"
|
||||
assert loaded_cfg.deckies[0].mutate_interval == 45
|
||||
Reference in New Issue
Block a user