Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/common/test_tools/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,17 @@ def assert_metric_impl() -> Generator[AssertMetricFixture, None, None]:
registry = prometheus_client.REGISTRY
collectors = [*registry._collector_to_names]

# Reset registry state
# Reset registry state. `MetricWrapperBase.clear()` is implemented for
# parent metrics (those declared with labels) — calling it on an
# unlabeled metric raises AttributeError because `_lock` is only set on
# parents. Reset unlabeled metrics' observable state via `_metric_init`.
for collector in collectors:
if isinstance(collector, prometheus_client.metrics.MetricWrapperBase):
if not isinstance(collector, prometheus_client.metrics.MetricWrapperBase):
continue
if collector._is_parent(): # type: ignore[no-untyped-call]
collector.clear()
else:
collector._metric_init() # type: ignore[no-untyped-call]

def _assert_metric(
*,
Expand Down
8 changes: 8 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,11 @@ def test_metric() -> prometheus_client.Counter:
"Total number of tests run by pytest.",
["test_name"],
)


@pytest.fixture(scope="session")
def test_unlabeled_metric() -> prometheus_client.Counter:
return prometheus_client.Counter(
"pytest_unlabeled_total",
"Total number of pytest unlabeled-metric assertions.",
)
16 changes: 16 additions & 0 deletions tests/unit/common/test_tools/test_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ def test_assert_metrics__metric_incremented__asserts_expected(
)


def test_assert_metrics__unlabeled_metric_incremented__asserts_expected(
assert_metric: AssertMetricFixture,
test_unlabeled_metric: prometheus_client.Counter,
) -> None:
# Given an unlabeled counter incremented during the test (the fixture
# has already reset the registry). The unlabeled-metric reset previously
# raised AttributeError because `MetricWrapperBase.clear()` only works
# on labeled parents; the fixture now uses `_metric_init` for unlabeled.

# When the counter is incremented
test_unlabeled_metric.inc()

# Then the assertion sees a fresh count of 1
assert_metric(name="pytest_unlabeled_total", labels={}, value=1)


def test_assert_metrics__after_registry_reset__raises_assertion(
test_metric: prometheus_client.Counter,
) -> None:
Expand Down