Skip to content

Unit tests for osism/settings.py #2197

@berendt

Description

@berendt

Background

Follow-up to #2192 (foundation) and PR #2193 (pytest + Zuul infrastructure). osism/settings.py reads configuration from environment variables and from Docker secrets in /run/secrets/. Defaults are important here because they determine behavior when an operator forgets to set a variable — so they deserve a regression test.

Scope

Add tests/unit/test_settings.py covering the read_secret helper and the module-level settings in osism/settings.py.

Test targets

read_secret(secret_name)osism/settings.py:6

  • Existing file under /run/secrets/<name> with content \"abc\\n\" → returns \"abc\" (trailing newline stripped)
  • File does not exist → returns \"\" (not None)
  • File is empty → returns \"\"
  • File has leading/trailing whitespace on the first line → only trailing is stripped via .strip() (verify exact behavior matches implementation)

Use tmp_path and monkeypatch to redirect the /run/secrets/ path — the cleanest approach is monkeypatch.setattr(\"builtins.open\", ...) or refactor-free: pass a tmp_path-based path via monkeypatch.setattr of a helper. If that proves intrusive, it is acceptable to test by actually creating /run/secrets/ is not acceptable — use mocking instead.

Module-level settings

Because settings are read at import time, tests need to re-import osism.settings under controlled environments. The pattern:

import importlib
import osism.settings as settings_module

def test_default_redis_host(monkeypatch):
    monkeypatch.delenv(\"REDIS_HOST\", raising=False)
    importlib.reload(settings_module)
    assert settings_module.REDIS_HOST == \"redis\"

Required cases (pick representatives, not every single var):

Variable Default Override test
REDIS_HOST \"redis\" REDIS_HOST=mycache\"mycache\"
REDIS_PORT 6379 (int) REDIS_PORT=12341234 (int)
REDIS_DB 0 (int) REDIS_DB=55
IGNORE_SSL_ERRORS True IGNORE_SSL_ERRORS=FalseFalse; any other string → False
GATHER_FACTS_SCHEDULE 43200.0 (float) GATHER_FACTS_SCHEDULE=6060.0
FACTS_MAX_AGE equals int of GATHER_FACTS_SCHEDULE when unset explicit override honored
INVENTORY_RECONCILER_SCHEDULE 600.0 override honored
OPERATOR_USER \"dragon\" OSISM_OPERATOR_USER=admin\"admin\"
FRR_DUMMY_INTERFACE \"loopback0\" OSISM_FRR_DUMMY_INTERFACE=lo1\"lo1\"
NETBOX_URL None NETBOX_API beats NETBOX_URL when both set (verify precedence in os.getenv(\"NETBOX_API\", os.getenv(\"NETBOX_URL\")))
NETBOX_TOKEN \"\" env var wins over secret file; falls back to read_secret when env is unset; .strip() applied
SONIC_EXPORT_DIR / SONIC_EXPORT_PREFIX / SONIC_EXPORT_SUFFIX / SONIC_EXPORT_IDENTIFIER string defaults override honored
NETBOX_SECONDARIES \"[]\" env wins; read_secret fallback; literal default when both absent
REDFISH_TIMEOUT 20 (int) override honored
NETBOX_MAX_CONNECTIONS 5 (int) override honored

NETBOX_FILTER_CONDUCTOR_IRONIC / NETBOX_FILTER_CONDUCTOR_SONIC

  • Defaults are the literal strings documented in settings.py:44-51
  • Override via env is honored

Mocking hints

  • Use monkeypatch.setenv / monkeypatch.delenv and importlib.reload(osism.settings) per test. Restore the module at the end of the test module via an autouse fixture so later tests see the original state.
  • For read_secret, mock the open(...) call or monkeypatch a path constant if you extract one; do not write to /run/secrets/ on the host.

Definition of Done

  • tests/unit/test_settings.py created
  • All listed settings have at least a default-value test; overrides are covered for one representative per type (str/int/float/bool)
  • read_secret has tests for happy path, missing file, and whitespace handling
  • pipenv run pytest tests/unit/test_settings.py passes locally
  • Tests do not leak env vars to subsequent tests (verify with pipenv run pytest on the whole suite)
  • flake8, mypy, python-black remain green
  • Zuul job python-osism-unit-tests passes

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions