Skip to content

Commit 4bc3bec

Browse files
link04cursoragent
andauthored
Fix flaky v8js.resource.type validation in OTLP runtime metrics test (#7120)
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 4c2258c commit 4bc3bec

1 file changed

Lines changed: 35 additions & 36 deletions

File tree

tests/test_otlp_runtime_metrics.py

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,26 @@
66
runtime.go.*, runtime.node.*, etc.).
77
"""
88

9+
from typing import TypedDict
10+
911
from utils import context, features, interfaces, scenarios, weblog
1012

1113

12-
# Maps each expected metric to its attribute constraints:
13-
# "all" — attribute keys that must appear on every emitted data point.
14-
# "some" — attribute keys that must appear on at least one data point.
15-
#
16-
# Use "some" when a metric emits both per-dimension points (which carry the attribute)
17-
# and aggregate rollup points (which don't). For example, jvm.memory.used emits one
18-
# point per memory pool (with jvm.memory.pool.name) as well as heap/non-heap totals
19-
# (without it), so pool.name goes in "some" while jvm.memory.type, which is present
20-
# on every point, goes in "all".
21-
EXPECTED_METRICS: dict[str, dict[str, dict[str, list[str]]]] = {
14+
class MetricConstraints(TypedDict, total=False):
15+
"""Attribute constraints for an expected metric.
16+
17+
all: keys required on every data point.
18+
some: keys required on at least one data point.
19+
present_values: attribute -> values that must each appear on at least one data point.
20+
"""
21+
22+
all: list[str]
23+
some: list[str]
24+
present_values: dict[str, list[str]]
25+
26+
27+
# Maps each expected metric to its attribute constraints (see MetricConstraints).
28+
EXPECTED_METRICS: dict[str, dict[str, MetricConstraints]] = {
2229
"dotnet": {
2330
"dotnet.assembly.count": {"all": []},
2431
"dotnet.exceptions": {"all": []},
@@ -72,7 +79,12 @@
7279
"v8js.memory.heap.space.physical_size": {"all": ["v8js.heap.space.name"]},
7380
"v8js.memory.heap.space.size": {"all": ["v8js.heap.space.name"]},
7481
"v8js.memory.heap.used": {"all": ["v8js.heap.space.name"]},
75-
"v8js.resource.active": {"all": ["v8js.resource.type"]},
82+
# v8js.resource.type is open-ended, so assert a known value is present instead of using a
83+
# closed allow-list: the weblog's listening HTTP server always emits TCPServerWrap.
84+
"v8js.resource.active": {
85+
"all": ["v8js.resource.type"],
86+
"present_values": {"v8js.resource.type": ["TCPServerWrap"]},
87+
},
7688
},
7789
"java": {
7890
"jvm.buffer.count": {"all": ["jvm.buffer.pool.name"]},
@@ -108,9 +120,8 @@
108120

109121
# Valid value domains for attributes. For closed enums (jvm.memory.type, jvm.thread.*,
110122
# nodejs.eventloop.state, v8js.gc.type) these are exhaustive. For open-ended attributes
111-
# (pool names, GC names, V8 heap space names, libuv resource types) these are supersets
112-
# covering all known implementations — the assertion is that observed values fall within
113-
# the known universe.
123+
# (pool names, GC names, V8 heap space names) these are supersets covering all known
124+
# implementations — the assertion is that observed values fall within the known universe.
114125
EXPECTED_METRIC_ATTRIBUTE_VALUES: dict[str, dict[str, frozenset[str]]] = {
115126
"nodejs": {
116127
# Closed enum: performance.eventLoopUtilization() exposes idle and active only.
@@ -138,28 +149,7 @@
138149
"shared_trusted_large_object_space",
139150
}
140151
),
141-
# libuv handle types reported by process.getActiveResourcesInfo(); superset across Node 18+.
142-
"v8js.resource.type": frozenset(
143-
{
144-
"Immediate",
145-
"Timeout",
146-
"TCPServerWrap",
147-
"TCPWrap",
148-
"TTYWrap",
149-
"PipeWrap",
150-
"UDPWrap",
151-
"TLSWrap",
152-
"FSReqCallback",
153-
"MessagePort",
154-
"DNSChannel",
155-
"FSEvent",
156-
"SignalWrap",
157-
"StatWatcher",
158-
"HTTPClientRequest",
159-
"HTTPParser",
160-
"Microtask",
161-
}
162-
),
152+
# v8js.resource.type is open-ended (validated via present_values in EXPECTED_METRICS, not here).
163153
},
164154
"java": {
165155
"jvm.memory.type": frozenset({"heap", "non_heap"}),
@@ -314,6 +304,15 @@ def test_otel_metrics_are_present_and_attributed(self) -> None:
314304
f"Expected one of: {sorted(attribute_values[key])}"
315305
)
316306

307+
for attr_key, required_values in constraints.get("present_values", {}).items():
308+
observed_values = {pt[attr_key] for pt in points if attr_key in pt}
309+
for required_value in required_values:
310+
assert required_value in observed_values, (
311+
f"Metric '{metric_name}' expected at least one data point with "
312+
f"{attr_key}='{required_value}' for {library}. "
313+
f"Observed {attr_key} values: {sorted(observed_values)}"
314+
)
315+
317316
def setup_dd_metrics_are_absent(self) -> None:
318317
self.req = weblog.get("/")
319318

0 commit comments

Comments
 (0)