From d941f41d9daf4b06a2a6d32d130f5aa480564745 Mon Sep 17 00:00:00 2001 From: Tristan Escalada <355457+tescalada@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:07:37 -0400 Subject: [PATCH] Remove _is_ground wrapper; guard metering lifespan with config.is_cloud() _is_ground() wrapped config.is_ground() in a try/except whose fallback read SPARKMETER_MODE, an env var that no deployment sets. The config already determines ground vs cloud (via the HEROKU key), so metering_lifespan now guards its cloud no-op directly with config.is_cloud(), and _is_ground is deleted. --- sparkmeter/metering/lifespan.py | 14 +++----------- sparkmeter/metering/tests/test_lifespan.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 11 deletions(-) create mode 100644 sparkmeter/metering/tests/test_lifespan.py diff --git a/sparkmeter/metering/lifespan.py b/sparkmeter/metering/lifespan.py index f6d6c19..45e6cdd 100644 --- a/sparkmeter/metering/lifespan.py +++ b/sparkmeter/metering/lifespan.py @@ -24,20 +24,12 @@ logger = logging.getLogger(__name__) -def _is_ground() -> bool: - """Whether this deployment should drive a meter network.""" - try: - from sparkmeter.config.configdict import config - - return config.is_ground() - except Exception: # noqa: BLE001 - return os.environ.get("SPARKMETER_MODE", "ground") == "ground" - - @asynccontextmanager async def metering_lifespan(app: FastAPI) -> AsyncIterator[None]: """Owns the metering-provider connection on ground; no-op on cloud.""" - if not _is_ground(): + from sparkmeter.config.configdict import config + + if config.is_cloud(): app.state.metering = None yield return diff --git a/sparkmeter/metering/tests/test_lifespan.py b/sparkmeter/metering/tests/test_lifespan.py new file mode 100644 index 0000000..b4b9955 --- /dev/null +++ b/sparkmeter/metering/tests/test_lifespan.py @@ -0,0 +1,22 @@ +"""Tests for the metering FastAPI lifespan.""" + +from types import SimpleNamespace + +import pytest + +from sparkmeter.metering import lifespan + + +@pytest.mark.asyncio +async def test_metering_lifespan_is_noop_on_cloud(monkeypatch): + """On cloud, the lifespan sets app.state.metering to None and starts nothing.""" + monkeypatch.setattr("sparkmeter.config.configdict.config.is_cloud", lambda: True) + + app = SimpleNamespace(state=SimpleNamespace()) + + async with lifespan.metering_lifespan(app): + assert app.state.metering is None + + # No metering runtime was started: only the no-op no-metering state exists. + assert app.state.metering is None + assert not hasattr(app.state, "metering_command_queue")