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:
Tomu Hirata
2026-08-20 06:22:20 +09:00
committed by GitHub
parent 741f2d29e4
commit 4ac3dfc5be
3 changed files with 148 additions and 0 deletions
@@ -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()
+71
View File
@@ -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