headroom/tests/test_proxy_telemetry_env.py
wstczyw b514695efd
test(proxy): cover enabled periodic TOIN stats startup (#1268)
## Description

Follow-up to #1265. Add coverage for the enabled branch of
`periodic_toin_stats_enabled` during proxy lifespan startup.

The original PR added the opt-out and disabled-path coverage. This test
covers the default/enabled path so the new lifespan guard is not left
partially covered.

Closes #

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Added `test_lifespan_schedules_periodic_toin_stats_when_enabled`.
- The test patches `_log_toin_stats_periodically` with a short noop
coroutine and verifies the proxy lifespan requests it when
`periodic_toin_stats_enabled=True`.
- This complements the existing disabled-path test from #1265.

## Testing

- [x] Unit tests pass (`pytest`)
- [ ] Linting passes (`ruff check .`)
- [ ] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
$ uv run pytest tests/test_proxy_telemetry_env.py -q
============================= test session starts =============================
platform win32 -- Python 3.11.15, pytest-9.0.3, pluggy-1.6.0
rootdir: C:\Users\wstcz\AppData\Local\Temp\headroom-main-20260622-073524
configfile: pyproject.toml
plugins: anyio-4.12.1, langsmith-0.8.0, asyncio-1.3.0, cov-7.0.0
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=None, asyncio_default_test_loop_scope=function
collected 10 items

tests\test_proxy_telemetry_env.py ..........                             [100%]

============================== warnings summary ===============================
.venv\Lib\site-packages\fastapi\testclient.py:1
  C:\Users\wstcz\AppData\Local\Temp\headroom-main-20260622-073524\.venv\Lib\site-packages\fastapi\testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead.
    from starlette.testclient import TestClient as TestClient  # noqa

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
======================== 10 passed, 1 warning in 2.02s ========================

$ git diff --check origin/main..HEAD
# no output; command exited 0
```

## Real Behavior Proof

- Environment: Windows, Python 3.11.15 uv-managed `.venv`, branch based
on current `origin/main`.
- Exact command / steps: ran `uv run pytest
tests/test_proxy_telemetry_env.py -q`.
- Observed result: all 10 tests in `tests/test_proxy_telemetry_env.py`
passed, including the enabled periodic TOIN stats lifespan branch.
- Not tested: full repository pytest, ruff, and mypy were not run for
this test-only follow-up.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have updated the CHANGELOG.md if applicable

## Screenshots (if applicable)

N/A.

## Additional Notes

- Test-only follow-up to #1265.
- No production behavior changes.
- The focused pytest run still emits the existing Starlette/FastAPI
TestClient deprecation warning from dependencies, so `My changes
generate no new warnings` is intentionally left unchecked.
2026-06-22 18:57:38 -05:00

150 lines
4.8 KiB
Python

"""Tests for proxy telemetry environment variable handling."""
import asyncio
from unittest.mock import patch
import pytest
pytest.importorskip("fastapi")
from fastapi.testclient import TestClient
from headroom.proxy.server import ProxyConfig, _proxy_config_from_env, create_app
class TestProxyTelemetrySDKEnv:
"""Test HEADROOM_SDK handling when the proxy builds telemetry beacons."""
def test_proxy_telemetry_sdk_defaults_to_proxy(self, monkeypatch):
"""Telemetry beacon uses the default SDK label when env var is unset."""
monkeypatch.delenv("HEADROOM_SDK", raising=False)
with patch("headroom.telemetry.beacon.TelemetryBeacon") as mock_beacon:
create_app(
ProxyConfig(
cache_enabled=False,
rate_limit_enabled=False,
cost_tracking_enabled=False,
)
)
assert mock_beacon.call_args.kwargs["sdk"] == "proxy"
def test_proxy_telemetry_sdk_uses_env_override(self, monkeypatch):
"""Telemetry beacon uses HEADROOM_SDK when it is non-empty."""
monkeypatch.setenv("HEADROOM_SDK", "headroom-app")
with patch("headroom.telemetry.beacon.TelemetryBeacon") as mock_beacon:
create_app(
ProxyConfig(
cache_enabled=False,
rate_limit_enabled=False,
cost_tracking_enabled=False,
)
)
assert mock_beacon.call_args.kwargs["sdk"] == "headroom-app"
def test_proxy_telemetry_sdk_empty_env_falls_back_to_proxy(self, monkeypatch):
"""Telemetry beacon falls back to proxy when HEADROOM_SDK is blank."""
monkeypatch.setenv("HEADROOM_SDK", " ")
with patch("headroom.telemetry.beacon.TelemetryBeacon") as mock_beacon:
create_app(
ProxyConfig(
cache_enabled=False,
rate_limit_enabled=False,
cost_tracking_enabled=False,
)
)
assert mock_beacon.call_args.kwargs["sdk"] == "proxy"
class TestProxyPeriodicTOINStatsEnv:
"""Test HEADROOM_PERIODIC_TOIN_STATS handling for long-lived proxy workers."""
def test_periodic_toin_stats_enabled_by_default(self, monkeypatch):
"""Periodic TOIN stats logging remains enabled unless explicitly disabled."""
monkeypatch.delenv("HEADROOM_PERIODIC_TOIN_STATS", raising=False)
config = _proxy_config_from_env()
assert config.periodic_toin_stats_enabled is True
@pytest.mark.parametrize("value", ["0", "false", "off", "no"])
def test_periodic_toin_stats_can_be_disabled_by_env(self, monkeypatch, value):
"""HEADROOM_PERIODIC_TOIN_STATS=0/false/off/no disables periodic logging."""
monkeypatch.setenv("HEADROOM_PERIODIC_TOIN_STATS", value)
config = _proxy_config_from_env()
assert config.periodic_toin_stats_enabled is False
def test_lifespan_skips_periodic_toin_stats_when_disabled(self, monkeypatch):
"""Disabling periodic TOIN stats avoids scheduling the stats loop."""
monkeypatch.setenv("HEADROOM_SKIP_UPSTREAM_CHECK", "1")
requested = False
def fake_periodic_toin_stats():
nonlocal requested
requested = True
async def noop():
await asyncio.sleep(0)
return noop()
monkeypatch.setattr(
"headroom.proxy.server._log_toin_stats_periodically",
fake_periodic_toin_stats,
)
app = create_app(
ProxyConfig(
optimize=False,
cache_enabled=False,
rate_limit_enabled=False,
cost_tracking_enabled=False,
periodic_toin_stats_enabled=False,
)
)
with TestClient(app):
pass
assert requested is False
def test_lifespan_schedules_periodic_toin_stats_when_enabled(self, monkeypatch):
"""Enabled periodic TOIN stats schedules the stats loop at startup."""
monkeypatch.setenv("HEADROOM_SKIP_UPSTREAM_CHECK", "1")
requested = False
def fake_periodic_toin_stats():
nonlocal requested
requested = True
async def noop():
await asyncio.sleep(0)
return noop()
monkeypatch.setattr(
"headroom.proxy.server._log_toin_stats_periodically",
fake_periodic_toin_stats,
)
app = create_app(
ProxyConfig(
optimize=False,
cache_enabled=False,
rate_limit_enabled=False,
cost_tracking_enabled=False,
periodic_toin_stats_enabled=True,
)
)
with TestClient(app):
pass
assert requested is True