Skip to content

Commit 21b26ed

Browse files
update scope value to support new exporter path
1 parent 180c2aa commit 21b26ed

3 files changed

Lines changed: 124 additions & 1 deletion

File tree

CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,18 @@ Place it before imports with one blank line after.
219219
- **Never** use the keyword "Kairo" in code - it's a legacy reference that must be removed/replaced
220220
- If found during code review, flag for removal
221221

222+
### Observability Export Configuration — Coordinated Review Required
223+
224+
The following three constants must stay in sync. If a PR changes **any one** of them, the reviewer (human or Copilot) **must** ask the author to confirm the other two are still correct:
225+
226+
| Constant | Location |
227+
|---|---|
228+
| `PROD_OBSERVABILITY_SCOPE` | `libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/environment_utils.py` |
229+
| `DEFAULT_ENDPOINT_URL` | `libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py` |
230+
| Export URL path pattern | `build_export_url()` in `libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/utils.py` |
231+
232+
Snapshot tests in `tests/observability/core/test_export_config_consistency.py` will fail if any value drifts, but the developer must also verify the values are correct for the target environment — the tests only catch accidental drift, not intentional-but-incomplete updates.
233+
222234
### Python Conventions
223235

224236
- Type hints required on all function parameters and return types

libraries/microsoft-agents-a365-runtime/microsoft_agents_a365/runtime/environment_utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
import os
99

1010
# Authentication scopes for different environments
11-
PROD_OBSERVABILITY_SCOPE = "https://api.powerplatform.com/.default"
11+
PROD_OBSERVABILITY_SCOPE = (
12+
"api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite"
13+
)
1214

1315
# Cluster categories for different environments
1416
PROD_OBSERVABILITY_CLUSTER_CATEGORY = "prod"
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
"""
5+
Snapshot tests for observability export configuration.
6+
7+
These tests pin the production export URL path and authentication scope together.
8+
If the export URL path changes (e.g. from a backend migration), the scope must
9+
be reviewed and updated in lockstep — and vice versa. A failure here is a
10+
reminder to verify both values are consistent with the deployed backend.
11+
"""
12+
13+
import unittest
14+
15+
from microsoft_agents_a365.observability.core.exporters.agent365_exporter import (
16+
DEFAULT_ENDPOINT_URL,
17+
)
18+
from microsoft_agents_a365.observability.core.exporters.utils import build_export_url
19+
from microsoft_agents_a365.runtime.environment_utils import (
20+
PROD_OBSERVABILITY_SCOPE,
21+
get_observability_authentication_scope,
22+
)
23+
24+
25+
class TestExportConfigConsistency(unittest.TestCase):
26+
"""Ensure export URL, endpoint, and auth scope stay in sync.
27+
28+
These are intentionally pinned snapshot values. If any of the three
29+
production constants change (endpoint, scope, URL path), **all three
30+
tests below will likely need updating together**. That forced review is
31+
the whole point — it prevents one value from drifting without the others.
32+
"""
33+
34+
# ---- pinned production values ----
35+
36+
EXPECTED_ENDPOINT = "https://agent365.svc.cloud.microsoft"
37+
EXPECTED_SCOPE = (
38+
"api://9b975845-388f-4429-889e-eab1ef63949c/Agent365.Observability.OtelWrite"
39+
)
40+
EXPECTED_STANDARD_PATH = "/observability/tenants/{tid}/otlp/agents/{aid}/traces"
41+
EXPECTED_S2S_PATH = "/observabilityService/tenants/{tid}/otlp/agents/{aid}/traces"
42+
43+
# ---- snapshot assertions ----
44+
45+
def test_default_endpoint_url(self):
46+
"""DEFAULT_ENDPOINT_URL must match the expected production endpoint."""
47+
self.assertEqual(
48+
DEFAULT_ENDPOINT_URL,
49+
self.EXPECTED_ENDPOINT,
50+
"DEFAULT_ENDPOINT_URL changed — also review PROD_OBSERVABILITY_SCOPE "
51+
"and build_export_url() path. All three must stay in sync.",
52+
)
53+
54+
def test_prod_observability_scope_value(self):
55+
"""PROD_OBSERVABILITY_SCOPE must match the expected production scope."""
56+
self.assertEqual(
57+
PROD_OBSERVABILITY_SCOPE,
58+
self.EXPECTED_SCOPE,
59+
"PROD_OBSERVABILITY_SCOPE changed — also review DEFAULT_ENDPOINT_URL "
60+
"and build_export_url() path. All three must stay in sync.",
61+
)
62+
63+
def test_export_url_standard_path_structure(self):
64+
"""Standard export URL must use the pinned path pattern."""
65+
url = build_export_url(self.EXPECTED_ENDPOINT, "a1", "t1")
66+
expected = (
67+
f"{self.EXPECTED_ENDPOINT}"
68+
f"{self.EXPECTED_STANDARD_PATH.format(tid='t1', aid='a1')}?api-version=1"
69+
)
70+
self.assertEqual(
71+
url,
72+
expected,
73+
"Standard export URL path changed — also review PROD_OBSERVABILITY_SCOPE "
74+
"and DEFAULT_ENDPOINT_URL. All three must stay in sync.",
75+
)
76+
77+
def test_export_url_s2s_path_structure(self):
78+
"""S2S export URL must use the pinned path pattern."""
79+
url = build_export_url(self.EXPECTED_ENDPOINT, "a1", "t1", use_s2s_endpoint=True)
80+
expected = (
81+
f"{self.EXPECTED_ENDPOINT}"
82+
f"{self.EXPECTED_S2S_PATH.format(tid='t1', aid='a1')}?api-version=1"
83+
)
84+
self.assertEqual(
85+
url,
86+
expected,
87+
"S2S export URL path changed — also review PROD_OBSERVABILITY_SCOPE "
88+
"and DEFAULT_ENDPOINT_URL. All three must stay in sync.",
89+
)
90+
91+
def test_scope_and_endpoint_are_coherent(self):
92+
"""Auth scope and endpoint must both target the agent365 service.
93+
94+
This is a coarse sanity check: if the endpoint domain changes away
95+
from 'agent365' but the scope still references the old AAD app, or
96+
vice versa, something is likely wrong.
97+
"""
98+
scopes = get_observability_authentication_scope()
99+
self.assertEqual(len(scopes), 1)
100+
scope = scopes[0]
101+
102+
# Scope should reference the Agent365 Observability permission
103+
self.assertIn("Agent365.Observability", scope)
104+
# Endpoint should be the agent365 service
105+
self.assertIn("agent365", DEFAULT_ENDPOINT_URL)
106+
107+
108+
if __name__ == "__main__":
109+
unittest.main()

0 commit comments

Comments
 (0)