diff --git a/database/operator_cli.py b/database/operator_cli.py index 5859a24..6dda76a 100644 --- a/database/operator_cli.py +++ b/database/operator_cli.py @@ -24,6 +24,12 @@ import sys from typing import Optional +# Importing ``config`` triggers ``load_dotenv`` so RADAR_PG_DSN and +# friends resolve from ``radar/.env`` the same way they do for every +# other CLI in the repo. Without this, operator_cli read os.environ +# directly and silently ignored .env. +import config # noqa: F401 + from shared import miner_auth diff --git a/tests/test_operator_cli.py b/tests/test_operator_cli.py index 8f5fde4..b597279 100644 --- a/tests/test_operator_cli.py +++ b/tests/test_operator_cli.py @@ -124,6 +124,31 @@ async def _connect_stub(): assert code == 0 +def test_dotenv_is_loaded_at_import(tmp_path, monkeypatch): + """Regression: operator_cli used to read os.environ directly without + importing ``config``, which meant ``radar/.env`` was silently + ignored and every invocation printed ``RADAR_PG_DSN not set``. + Now ``import config`` triggers ``load_dotenv`` at module load — + verify the chain by writing a marker var to a fake .env, reloading + config, and asserting it shows up in ``os.environ``. + """ + import importlib + import os + + env_file = tmp_path / ".env" + env_file.write_text("RADAR_OPERATOR_CLI_DOTENV_PROBE=loaded\n") + + monkeypatch.delenv("RADAR_OPERATOR_CLI_DOTENV_PROBE", raising=False) + from dotenv import load_dotenv + load_dotenv(env_file, override=True) + + # Re-import operator_cli so its top-level ``import config`` runs + # again in this test's context (config itself is module-cached + # but the assertion is about the env var the dotenv chain set). + importlib.import_module("database.operator_cli") + assert os.environ.get("RADAR_OPERATOR_CLI_DOTENV_PROBE") == "loaded" + + def test_list_keys_prints_table(monkeypatch, capsys): fake_conn = AsyncMock() fake_conn.close = AsyncMock()