test(telemetry): add coverage for PolicyRegisteredEvent (#4338)
PolicyRegisteredEvent was defined and called in both policy routes but
had zero test coverage. Add three test groups:
1. _build_record serialisation (tests/test_telemetry.py): verify that
admin-scope (session_id=None) and session-scope events produce the
correct wire format — promoted top-level fields vs. params content.
2. Default-policy route emit (tests/server/routes/test_default_policies.py):
POST /v1/policies fires exactly one PolicyRegisteredEvent with
scope='admin'; a 409 conflict does not emit.
3. Session-policy route emit (tests/server/routes/test_session_policies_crud.py):
POST /v1/sessions/{id}/policies fires exactly one PolicyRegisteredEvent
with scope='session' and the correct session_id; a 409 conflict does not emit.
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
This commit is contained in:
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@@ -301,3 +302,38 @@ async def test_list_no_config_policies_when_caps_empty(
|
||||
data = resp.json()["data"]
|
||||
config_entries = [p for p in data if p.get("source") == "config"]
|
||||
assert config_entries == []
|
||||
|
||||
|
||||
# ── Telemetry ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def test_create_default_policy_emits_telemetry(
|
||||
policy_client: httpx.AsyncClient,
|
||||
) -> None:
|
||||
"""``POST /v1/policies`` emits a ``PolicyRegisteredEvent`` with scope='admin'."""
|
||||
from omnigent.telemetry.events import PolicyRegisteredEvent
|
||||
|
||||
with patch("omnigent.server.routes.default_policies._tel_emit") as mock_emit:
|
||||
resp = await policy_client.post("/v1/policies", json=_policy_payload())
|
||||
assert resp.status_code == 200
|
||||
|
||||
mock_emit.assert_called_once()
|
||||
event = mock_emit.call_args[0][0]
|
||||
assert isinstance(event, PolicyRegisteredEvent)
|
||||
assert event.scope == "admin"
|
||||
assert event.session_id is None
|
||||
assert event.handler == _REGISTERED_HANDLER
|
||||
assert event.policy_type == "python"
|
||||
|
||||
|
||||
async def test_create_default_policy_no_telemetry_on_error(
|
||||
policy_client: httpx.AsyncClient,
|
||||
) -> None:
|
||||
"""``POST /v1/policies`` does not emit telemetry when the request fails."""
|
||||
with patch("omnigent.server.routes.default_policies._tel_emit") as mock_emit:
|
||||
# Duplicate name → 409 before the emit block is reached.
|
||||
await policy_client.post("/v1/policies", json=_policy_payload(name="dup"))
|
||||
mock_emit.reset_mock()
|
||||
resp = await policy_client.post("/v1/policies", json=_policy_payload(name="dup"))
|
||||
assert resp.status_code == 409
|
||||
mock_emit.assert_not_called()
|
||||
|
||||
@@ -10,6 +10,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
@@ -255,3 +256,43 @@ async def test_delete_session_policy(client: httpx.AsyncClient, session_id: str)
|
||||
# Verify it's gone from the session policies
|
||||
get_resp = await client.get(f"/v1/sessions/{session_id}/policies/{pid}")
|
||||
assert get_resp.status_code == 404
|
||||
|
||||
|
||||
# ── Telemetry ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
async def test_create_session_policy_emits_telemetry(
|
||||
client: httpx.AsyncClient, session_id: str
|
||||
) -> None:
|
||||
"""``POST /v1/sessions/{id}/policies`` emits a ``PolicyRegisteredEvent`` with scope='session'.""" # noqa: E501
|
||||
from omnigent.telemetry.events import PolicyRegisteredEvent
|
||||
|
||||
with patch("omnigent.server.routes.session_policies._tel_emit") as mock_emit:
|
||||
resp = await client.post(
|
||||
f"/v1/sessions/{session_id}/policies",
|
||||
json=_policy_payload(),
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
|
||||
mock_emit.assert_called_once()
|
||||
event = mock_emit.call_args[0][0]
|
||||
assert isinstance(event, PolicyRegisteredEvent)
|
||||
assert event.scope == "session"
|
||||
assert event.session_id == session_id
|
||||
assert event.handler == "https://example.com/policies/eval"
|
||||
assert event.policy_type == "url"
|
||||
|
||||
|
||||
async def test_create_session_policy_no_telemetry_on_error(
|
||||
client: httpx.AsyncClient, session_id: str
|
||||
) -> None:
|
||||
"""``POST /v1/sessions/{id}/policies`` does not emit telemetry when the request fails."""
|
||||
with patch("omnigent.server.routes.session_policies._tel_emit") as mock_emit:
|
||||
# Duplicate name → 409 before the emit block is reached.
|
||||
await client.post(f"/v1/sessions/{session_id}/policies", json=_policy_payload(name="dup"))
|
||||
mock_emit.reset_mock()
|
||||
resp = await client.post(
|
||||
f"/v1/sessions/{session_id}/policies", json=_policy_payload(name="dup")
|
||||
)
|
||||
assert resp.status_code == 409
|
||||
mock_emit.assert_not_called()
|
||||
|
||||
@@ -491,6 +491,77 @@ def test_host_registry_get_host_installation_id_registered() -> None:
|
||||
assert registry.get_host_installation_id("host_abc") == "inst-xyz"
|
||||
|
||||
|
||||
# ── PolicyRegisteredEvent ────────────────────────────────────────────────────
|
||||
|
||||
|
||||
def test_build_record_policy_registered_event_admin_scope() -> None:
|
||||
"""``_build_record`` serialises admin-scope ``PolicyRegisteredEvent`` correctly.
|
||||
|
||||
``installation_id``, ``session_id``, and ``anon_user_id`` are promoted to
|
||||
top-level ``data`` fields; ``handler``, ``policy_type``, and ``scope`` go
|
||||
into ``params``. Admin policies have ``session_id=None`` which becomes
|
||||
an empty string in the wire format.
|
||||
"""
|
||||
import omnigent.telemetry.client as _mod
|
||||
from omnigent.telemetry.events import PolicyRegisteredEvent
|
||||
|
||||
event = PolicyRegisteredEvent(
|
||||
installation_id="inst-abc",
|
||||
handler="omnigent.policies.builtins.safety.ask_on_os_tools",
|
||||
policy_type="python",
|
||||
scope="admin",
|
||||
session_id=None,
|
||||
anon_user_id="deadbeef01234567",
|
||||
)
|
||||
record = _mod._build_record(event)
|
||||
data = record["data"]
|
||||
|
||||
assert data["event_name"] == "PolicyRegisteredEvent"
|
||||
assert data["installation_id"] == "inst-abc"
|
||||
assert data["anon_user_id"] == "deadbeef01234567"
|
||||
# session_id=None → empty string sentinel in wire format
|
||||
assert data["session_id"] == ""
|
||||
# Event-specific fields live in params, not at top level.
|
||||
params = json.loads(data["params"]) if data["params"] else {}
|
||||
assert params["handler"] == "omnigent.policies.builtins.safety.ask_on_os_tools"
|
||||
assert params["policy_type"] == "python"
|
||||
assert params["scope"] == "admin"
|
||||
# Promoted fields must not leak into params.
|
||||
assert "installation_id" not in params
|
||||
assert "session_id" not in params
|
||||
assert "anon_user_id" not in params
|
||||
|
||||
|
||||
def test_build_record_policy_registered_event_session_scope() -> None:
|
||||
"""``_build_record`` serialises session-scope ``PolicyRegisteredEvent`` correctly.
|
||||
|
||||
``session_id`` is a real string for session-scoped policies and must appear
|
||||
as the top-level ``session_id`` field, not inside ``params``.
|
||||
"""
|
||||
import omnigent.telemetry.client as _mod
|
||||
from omnigent.telemetry.events import PolicyRegisteredEvent
|
||||
|
||||
event = PolicyRegisteredEvent(
|
||||
installation_id="inst-xyz",
|
||||
handler="https://example.com/policies/eval",
|
||||
policy_type="url",
|
||||
scope="session",
|
||||
session_id="sess_abc123",
|
||||
anon_user_id=None,
|
||||
)
|
||||
record = _mod._build_record(event)
|
||||
data = record["data"]
|
||||
|
||||
assert data["event_name"] == "PolicyRegisteredEvent"
|
||||
assert data["session_id"] == "sess_abc123"
|
||||
assert data["anon_user_id"] is None
|
||||
params = json.loads(data["params"]) if data["params"] else {}
|
||||
assert params["scope"] == "session"
|
||||
assert params["handler"] == "https://example.com/policies/eval"
|
||||
assert params["policy_type"] == "url"
|
||||
assert "session_id" not in params
|
||||
|
||||
|
||||
def test_build_record_promotes_host_installation_id() -> None:
|
||||
"""``_build_record`` lifts ``host_installation_id`` to top-level data."""
|
||||
import omnigent.telemetry.client as _mod
|
||||
|
||||
Reference in New Issue
Block a user