Newer
Older
navi-1 / tests / unit / overlay / test_notify_route.py
"""POST /config/notify — the hive's push, on the instance side."""

import asyncio

import pytest
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient

from navi.api.routes import config_overlay
from navi.overlay.state import get_state, reset_state
from navi.settings_model import Settings


@pytest.fixture(autouse=True)
def _state():
    reset_state()
    state = get_state()
    state.enabled = True
    state.base_settings = Settings(_env_file=None)
    yield
    reset_state()


@pytest.fixture()
def app(monkeypatch):
    monkeypatch.setattr("navi.swarm.verify_swarm_key", lambda key: key == "swarm-secret")
    app = FastAPI()
    app.include_router(config_overlay.router)
    return app


async def _post(app, **kwargs):
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://navi") as client:
        return await client.post("/config/notify", **kwargs)


async def test_notify_requires_the_swarm_key(app):
    assert (await _post(app)).status_code == 401
    assert (await _post(app, headers={"X-Swarm-Key": "nope"})).status_code == 403


async def test_notify_schedules_a_refresh(app, monkeypatch):
    calls = []

    def fake_refresh(trigger):
        calls.append(trigger)
        return {"status": "applied"}

    monkeypatch.setattr(config_overlay, "refresh", fake_refresh)
    resp = await _post(
        app,
        json={"version": 3, "hash": "sha256:x"},
        headers={"X-Swarm-Key": "swarm-secret"},
    )
    assert resp.status_code == 202
    assert resp.json()["status"] == "scheduled"

    for _ in range(100):
        if calls:
            break
        await asyncio.sleep(0.01)
    assert calls == ["notify"]


async def test_notify_on_a_disabled_instance_does_nothing(app):
    get_state().enabled = False
    resp = await _post(
        app, json={"version": 3}, headers={"X-Swarm-Key": "swarm-secret"}
    )
    assert resp.status_code == 202
    assert "disabled" in resp.json()["detail"]


async def test_status_requires_the_key_and_returns_the_state(app):
    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url="http://navi") as client:
        assert (await client.get("/config/status")).status_code == 401
        body = (
            await client.get(
                "/config/status", headers={"X-Swarm-Key": "swarm-secret"}
            )
        ).json()
    assert "status" in body
    assert "hash" in body