Files
FabledSteward/tests/plugins/traefik/test_status_codes.py
T
bvandeusenandClaude Opus 5.5 86087982b0
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 1m15s
CI / unit (push) Successful in 1m32s
CI / publish (push) Successful in 1m6s
test(traefik): drop the unused datetime import (#5711)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:09:16 -04:00

78 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Each router's 4xx and 5xx responses by code (#5711): counted from
traefik_service_requests_total{code} between scrapes, and shown on the
problem card so it can say whether it's scanners, a login wall or the app."""
from plugins.traefik.routes import router_state
from plugins.traefik.scraper import compute_router_metrics, compute_status_codes
from steward.core.settings import to_thresholds_cfg
from tests._templates import render_plugin_template
from tests.plugins.traefik.test_traefik_page import _latest
REQ = "traefik_service_requests_total"
def _req(*series):
"""{REQ: {labels: value}} from (service, code, method, value) tuples."""
return {REQ: {frozenset({"service": s, "code": c, "method": m}.items()): v for s, c, m, v in series}}
def test_the_codes_since_the_last_scrape_are_counted_per_router():
before = _req(("app@docker", "200", "GET", 1000), ("app@docker", "404", "GET", 100),
("app@docker", "404", "POST", 10), ("api@docker", "401", "GET", 5))
after = _req(("app@docker", "200", "GET", 1500), ("app@docker", "404", "GET", 160),
("app@docker", "404", "POST", 12), ("api@docker", "401", "GET", 5),
("app@docker", "502", "GET", 3)) # a series the last scrape didn't have
got = compute_status_codes(after, before)
# 2xx is left out; 404 sums over methods; a new 502 series counts whole; api's 401 didn't move.
assert got == {"app@docker": {"404": 62, "502": 3}}
def test_the_first_scrape_counts_nothing_and_a_reset_counts_nothing():
series = _req(("app@docker", "500", "GET", 40))
assert compute_status_codes(series, None) == {}
assert compute_status_codes(_req(("app@docker", "500", "GET", 2)), series) == {} # Traefik restarted
def test_a_router_first_502s_count_in_its_5xx_share():
"""A series new since the last scrape started from 0: before #5711 its
whole first burst was dropped, so a router's first 502s read as 0%."""
before = _req(("app@docker", "200", "GET", 100))
after = _req(("app@docker", "200", "GET", 190), ("app@docker", "502", "GET", 10))
got = compute_router_metrics(after, before, 60.0)["app@docker"]
assert got["error_rate_5xx_pct"] == 10.0 and abs(got["request_rate"] - 100 / 60) < 1e-9
TH = to_thresholds_cfg({})
def _problem(**codes_ctx):
latest = _latest(e4=6.0)
state, why = router_state(latest, TH)
return {"name": "app", "latest": latest, "state": state, "why": why, **codes_ctx,
**{f"sparkline_{k}": "" for k in ("req", "bytes", "p95", "4xx", "5xx")}}
async def _card(app, router, codes_known=True):
async with app.test_request_context("/plugins/traefik/rows"):
page = await render_plugin_template(
app, "traefik", "traefik/rows.html", problems=[router], healthy=[], gone=[], global_stat=None,
certs=[], old_certs=[], range_key="24h", current_range="24h", error_4xx=1.0, error_5xx=0.1,
codes_known=codes_known)
page = " ".join(page.split())
return page[page.index(">app</h3>"):]
async def test_the_card_lists_the_codes_most_first_before_its_figures(app):
codes = [("404", 1812), ("502", 40), ("401", 9), ("403", 3), ("429", 2), ("499", 1), ("500", 1)]
card = await _card(app, _problem(codes=codes))
assert card.index('Last 15 min: <span class="tone-warn">404</span> ×1,812, <span class="tone-crit">502</span> ×40') \
< card.index("<table")
assert "429</span> ×2, 2 more codes" in card and "499" not in card
async def test_a_card_without_codes_says_whether_they_were_recorded(app):
none = await _card(app, _problem(codes=[]), codes_known=True)
assert "No 4xx or 5xx responses in the last 15 min." in none
early = await _card(app, _problem(codes=[]), codes_known=False)
assert "Status codes not recorded yet" in early