Skip to content
Open
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
31 changes: 30 additions & 1 deletion tests/stats/test_stats.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pytest
from utils import interfaces, weblog, features, scenarios, logger
from utils import context, features, interfaces, logger, missing_feature, scenarios, weblog

"""
Test scenarios we want:
Expand Down Expand Up @@ -99,6 +99,35 @@ def test_disable(self):
requests = list(interfaces.library.get_data("/v0.6/stats"))
assert len(requests) == 0, "Client-side stats should be disabled by default"

def setup_grpc_status_code(self):
self.grpc_request = weblog.grpc("grpc stats")

@missing_feature(
context.library != "java" or context.library < "java@1.61.0",
reason="GRPCStatusCode stats payload field was added in java@1.61.0",
Comment on lines +105 to +107

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move library/version gating from decorators to manifests

This test deactivation logic is encoded in @missing_feature conditions that depend only on library version and weblog variant, but docs/edit/skip-tests.md explicitly requires those conditions to live in manifest files ("Always prefer manifest files" and "If your condition depends only on library name, library version, or weblog variant, use the manifest file instead", lines 3-13). Keeping this in decorators makes activation state harder to track and maintain (especially for automated manifest-based activation/deactivation workflows), which can leave this test silently skipped longer than intended as versions evolve.

Useful? React with 👍 / 👎.

force_skip=True,
)
@missing_feature(
context.weblog_variant != "spring-boot",
reason="gRPC stats coverage requires a weblog variant with gRPC support",
force_skip=True,
)
def test_grpc_status_code(self):
grpc_stats = []

for data in interfaces.library.get_data("/v0.6/stats"):
payload = data["request"]["content"]
for bucket in payload.get("Stats", []):
for stat in bucket.get("Stats", []):
if stat.get("Type") == "rpc" and stat.get("SpanKind") == "server":
grpc_stats.append(stat)

assert grpc_stats, "Expected at least one gRPC stats entry in the v0.6/stats payload"
# 0 means OK in gRPC status codes
assert any(stat.get("GRPCStatusCode") == "0" for stat in grpc_stats), (
f"Expected a gRPC stats entry with GRPCStatusCode=0, got: {grpc_stats}"
)


@features.client_side_stats_supported
@scenarios.trace_stats_computation
Expand Down
2 changes: 2 additions & 0 deletions utils/docker_fixtures/spec/trace.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class V06StatsAggr(TypedDict):
Type: str
Service: str
HTTPStatusCode: int
GRPCStatusCode: str
Synthetics: bool
Hits: int
TopLevelHits: int
Expand Down Expand Up @@ -131,6 +132,7 @@ def decode_v06_stats(data: bytes) -> V06StatsPayload:
Service=raw_stats["Service"],
Type=raw_stats.get("Type"),
HTTPStatusCode=raw_stats.get("HTTPStatusCode"),
GRPCStatusCode=raw_stats.get("GRPCStatusCode"),
Synthetics=raw_stats["Synthetics"],
Hits=raw_stats["Hits"],
TopLevelHits=raw_stats["TopLevelHits"],
Expand Down
Loading