merge: testing → main (reconcile 2-week divergence)
This commit is contained in:
91
tests/service_testing/test_instance_seed.py
Normal file
91
tests/service_testing/test_instance_seed.py
Normal file
@@ -0,0 +1,91 @@
|
||||
"""
|
||||
Tests for decnet/templates/instance_seed.py — the per-instance stealth
|
||||
seeding helper. These tests pin NODE_NAME to assert determinism of the
|
||||
seeded functions, and sweep NODE_NAMEs to assert cross-fleet divergence.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import importlib.util
|
||||
import sys
|
||||
import time
|
||||
from unittest.mock import patch
|
||||
|
||||
|
||||
def _load_seed(node_name: str):
|
||||
sys.modules.pop("instance_seed", None)
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"instance_seed", "decnet/templates/instance_seed.py"
|
||||
)
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
with patch.dict("os.environ", {"NODE_NAME": node_name}, clear=False):
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def test_same_nodename_yields_stable_uuid():
|
||||
a = _load_seed("deckie-42").instance_uuid("x")
|
||||
b = _load_seed("deckie-42").instance_uuid("x")
|
||||
assert a == b
|
||||
|
||||
|
||||
def test_different_nodename_yields_different_uuid():
|
||||
a = _load_seed("deckie-alpha").instance_uuid("x")
|
||||
b = _load_seed("deckie-beta").instance_uuid("x")
|
||||
assert a != b
|
||||
|
||||
|
||||
def test_pick_is_deterministic_per_instance():
|
||||
choices = ["a", "b", "c", "d", "e"]
|
||||
m1 = _load_seed("hostX")
|
||||
m2 = _load_seed("hostX")
|
||||
assert m1.pick(choices) == m2.pick(choices)
|
||||
|
||||
|
||||
def test_pick_varies_across_fleet():
|
||||
"""For a reasonable fleet size, pick should land on at least 2 distinct
|
||||
values. Anything less means the seed isn't actually diversifying output."""
|
||||
choices = list("abcdefghij")
|
||||
picks = {_load_seed(f"host{i}").pick(choices) for i in range(20)}
|
||||
assert len(picks) >= 3
|
||||
|
||||
|
||||
def test_uptime_monotonic_across_calls():
|
||||
mod = _load_seed("uptime-host")
|
||||
u1 = mod.uptime_seconds()
|
||||
time.sleep(0.02)
|
||||
u2 = mod.uptime_seconds()
|
||||
assert u2 >= u1
|
||||
|
||||
|
||||
def test_uptime_includes_boot_offset():
|
||||
"""uptime should be > a few minutes even at process start — deckies
|
||||
should not look like they just booted."""
|
||||
mod = _load_seed("fresh-host")
|
||||
assert mod.uptime_seconds() > 600
|
||||
|
||||
|
||||
def test_fresh_bytes_is_not_deterministic():
|
||||
"""fresh_bytes is per-connection randomness, not seeded — otherwise
|
||||
two MySQL handshakes to the same decky would present identical salts."""
|
||||
mod = _load_seed("host")
|
||||
assert mod.fresh_bytes(16) != mod.fresh_bytes(16)
|
||||
|
||||
|
||||
def test_random_bytes_is_deterministic():
|
||||
"""random_bytes is the *seeded* variant — used for stable per-instance
|
||||
identifiers like cluster UUIDs."""
|
||||
a = _load_seed("h").random_bytes(16, "ns")
|
||||
b = _load_seed("h").random_bytes(16, "ns")
|
||||
assert a == b
|
||||
|
||||
|
||||
def test_jitter_sleeps_in_range():
|
||||
mod = _load_seed("jh")
|
||||
|
||||
async def run():
|
||||
start = time.perf_counter()
|
||||
await mod.jitter(10, 30)
|
||||
return time.perf_counter() - start
|
||||
|
||||
elapsed = asyncio.run(run())
|
||||
assert 0.005 <= elapsed <= 0.200 # generous upper bound for CI jitter
|
||||
Reference in New Issue
Block a user