From c6b63909d08e19660367380e911e3517d069b632 Mon Sep 17 00:00:00 2001 From: Robert Kaussow Date: Sun, 6 Mar 2022 13:48:40 +0100 Subject: [PATCH] chore: add unit tests for PrometheusSD class (#162) --- .drone.jsonnet | 2 +- prometheuspvesd/config.py | 2 +- prometheuspvesd/test/__init__.py | 1 + prometheuspvesd/test/data/config_error.yml | 4 + prometheuspvesd/test/data/log_error.yml | 0 prometheuspvesd/test/fixtures/__init__.py | 1 + prometheuspvesd/test/fixtures/fixtures.py | 133 ++++++++++++++++++++ prometheuspvesd/test/unit/conftest.py | 12 ++ prometheuspvesd/test/unit/test_cli.py | 68 ++++++++++ prometheuspvesd/test/unit/test_discovery.py | 2 +- pyproject.toml | 2 +- setup.cfg | 2 +- 12 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 prometheuspvesd/test/data/config_error.yml create mode 100644 prometheuspvesd/test/data/log_error.yml create mode 100644 prometheuspvesd/test/unit/test_cli.py diff --git a/.drone.jsonnet b/.drone.jsonnet index 82d9ec7..5ac19ae 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -8,7 +8,7 @@ local PythonVersion(pyversion='3.7') = { 'pip install poetry poetry-dynamic-versioning -qq', 'poetry config experimental.new-installer false', 'poetry install', - 'poetry run pytest', + 'poetry run pytest --cov-append', 'poetry version', 'poetry run prometheus-pve-sd --help', ], diff --git a/prometheuspvesd/config.py b/prometheuspvesd/config.py index 4b17387..dc78851 100644 --- a/prometheuspvesd/config.py +++ b/prometheuspvesd/config.py @@ -254,7 +254,7 @@ class Config(): except jsonschema.exceptions.ValidationError as e: schema_error = "Failed validating '{validator}' in schema{schema}\n{message}".format( validator=e.validator, - schema=format_as_index(list(e.relative_schema_path)[:-1]), + schema=format_as_index(list(e.relative_schema_path)[:-1], 0), message=e.message ) raise prometheuspvesd.exception.ConfigError("Configuration error", schema_error) diff --git a/prometheuspvesd/test/__init__.py b/prometheuspvesd/test/__init__.py index e69de29..f4a56ad 100644 --- a/prometheuspvesd/test/__init__.py +++ b/prometheuspvesd/test/__init__.py @@ -0,0 +1 @@ +"""Init pytest unit tests.""" diff --git a/prometheuspvesd/test/data/config_error.yml b/prometheuspvesd/test/data/config_error.yml new file mode 100644 index 0000000..20c113c --- /dev/null +++ b/prometheuspvesd/test/data/config_error.yml @@ -0,0 +1,4 @@ +--- +exclude_vmid: + - 100 + - "101" diff --git a/prometheuspvesd/test/data/log_error.yml b/prometheuspvesd/test/data/log_error.yml new file mode 100644 index 0000000..e69de29 diff --git a/prometheuspvesd/test/fixtures/__init__.py b/prometheuspvesd/test/fixtures/__init__.py index e69de29..c1b2ce8 100644 --- a/prometheuspvesd/test/fixtures/__init__.py +++ b/prometheuspvesd/test/fixtures/__init__.py @@ -0,0 +1 @@ +"""Init pytest fixtures.""" diff --git a/prometheuspvesd/test/fixtures/fixtures.py b/prometheuspvesd/test/fixtures/fixtures.py index cc3c5d8..89f8b5f 100644 --- a/prometheuspvesd/test/fixtures/fixtures.py +++ b/prometheuspvesd/test/fixtures/fixtures.py @@ -1,8 +1,141 @@ """Global pytest fixtures.""" +import environs import pytest +@pytest.fixture +def builtins(): + return { + "metrics.enabled": { + "default": True, + "env": "METRICS_ENABLED", + "type": environs.Env().bool + }, + "metrics.address": { + "default": "127.0.0.1", + "env": "METRICS_ADDRESS", + "type": environs.Env().str + }, + "metrics.port": { + "default": 8000, + "env": "METRICS_PORT", + "type": environs.Env().int + }, + "config_file": { + "default": "", + "env": "CONFIG_FILE", + "type": environs.Env().str + }, + "logging.level": { + "default": "WARNING", + "env": "LOG_LEVEL", + "file": True, + "type": environs.Env().str + }, + "logging.format": { + "default": "console", + "env": "LOG_FORMAT", + "file": True, + "type": environs.Env().str + }, + "output_file": { + "default": "dummy", + "env": "OUTPUT_FILE", + "file": True, + "type": environs.Env().str + }, + "loop_delay": { + "default": 300, + "env": "LOOP_DELAY", + "file": True, + "type": environs.Env().int + }, + "service": { + "default": False, + "env": "SERVICE", + "file": True, + "type": environs.Env().bool + }, + "exclude_state": { + "default": [], + "env": "EXCLUDE_STATE", + "file": True, + "type": environs.Env().list + }, + "exclude_vmid": { + "default": [], + "env": "EXCLUDE_VMID", + "file": True, + "type": environs.Env().list + }, + "exclude_tags": { + "default": [], + "env": "EXCLUDE_TAGS", + "file": True, + "type": environs.Env().list + }, + "pve.server": { + "default": "dummyserver", + "env": "PVE_SERVER", + "file": True, + "type": environs.Env().str + }, + "pve.user": { + "default": "dummyuser", + "env": "PVE_USER", + "file": True, + "type": environs.Env().str + }, + "pve.password": { + "default": "dummypass", + "env": "PVE_PASSWORD", + "file": True, + "type": environs.Env().str + }, + "pve.auth_timeout": { + "default": 5, + "env": "PVE_AUTH_TIMEOUT", + "file": True, + "type": environs.Env().int + }, + "pve.verify_ssl": { + "default": True, + "env": "PVE_VERIFY_SSL", + "file": True, + "type": environs.Env().bool + } + } + + +@pytest.fixture +def defaults(): + return { + "exclude_state": [], + "exclude_tags": [], + "exclude_vmid": [], + "logging": { + "format": "console", + "level": "WARNING" + }, + "loop_delay": 300, + "metrics": { + "address": "127.0.0.1", + "enabled": True, + "port": 8000 + }, + "output_file": "dummy", + "pve": { + "auth_timeout": 5, + "password": "dummypass", + "server": "dummyserver", + "user": "dummyuser", + "verify_ssl": True + }, + "service": True, + } + + @pytest.fixture def qemus(): return [ diff --git a/prometheuspvesd/test/unit/conftest.py b/prometheuspvesd/test/unit/conftest.py index 572fa46..e24b42a 100644 --- a/prometheuspvesd/test/unit/conftest.py +++ b/prometheuspvesd/test/unit/conftest.py @@ -1,4 +1,6 @@ """Pytest conftest fixtures.""" +import os +import sys import pytest @@ -8,3 +10,13 @@ from prometheuspvesd.utils import Singleton @pytest.fixture(autouse=True) def reset_singletons(): Singleton._instances = {} + + +@pytest.fixture(autouse=True) +def reset_os_environment(): + os.environ = {} + + +@pytest.fixture(autouse=True) +def reset_sys_argv(): + sys.argv = ["prometheus-pve-sd"] diff --git a/prometheuspvesd/test/unit/test_cli.py b/prometheuspvesd/test/unit/test_cli.py new file mode 100644 index 0000000..6e267d2 --- /dev/null +++ b/prometheuspvesd/test/unit/test_cli.py @@ -0,0 +1,68 @@ +"""Test CLI class.""" +import pytest +from proxmoxer import ProxmoxAPI + +import prometheuspvesd.exception +from prometheuspvesd.cli import PrometheusSD +from prometheuspvesd.config import Config +from prometheuspvesd.discovery import Discovery +from prometheuspvesd.exception import APIError +from prometheuspvesd.logger import Log + +pytest_plugins = [ + "prometheuspvesd.test.fixtures.fixtures", +] + + +def test_cli_required_error(mocker, capsys): + mocker.patch.object(Discovery, "_auth", return_value=mocker.create_autospec(ProxmoxAPI)) + mocker.patch.object(PrometheusSD, "_fetch", return_value=True) + + with pytest.raises(SystemExit) as e: + PrometheusSD() + + stdout, stderr = capsys.readouterr() + assert "Option 'pve.server' is required but not set" in stderr + assert e.value.code == 1 + + +def test_cli_config_error(mocker, capsys): + mocker.patch( + "prometheuspvesd.config.SingleConfig.__init__", + side_effect=prometheuspvesd.exception.ConfigError("Dummy Config Exception") + ) + mocker.patch.object(Discovery, "_auth", return_value=mocker.create_autospec(ProxmoxAPI)) + mocker.patch.object(PrometheusSD, "_fetch", return_value=True) + + with pytest.raises(SystemExit) as e: + PrometheusSD() + + stdout, stderr = capsys.readouterr() + assert "Dummy Config Exception" in stderr + assert e.value.code == 1 + + +def test_cli_log_error(mocker, capsys): + mocker.patch.object(Log, "update_logger", side_effect=ValueError("Dummy Logleve Exception")) + mocker.patch.object(Discovery, "_auth", return_value=mocker.create_autospec(ProxmoxAPI)) + mocker.patch.object(PrometheusSD, "_fetch", return_value=True) + + with pytest.raises(SystemExit) as e: + PrometheusSD() + + stdout, stderr = capsys.readouterr() + assert "Dummy Logleve Exception" in stderr + assert e.value.code == 1 + + +def test_cli_api_error(mocker, builtins, capsys): + mocker.patch.dict(Config.SETTINGS, builtins) + mocker.patch.object(Discovery, "_auth", side_effect=APIError("Dummy API Exception")) + mocker.patch.object(PrometheusSD, "_fetch", return_value=True) + + with pytest.raises(SystemExit) as e: + PrometheusSD() + + stdout, stderr = capsys.readouterr() + assert "Proxmoxer API error: Dummy API Exception" in stderr + assert e.value.code == 1 diff --git a/prometheuspvesd/test/unit/test_discovery.py b/prometheuspvesd/test/unit/test_discovery.py index 894e095..4e163ad 100644 --- a/prometheuspvesd/test/unit/test_discovery.py +++ b/prometheuspvesd/test/unit/test_discovery.py @@ -1,4 +1,4 @@ -"""Test Autostop class.""" +"""Test Discovery class.""" import pytest from proxmoxer import ProxmoxAPI diff --git a/pyproject.toml b/pyproject.toml index 2bda17c..9eb6584 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] skip_glob = ["**/.env*", "**/env/*", "**/.venv/*", "**/docs/*"] [tool.pytest.ini_options] -addopts = "prometheuspvesd --cov=prometheuspvesd --cov-report=xml:coverage.xml --cov-report=term --cov-append --no-cov-on-fail" +addopts = "prometheuspvesd --cov=prometheuspvesd --cov-report=xml:coverage.xml --cov-report=term --no-cov-on-fail" filterwarnings = [ "ignore::FutureWarning", "ignore:.*distutils.*:DeprecationWarning", diff --git a/setup.cfg b/setup.cfg index cdbb081..fc1b429 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,7 +10,7 @@ ignore = D102, D103, D105, D107, D202, W503 max-line-length = 99 inline-quotes = double -exclude = .git, __pycache__, build, dist, test, *.pyc, *.egg-info, .cache, .eggs, env* +exclude = .git, __pycache__, build, dist, *.pyc, *.egg-info, .cache, .eggs, env* [yapf] based_on_style = google