Harden production workflows and agent admission

This commit is contained in:
Stanislav Rossovskii
2026-05-28 14:19:27 +04:00
parent a2e88b4e76
commit 37b1a1d6d6
109 changed files with 4927 additions and 894 deletions

View File

@@ -1,14 +1,14 @@
import pytest
from sqlalchemy import func, select
from monlet_server.models import Check, Event
from monlet_server.models import Agent, Check, Event
from ._helpers import make_event, make_heartbeat
from ._helpers import make_event, register_agent
@pytest.mark.asyncio
async def test_event_accepted_and_state(app_client, auth_headers, session):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_event_accepted_and_state(app_client, ui_auth_headers, session):
auth_headers = await register_agent(app_client, ui_auth_headers)
ev = make_event(status="ok", output="disk ok")
r = await app_client.post(
"/api/v1/events", json={"agent_id": "agent-1", "events": [ev]}, headers=auth_headers
@@ -23,8 +23,20 @@ async def test_event_accepted_and_state(app_client, auth_headers, session):
@pytest.mark.asyncio
async def test_duplicate_event_deduplicated(app_client, auth_headers):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_event_before_heartbeat_is_dropped(app_client, auth_headers, session):
ev = make_event(status="warning", exit_code=1)
r = await app_client.post(
"/api/v1/events", json={"agent_id": "agent-replay", "events": [ev]}, headers=auth_headers
)
assert r.status_code == 202
assert r.json() == {"accepted": 0, "deduplicated": 0}
n = (await session.execute(select(func.count()).select_from(Agent))).scalar_one()
assert n == 0
@pytest.mark.asyncio
async def test_duplicate_event_deduplicated(app_client, ui_auth_headers):
auth_headers = await register_agent(app_client, ui_auth_headers)
ev = make_event(status="critical", exit_code=2)
r1 = await app_client.post(
"/api/v1/events", json={"agent_id": "agent-1", "events": [ev]}, headers=auth_headers
@@ -46,8 +58,8 @@ async def test_empty_batch_400(app_client, auth_headers):
@pytest.mark.asyncio
@pytest.mark.parametrize("observed_at", ["2026-05-27T09:00:00", "2026-05-27T09:00:00+04:00"])
async def test_event_requires_utc_timestamp(app_client, auth_headers, observed_at):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_event_requires_utc_timestamp(app_client, ui_auth_headers, observed_at):
auth_headers = await register_agent(app_client, ui_auth_headers)
ev = make_event(observed_at=observed_at)
r = await app_client.post(
"/api/v1/events", json={"agent_id": "agent-1", "events": [ev]}, headers=auth_headers
@@ -66,8 +78,8 @@ async def test_oversize_batch_400(app_client, auth_headers):
@pytest.mark.asyncio
async def test_repeated_same_status_not_stored(app_client, auth_headers, session):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_repeated_same_status_not_stored(app_client, ui_auth_headers, session):
auth_headers = await register_agent(app_client, ui_auth_headers)
evs = [make_event(status="ok") for _ in range(5)]
r = await app_client.post(
"/api/v1/events", json={"agent_id": "agent-1", "events": evs}, headers=auth_headers
@@ -79,8 +91,8 @@ async def test_repeated_same_status_not_stored(app_client, auth_headers, session
@pytest.mark.asyncio
async def test_status_transitions_stored(app_client, auth_headers, session):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_status_transitions_stored(app_client, ui_auth_headers, session):
auth_headers = await register_agent(app_client, ui_auth_headers)
seq = [
make_event(status="ok"),
make_event(status="warning", exit_code=1),
@@ -98,8 +110,8 @@ async def test_status_transitions_stored(app_client, auth_headers, session):
@pytest.mark.asyncio
async def test_same_status_different_exit_code_stored(app_client, auth_headers, session):
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
async def test_same_status_different_exit_code_stored(app_client, ui_auth_headers, session):
auth_headers = await register_agent(app_client, ui_auth_headers)
seq = [
make_event(status="warning", exit_code=1),
make_event(status="warning", exit_code=3),
@@ -113,10 +125,10 @@ async def test_same_status_different_exit_code_stored(app_client, auth_headers,
@pytest.mark.asyncio
async def test_late_event_does_not_move_state(app_client, auth_headers, session):
async def test_late_event_does_not_move_state(app_client, ui_auth_headers, session):
from datetime import UTC, datetime, timedelta
await app_client.post("/api/v1/heartbeat", json=make_heartbeat(), headers=auth_headers)
auth_headers = await register_agent(app_client, ui_auth_headers)
newer = datetime.now(UTC).replace(microsecond=0)
older = (newer - timedelta(minutes=5)).isoformat()
ev_now = make_event(