"""hive — the swarm address book service (see hive/app.py).
The app module reads env config at import time, so each test reimports it with
fresh env into its own key file / database.
"""
import importlib
import sqlite3
import pytest
from fastapi.testclient import TestClient
@pytest.fixture()
def hive(tmp_path, monkeypatch):
monkeypatch.setenv("HIVE_KEY_FILE", str(tmp_path / ".swarm-key"))
monkeypatch.setenv("HIVE_DB", str(tmp_path / "hive.db"))
monkeypatch.setenv("HIVE_TTL_SEC", "300")
# Deterministic admin key: no ambient .hive-admin-key from the checkout.
monkeypatch.setenv("HIVE_ADMIN_KEY", "admin-secret")
monkeypatch.setenv("HIVE_ADMIN_KEY_FILE", str(tmp_path / ".hive-admin-key"))
(tmp_path / ".swarm-key").write_text("secret-key-123")
import hive.app as hive_app
module = importlib.reload(hive_app)
yield module
module._db.close()
def _headers(key: str = "secret-key-123") -> dict:
return {"X-Swarm-Key": key}
def _announce(client, name="quiet-otter", instance_id="11111111-2222-3333-4444-555555555555", port=8099):
return client.post(
"/announce",
json={"name": name, "instance_id": instance_id, "version": "0.1.0", "port": port,
"meta": {"hostname": "gpu-box", "os": "Linux 6.1", "cpu_cores": 16, "ram_mb": 65536}},
headers=_headers(),
)
def test_health_open_without_key(hive):
with TestClient(hive.app) as client:
resp = client.get("/health")
assert resp.status_code == 200
assert resp.json()["hive"] is True
def test_announce_requires_key(hive):
with TestClient(hive.app) as client:
assert client.post("/announce", json={"name": "x", "instance_id": "12345678", "port": 8099}).status_code == 401
assert client.post(
"/announce",
json={"name": "x", "instance_id": "12345678", "port": 8099},
headers={"X-Swarm-Key": "wrong"},
).status_code == 403
def test_peers_requires_key(hive):
with TestClient(hive.app) as client:
assert client.get("/peers").status_code == 401
assert client.get("/peers", headers={"X-Swarm-Key": "wrong"}).status_code == 403
def test_announce_upsert_and_peers_listing(hive):
with TestClient(hive.app) as client:
assert _announce(client).status_code == 200
# same instance_id again = upsert (renamed, different port), not a second row
assert _announce(client, name="renamed-otter", port=8123).status_code == 200
# a second machine
assert _announce(client, name="amber-falcon", instance_id="99999999-2222-3333-4444-555555555555").status_code == 200
resp = client.get("/peers", headers=_headers())
assert resp.status_code == 200
body = resp.json()
peers = body["peers"]
assert len(peers) == 2
assert [p["name"] for p in peers] == ["amber-falcon", "renamed-otter"] # sorted by name
otter = peers[1]
assert otter["port"] == 8123
assert otter["address"] == f"{otter['host']}:8123"
assert otter["online"] is True
assert otter["meta"]["hostname"] == "gpu-box"
assert otter["meta"]["ram_mb"] == 65536
assert otter["version"] == "0.1.0"
assert body["ttl_sec"] == 300
def test_offline_after_ttl(hive, tmp_path):
with TestClient(hive.app) as client:
_announce(client)
# backdate the last announcement beyond the TTL — machine went quiet
conn = sqlite3.connect(tmp_path / "hive.db")
conn.execute("UPDATE machines SET last_seen = last_seen - 400")
conn.commit()
conn.close()
peers = client.get("/peers", headers=_headers()).json()["peers"]
assert peers[0]["online"] is False
def test_previous_key_still_accepted(hive, tmp_path):
# rotation window: .swarm-key.previous keeps working
(tmp_path / ".swarm-key.previous").write_text("old-key-42")
import hive.app as module
importlib.reload(module)
with TestClient(module.app) as client:
resp = client.post(
"/announce",
json={"name": "x", "instance_id": "12345678", "port": 8099},
headers={"X-Swarm-Key": "old-key-42"},
)
assert resp.status_code == 200
module._db.close()
def test_hive_without_key_file_refuses(hive, tmp_path, monkeypatch):
monkeypatch.setenv("HIVE_KEY_FILE", str(tmp_path / ".no-key"))
monkeypatch.setenv("HIVE_DB", str(tmp_path / "hive2.db"))
import hive.app as module
importlib.reload(module)
with TestClient(module.app) as client:
resp = client.post(
"/announce",
json={"name": "x", "instance_id": "12345678", "port": 8099},
headers={"X-Swarm-Key": "whatever"},
)
assert resp.status_code == 503
module._db.close()
# ── config documents ────────────────────────────────────────────────────────
def _admin(key: str = "admin-secret") -> dict:
return {"X-Admin-Key": key}
def _publish(client, layers, note="", force=False, key="admin-secret"):
return client.put(
"/config",
json={"layers": layers, "note": note, "force": force},
headers=_admin(key),
)
def _global(**settings):
return {"name": "global", "config": {"settings": settings}}
def test_publish_requires_admin_key(hive):
with TestClient(hive.app) as client:
resp = client.put("/config", json={"layers": [_global(llm_complete_timeout=30)]})
assert resp.status_code == 403
bad = client.put(
"/config",
json={"layers": [_global(llm_complete_timeout=30)]},
headers=_admin("wrong"),
)
assert bad.status_code == 403
def test_fetch_returns_404_before_anything_is_published(hive):
with TestClient(hive.app) as client:
assert client.get("/config", headers=_headers()).status_code == 404
def test_publish_then_fetch_global_layer(hive):
with TestClient(hive.app) as client:
published = _publish(client, [_global(llm_complete_timeout=30)]).json()
assert published["status"] == "published"
assert published["version"] == 1
body = client.get("/config", headers=_headers()).json()
assert body["version"] == 1
assert body["layers"] == [
{"name": "global", "config": {"settings": {"llm_complete_timeout": 30}}}
]
def test_layers_target_by_tag_and_name(hive):
layers = [
{"name": "global", "config": {"settings": {"llm_complete_timeout": 30}}},
{"name": "lab", "match": {"tags": ["lab"]}, "config": {"settings": {"llm_complete_retries": 1}}},
{"name": "yuki", "match": {"names": ["yuki"]}, "config": {"settings": {"navi_push_cooldown_sec": 5}}},
]
with TestClient(hive.app) as client:
_publish(client, layers)
plain = client.get("/config", params={"name": "other"}, headers=_headers()).json()
assert [layer["name"] for layer in plain["layers"]] == ["global"]
lab = client.get(
"/config", params={"name": "other", "tags": "beta,lab"}, headers=_headers()
).json()
assert [layer["name"] for layer in lab["layers"]] == ["global", "lab"]
named = client.get("/config", params={"name": "yuki"}, headers=_headers()).json()
assert [layer["name"] for layer in named["layers"]] == ["global", "yuki"]
# The hash is over the layers that instance receives, not the document.
assert plain["hash"] != named["hash"]
def test_identical_publish_is_a_no_op(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
again = _publish(client, [_global(llm_complete_timeout=30)]).json()
assert again["status"] == "unchanged"
assert again["version"] == 1
forced = _publish(client, [_global(llm_complete_timeout=30)], force=True).json()
assert forced["status"] == "renotified"
assert forced["version"] == 1
assert len(client.get("/config/history", headers=_admin()).json()["versions"]) == 1
def test_empty_document_clears_the_overlay(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
cleared = _publish(client, [], note="back to local").json()
assert cleared["status"] == "published"
assert cleared["version"] == 2
body = client.get("/config", headers=_headers()).json()
assert body["layers"] == []
def test_invalid_document_is_rejected_with_a_reason(hive):
with TestClient(hive.app) as client:
resp = _publish(client, [{"name": "x"}])
assert resp.status_code == 400
assert "config" in resp.json()["detail"]
resp = _publish(client, [{"name": "x", "config": {}, "match": {"nope": ["a"]}}])
assert resp.status_code == 400
def test_announce_hint_tells_a_behind_instance_to_fetch(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
# An older build that reports no config: unknown, not behind.
old = _announce(client).json()
assert old["config"] is None
# A current build with an empty hash is behind.
behind = client.post(
"/announce",
json={
"name": "quiet-otter",
"instance_id": "11111111-2222-3333-4444-555555555555",
"port": 8099,
"meta": {"config": {"hash": "", "status": "applied"}},
},
headers=_headers(),
).json()
assert behind["config"]["fetch"] is True
assert behind["config"]["version"] == 1
# One that reports the exact effective hash is up to date.
effective = client.get("/config", headers=_headers()).json()["hash"]
current = client.post(
"/announce",
json={
"name": "quiet-otter",
"instance_id": "11111111-2222-3333-4444-555555555555",
"port": 8099,
"meta": {"config": {"hash": effective, "status": "applied"}},
},
headers=_headers(),
).json()
assert current["config"] is None
def test_ack_is_recorded_and_shown_in_status(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
_announce(client)
resp = client.post(
"/config/ack",
json={
"instance_id": "11111111-2222-3333-4444-555555555555",
"version": 1,
"hash": "sha256:x",
"status": "staged",
"restart_required": ["openai_api_key"],
},
headers=_headers(),
)
assert resp.status_code == 200
status = client.get("/config/status", headers=_admin()).json()
row = status["instances"][0]
assert row["ack"]["status"] == "staged"
assert row["ack"]["restart_required"] == ["openai_api_key"]
def test_status_marks_behind_instances(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
effective = client.get("/config", headers=_headers()).json()["hash"]
client.post(
"/announce",
json={
"name": "quiet-otter",
"instance_id": "11111111-2222-3333-4444-555555555555",
"port": 8099,
"meta": {"config": {"hash": "sha256:old", "status": "applied"}},
},
headers=_headers(),
)
status = client.get("/config/status", headers=_admin()).json()
assert status["behind"] == ["quiet-otter"]
assert status["instances"][0]["effective_hash"] == effective
client.post(
"/announce",
json={
"name": "quiet-otter",
"instance_id": "11111111-2222-3333-4444-555555555555",
"port": 8099,
"meta": {"config": {"hash": effective, "status": "applied"}},
},
headers=_headers(),
)
assert client.get("/config/status", headers=_admin()).json()["behind"] == []
def test_history_version_and_rollback(hive):
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)], note="first")
_publish(client, [_global(llm_complete_timeout=45)], note="second")
history = client.get("/config/history", headers=_admin()).json()["versions"]
assert [v["id"] for v in history] == [2, 1]
assert history[1]["note"] == "first"
version = client.get("/config/version/1", headers=_admin()).json()
assert version["layers"][0]["config"]["settings"]["llm_complete_timeout"] == 30
rolled = client.post("/config/rollback/1", headers=_admin()).json()
assert rolled["status"] == "rolled_back"
assert rolled["version"] == 3
assert client.get("/config", headers=_headers()).json()["layers"][0]["config"] == {
"settings": {"llm_complete_timeout": 30}
}
# History keeps the bad version rather than rewriting it.
assert len(client.get("/config/history", headers=_admin()).json()["versions"]) == 3
async def test_notify_all_pings_every_registered_instance(hive, monkeypatch):
with TestClient(hive.app) as client:
_announce(client, name="one", instance_id="aaaaaaaa-0000-0000-0000-000000000001")
_announce(client, name="two", instance_id="aaaaaaaa-0000-0000-0000-000000000002")
calls = []
async def fake(peer, version, doc_hash):
calls.append((peer["name"], version, doc_hash))
return True
monkeypatch.setattr(hive, "_notify_one", fake)
await hive._notify_all(4, "sha256:x")
assert sorted(name for name, _, _ in calls) == ["one", "two"]
async def test_retry_sweep_targets_only_the_instance_that_is_behind(hive, monkeypatch):
with TestClient(hive.app) as client:
_announce(client, name="one", instance_id="aaaaaaaa-0000-0000-0000-000000000001")
_announce(client, name="two", instance_id="aaaaaaaa-0000-0000-0000-000000000002")
_publish(client, [_global(llm_complete_timeout=30)])
effective = client.get("/config", headers=_headers()).json()["hash"]
client.post(
"/announce",
json={
"name": "one",
"instance_id": "aaaaaaaa-0000-0000-0000-000000000001",
"port": 8099,
"meta": {"config": {"hash": effective, "status": "applied"}},
},
headers=_headers(),
)
client.post(
"/announce",
json={
"name": "two",
"instance_id": "aaaaaaaa-0000-0000-0000-000000000002",
"port": 8099,
"meta": {"config": {"hash": "sha256:old", "status": "applied"}},
},
headers=_headers(),
)
calls = []
async def fake(peer, version, doc_hash):
calls.append(peer["name"])
return True
monkeypatch.setattr(hive, "_notify_one", fake)
await hive._notify_behind()
assert calls == ["two"]
def test_a_fresh_ack_beats_a_stale_announce(hive):
"""The announce may lag an interval behind; an ack for the current hash is proof."""
iid = "11111111-2222-3333-4444-555555555555"
with TestClient(hive.app) as client:
_publish(client, [_global(llm_complete_timeout=30)])
effective = client.get("/config", headers=_headers()).json()["hash"]
client.post(
"/announce",
json={
"name": "quiet-otter",
"instance_id": iid,
"port": 8099,
"meta": {"config": {"hash": "sha256:old", "status": "applied"}},
},
headers=_headers(),
)
assert client.get("/config/status", headers=_admin()).json()["behind"] == [
"quiet-otter"
]
client.post(
"/config/ack",
json={"instance_id": iid, "version": 1, "hash": effective, "status": "applied"},
headers=_headers(),
)
assert client.get("/config/status", headers=_admin()).json()["behind"] == []