Improve timestamp handling and event navigation

This commit is contained in:
Stanislav Rossovskii
2026-05-27 11:01:27 +04:00
parent edc51e9c59
commit 71f0035b0b
37 changed files with 485 additions and 109 deletions

View File

@@ -43,6 +43,18 @@ async def test_empty_batch_400(app_client, auth_headers):
assert r.status_code == 400
@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)
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
)
assert r.status_code == 400
assert r.json()["error"]["code"] == "validation"
@pytest.mark.asyncio
async def test_oversize_batch_400(app_client, auth_headers):
evs = [make_event() for _ in range(201)]

View File

@@ -43,6 +43,16 @@ async def test_heartbeat_validation_400(app_client, auth_headers):
assert r.json()["error"]["code"] == "validation"
@pytest.mark.asyncio
@pytest.mark.parametrize("observed_at", ["2026-05-27T09:00:00", "2026-05-27T09:00:00+04:00"])
async def test_heartbeat_requires_utc_timestamp(app_client, auth_headers, observed_at):
hb = make_heartbeat()
hb["observed_at"] = observed_at
r = await app_client.post("/api/v1/heartbeat", json=hb, headers=auth_headers)
assert r.status_code == 400
assert r.json()["error"]["code"] == "validation"
@pytest.mark.asyncio
async def test_heartbeat_rejects_extra_fields(app_client, auth_headers):
hb = make_heartbeat()

View File

@@ -1,5 +1,6 @@
from __future__ import annotations
import json
import os
from datetime import UTC, datetime, timedelta
from uuid import uuid4
@@ -27,8 +28,8 @@ def _payload() -> dict:
"status": "critical",
"severity": "critical",
"incident_key": "agent-1:chk-1",
"observed_at": datetime.now(UTC).isoformat(),
"opened_at": datetime.now(UTC).isoformat(),
"observed_at": "2026-05-27T05:00:00+00:00",
"opened_at": "2026-05-27T05:00:00+00:00",
"summary": "boom",
"output": "stderr: boom token=abc123",
}
@@ -60,12 +61,13 @@ async def test_telegram_success_and_redaction():
def handler(req):
body = req.content.decode()
assert "token=abc123" not in body
assert "2026-05-27 09:00:00 +04" in body
assert "***" in body
return httpx.Response(200, json={"ok": True})
client, t = _client(handler)
try:
n = TelegramNotifier(client, "TOK", "123")
n = TelegramNotifier(client, "TOK", "123", "Asia/Tbilisi")
res = await n.deliver("firing", _payload())
assert res.ok
assert "/botTOK/sendMessage" in str(t.requests[0].url)
@@ -77,7 +79,7 @@ async def test_telegram_success_and_redaction():
async def test_telegram_4xx_permanent():
client, _ = _client(lambda r: httpx.Response(400, json={}))
try:
n = TelegramNotifier(client, "TOK", "123")
n = TelegramNotifier(client, "TOK", "123", "UTC")
res = await n.deliver("firing", _payload())
assert not res.ok and res.permanent
finally:
@@ -88,7 +90,7 @@ async def test_telegram_4xx_permanent():
async def test_telegram_5xx_transient():
client, _ = _client(lambda r: httpx.Response(503, json={}))
try:
n = TelegramNotifier(client, "TOK", "123")
n = TelegramNotifier(client, "TOK", "123", "UTC")
res = await n.deliver("firing", _payload())
assert not res.ok and not res.permanent
finally:
@@ -102,7 +104,7 @@ async def test_telegram_network_error_transient():
client, _ = _client(handler)
try:
n = TelegramNotifier(client, "TOK", "123")
n = TelegramNotifier(client, "TOK", "123", "UTC")
res = await n.deliver("firing", _payload())
assert not res.ok and not res.permanent
finally:
@@ -140,11 +142,15 @@ async def test_alertmanager_payload_shape():
client, _ = _client(handler)
try:
n = AlertmanagerNotifier(client, "http://am/")
n = AlertmanagerNotifier(client, "http://am/", "Asia/Tbilisi")
res = await n.deliver("firing", _payload())
assert res.ok
assert captured["url"].endswith("/api/v2/alerts")
assert '"alertname":"monlet_incident"' in captured["body"]
body = json.loads(captured["body"])
alert = body[0]
assert alert["labels"]["alertname"] == "monlet_incident"
assert alert["startsAt"] == "2026-05-27T05:00:00+00:00"
assert alert["annotations"]["observed_at"] == "2026-05-27 09:00:00 +04"
finally:
await client.aclose()
@@ -348,7 +354,7 @@ async def test_alertmanager_severity_not_in_labels():
client, _ = _client(handler)
try:
n = AlertmanagerNotifier(client, "http://am")
n = AlertmanagerNotifier(client, "http://am", "UTC")
await n.deliver("firing", _payload())
import json as _json
@@ -371,7 +377,7 @@ async def test_telegram_redacts_secret_in_incident_key():
try:
p = _payload()
p["incident_key"] = "agent-1:chk-1:token=leakedsecret"
n = TelegramNotifier(client, "TOK", "1")
n = TelegramNotifier(client, "TOK", "1", "UTC")
await n.deliver("firing", p)
assert "leakedsecret" not in captured["body"]
finally: