Use fake HTTP server for the Patroni API in tests

We introduce a patroni_api fixture, defined in tests/conftest.py, which
sets up an HTTP server serving files in a temporary directory. The
server is itself defined by the PatroniAPI class; it has a 'routes()'
context manager method to be used in actual tests to setup expected
responses based on specified JSON files.

We set up some logging in order to improve debugging.

The direct advantage of this is that PatroniResource.rest_api() method
is now covered by the test suite.

Coverage before this commit:

  Name                        Stmts   Miss  Cover
  -----------------------------------------------
  check_patroni/__init__.py       3      0   100%
  check_patroni/cli.py          193     18    91%
  check_patroni/cluster.py      113      0   100%
  check_patroni/convert.py       23      5    78%
  check_patroni/node.py         146      1    99%
  check_patroni/types.py         50     23    54%
  -----------------------------------------------
  TOTAL                         528     47    91%

and after this commit:

  Name                        Stmts   Miss  Cover
  -----------------------------------------------
  check_patroni/__init__.py       3      0   100%
  check_patroni/cli.py          193     18    91%
  check_patroni/cluster.py      113      0   100%
  check_patroni/convert.py       23      5    78%
  check_patroni/node.py         146      1    99%
  check_patroni/types.py         50      9    82%
  -----------------------------------------------
  TOTAL                         528     33    94%

In actual test functions, we either invoke patroni_api.routes() to
configure which JSON file(s) should be served for each endpoint, or we
define dedicated fixtures (e.g. cluster_config_has_changed()) to
configure this for several test functions or the whole module.

The 'old_replica_state' parametrized fixture is used when needed to
adjust such fixtures, e.g. in cluster_has_replica_ok(), to modify the
JSON content using cluster_api_set_replica_running() (previously in
tests/tools.py, now in tests/__init__.py).

The dependency on pytest-mock is no longer needed.
This commit is contained in:
Denis Laxalde 2023-09-28 09:41:33 +02:00
parent 32e06f7051
commit 903b83e211
21 changed files with 470 additions and 267 deletions

View file

@ -8,6 +8,9 @@
### Misc ### Misc
* Improve test coverage by running an HTTP server to fake the Patroni API (#55
by @dlax).
## check_patroni 1.0.0 - 2023-08-28 ## check_patroni 1.0.0 - 2023-08-28
Check patroni is now tagged as Production/Stable. Check patroni is now tagged as Production/Stable.

View file

@ -48,10 +48,9 @@ The `README.md` can be generated with `./docs/make_readme.sh`.
## Executing Tests ## Executing Tests
Crafting repeatable tests using a live Patroni cluster can be intricate. To Crafting repeatable tests using a live Patroni cluster can be intricate. To
simplify the development process, interactions with Patroni's API are simplify the development process, a fake HTTP server is set up as a test
substituted with a mock function that yields an HTTP return code and a JSON fixture and serves static files (either from `tests/json` directory or from
object outlining the cluster's status. The JSON files containing this in-memory data).
information are housed in the `./tests/json` directory.
An important consideration is that there is a potential drawback: if the JSON An important consideration is that there is a potential drawback: if the JSON
data is incorrect or if modifications have been made to Patroni without data is incorrect or if modifications have been made to Patroni without

View file

@ -5,7 +5,6 @@ flake8
mypy==0.961 mypy==0.961
pytest pytest
pytest-cov pytest-cov
pytest-mock
types-requests types-requests
setuptools setuptools
tox tox

View file

@ -46,7 +46,6 @@ setup(
extras_require={ extras_require={
"test": [ "test": [
"pytest", "pytest",
"pytest-mock",
], ],
}, },
entry_points={ entry_points={

View file

@ -0,0 +1,64 @@
import json
import logging
import shutil
from contextlib import contextmanager
from functools import partial
from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from typing import Any, Iterator, Mapping, Union
logger = logging.getLogger(__name__)
class PatroniAPI(HTTPServer):
def __init__(self, directory: Path, *, datadir: Path) -> None:
self.directory = directory
self.datadir = datadir
handler_cls = partial(SimpleHTTPRequestHandler, directory=str(directory))
super().__init__(("", 0), handler_cls)
def serve_forever(self, *args: Any) -> None:
logger.info(
"starting fake Patroni API at %s (directory=%s)",
self.endpoint,
self.directory,
)
return super().serve_forever(*args)
@property
def endpoint(self) -> str:
return f"http://{self.server_name}:{self.server_port}"
@contextmanager
def routes(self, mapping: Mapping[str, Union[Path, str]]) -> Iterator[None]:
"""Temporarily install specified files in served directory, thus
building "routes" from given mapping.
The 'mapping' defines target route paths as keys and files to be
installed in served directory as values. Mapping values of type 'str'
are assumed be relative file path to the 'datadir'.
"""
for route_path, fpath in mapping.items():
if isinstance(fpath, str):
fpath = self.datadir / fpath
shutil.copy(fpath, self.directory / route_path)
try:
yield None
finally:
for fname in mapping:
(self.directory / fname).unlink()
def cluster_api_set_replica_running(in_json: Path, target_dir: Path) -> Path:
# starting from 3.0.4 the state of replicas is streaming instead of running
with in_json.open() as f:
js = json.load(f)
for node in js["members"]:
if node["role"] in ["replica", "sync_standby"]:
if node["state"] == "streaming":
node["state"] = "running"
assert target_dir.is_dir()
out_json = target_dir / in_json.name
with out_json.open("w") as f:
json.dump(js, f)
return out_json

View file

@ -1,11 +1,11 @@
from functools import partial from pathlib import Path
from typing import Any, Callable from threading import Thread
from typing import Any, Iterator
import pytest import pytest
from click.testing import CliRunner from click.testing import CliRunner
from pytest_mock import MockerFixture
from .tools import my_mock from . import PatroniAPI
@pytest.fixture( @pytest.fixture(
@ -16,9 +16,24 @@ def old_replica_state(request: Any) -> Any:
return request.param return request.param
@pytest.fixture @pytest.fixture(scope="session")
def fake_restapi(mocker: MockerFixture) -> Callable[..., Any]: def datadir() -> Path:
return partial(my_mock, mocker) return Path(__file__).parent / "json"
@pytest.fixture(scope="session")
def patroni_api(
tmp_path_factory: pytest.TempPathFactory, datadir: Path
) -> Iterator[PatroniAPI]:
"""A fake HTTP server for the Patroni API serving files from a temporary
directory.
"""
httpd = PatroniAPI(tmp_path_factory.mktemp("api"), datadir=datadir)
t = Thread(target=httpd.serve_forever)
t.start()
yield httpd
httpd.shutdown()
t.join()
@pytest.fixture @pytest.fixture

View file

@ -2,18 +2,19 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_api_status_code_200(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_is_pending_restart_ok") def test_api_status_code_200(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke( with patroni_api.routes({"patroni": "node_is_pending_restart_ok.json"}):
main, ["-e", "https://10.20.199.3:8008", "node_is_pending_restart"] result = runner.invoke(
) main, ["-e", patroni_api.endpoint, "node_is_pending_restart"]
)
assert result.exit_code == 0 assert result.exit_code == 0
def test_api_status_code_404(runner: CliRunner, fake_restapi) -> None: def test_api_status_code_404(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("Fake test", status=404)
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_pending_restart"] main, ["-e", patroni_api.endpoint, "node_is_pending_restart"]
) )
assert result.exit_code == 3 assert result.exit_code == 3

View file

@ -1,20 +1,29 @@
from pathlib import Path from pathlib import Path
from typing import Iterator
import nagiosplugin import nagiosplugin
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
@pytest.fixture(scope="module", autouse=True)
def cluster_config_has_changed(patroni_api: PatroniAPI) -> Iterator[None]:
with patroni_api.routes({"config": "cluster_config_has_changed.json"}):
yield None
def test_cluster_config_has_changed_ok_with_hash( def test_cluster_config_has_changed_ok_with_hash(
runner: CliRunner, fake_restapi runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_config_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--hash", "--hash",
"96b12d82571473d13e890b893734e731", "96b12d82571473d13e890b893734e731",
@ -28,18 +37,17 @@ def test_cluster_config_has_changed_ok_with_hash(
def test_cluster_config_has_changed_ok_with_state_file( def test_cluster_config_has_changed_ok_with_state_file(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
state_file = tmp_path / "cluster_config_has_changed.state_file" state_file = tmp_path / "cluster_config_has_changed.state_file"
with state_file.open("w") as f: with state_file.open("w") as f:
f.write('{"hash": "96b12d82571473d13e890b893734e731"}') f.write('{"hash": "96b12d82571473d13e890b893734e731"}')
fake_restapi("cluster_config_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -53,14 +61,13 @@ def test_cluster_config_has_changed_ok_with_state_file(
def test_cluster_config_has_changed_ko_with_hash( def test_cluster_config_has_changed_ko_with_hash(
runner: CliRunner, fake_restapi runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_config_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--hash", "--hash",
"96b12d82571473d13e890b8937ffffff", "96b12d82571473d13e890b8937ffffff",
@ -74,19 +81,18 @@ def test_cluster_config_has_changed_ko_with_hash(
def test_cluster_config_has_changed_ko_with_state_file_and_save( def test_cluster_config_has_changed_ko_with_state_file_and_save(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
state_file = tmp_path / "cluster_config_has_changed.state_file" state_file = tmp_path / "cluster_config_has_changed.state_file"
with state_file.open("w") as f: with state_file.open("w") as f:
f.write('{"hash": "96b12d82571473d13e890b8937ffffff"}') f.write('{"hash": "96b12d82571473d13e890b8937ffffff"}')
fake_restapi("cluster_config_has_changed")
# test without saving the new hash # test without saving the new hash
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -111,7 +117,7 @@ def test_cluster_config_has_changed_ko_with_state_file_and_save(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -133,16 +139,15 @@ def test_cluster_config_has_changed_ko_with_state_file_and_save(
def test_cluster_config_has_changed_params( def test_cluster_config_has_changed_params(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
# This one is placed last because it seems like the exceptions are not flushed from stderr for the next tests. # This one is placed last because it seems like the exceptions are not flushed from stderr for the next tests.
fake_state_file = tmp_path / "fake_file_name.state_file" fake_state_file = tmp_path / "fake_file_name.state_file"
fake_restapi("cluster_config_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_config_has_changed", "cluster_config_has_changed",
"--hash", "--hash",
"640df9f0211c791723f18fc3ed9dbb95", "640df9f0211c791723f18fc3ed9dbb95",

View file

@ -2,12 +2,12 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_cluster_has_leader_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("cluster_has_leader_ok") def test_cluster_has_leader_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke( with patroni_api.routes({"cluster": "cluster_has_leader_ok.json"}):
main, ["-e", "https://10.20.199.3:8008", "cluster_has_leader"] result = runner.invoke(main, ["-e", patroni_api.endpoint, "cluster_has_leader"])
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -15,11 +15,11 @@ def test_cluster_has_leader_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_cluster_has_leader_ok_standby_leader(runner: CliRunner, fake_restapi) -> None: def test_cluster_has_leader_ok_standby_leader(
fake_restapi("cluster_has_leader_ok_standby_leader") runner: CliRunner, patroni_api: PatroniAPI
result = runner.invoke( ) -> None:
main, ["-e", "https://10.20.199.3:8008", "cluster_has_leader"] with patroni_api.routes({"cluster": "cluster_has_leader_ok_standby_leader.json"}):
) result = runner.invoke(main, ["-e", patroni_api.endpoint, "cluster_has_leader"])
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -27,11 +27,9 @@ def test_cluster_has_leader_ok_standby_leader(runner: CliRunner, fake_restapi) -
) )
def test_cluster_has_leader_ko(runner: CliRunner, fake_restapi) -> None: def test_cluster_has_leader_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("cluster_has_leader_ko") with patroni_api.routes({"cluster": "cluster_has_leader_ko.json"}):
result = runner.invoke( result = runner.invoke(main, ["-e", patroni_api.endpoint, "cluster_has_leader"])
main, ["-e", "https://10.20.199.3:8008", "cluster_has_leader"]
)
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout

View file

@ -1,16 +1,29 @@
from pathlib import Path
from typing import Iterator, Union
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI, cluster_api_set_replica_running
@pytest.fixture
def cluster_has_replica_ok(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_has_replica_ok.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
# TODO Lag threshold tests # TODO Lag threshold tests
def test_cluster_has_relica_ok( @pytest.mark.usefixtures("cluster_has_replica_ok")
runner: CliRunner, fake_restapi, old_replica_state: bool def test_cluster_has_relica_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
) -> None: result = runner.invoke(main, ["-e", patroni_api.endpoint, "cluster_has_replica"])
fake_restapi("cluster_has_replica_ok", use_old_replica_state=old_replica_state)
result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "cluster_has_replica"]
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -18,15 +31,15 @@ def test_cluster_has_relica_ok(
) )
@pytest.mark.usefixtures("cluster_has_replica_ok")
def test_cluster_has_replica_ok_with_count_thresholds( def test_cluster_has_replica_ok_with_count_thresholds(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ok", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--warning", "--warning",
"@1", "@1",
@ -41,15 +54,15 @@ def test_cluster_has_replica_ok_with_count_thresholds(
) )
@pytest.mark.usefixtures("cluster_has_replica_ok")
def test_cluster_has_replica_ok_with_sync_count_thresholds( def test_cluster_has_replica_ok_with_sync_count_thresholds(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ok", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--sync-warning", "--sync-warning",
"1:", "1:",
@ -62,15 +75,26 @@ def test_cluster_has_replica_ok_with_sync_count_thresholds(
) )
@pytest.fixture
def cluster_has_replica_ok_lag(
patroni_api: PatroniAPI, datadir: Path, tmp_path: Path, old_replica_state: bool
) -> Iterator[None]:
path: Union[str, Path] = "cluster_has_replica_ok_lag.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_has_replica_ok_lag")
def test_cluster_has_replica_ok_with_count_thresholds_lag( def test_cluster_has_replica_ok_with_count_thresholds_lag(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ok_lag", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--warning", "--warning",
"@1", "@1",
@ -87,15 +111,26 @@ def test_cluster_has_replica_ok_with_count_thresholds_lag(
) )
@pytest.fixture
def cluster_has_replica_ko(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_has_replica_ko.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_has_replica_ko")
def test_cluster_has_replica_ko_with_count_thresholds( def test_cluster_has_replica_ko_with_count_thresholds(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ko", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--warning", "--warning",
"@1", "@1",
@ -110,15 +145,15 @@ def test_cluster_has_replica_ko_with_count_thresholds(
) )
@pytest.mark.usefixtures("cluster_has_replica_ko")
def test_cluster_has_replica_ko_with_sync_count_thresholds( def test_cluster_has_replica_ko_with_sync_count_thresholds(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ko", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--sync-warning", "--sync-warning",
"2:", "2:",
@ -133,15 +168,26 @@ def test_cluster_has_replica_ko_with_sync_count_thresholds(
) )
@pytest.fixture
def cluster_has_replica_ko_lag(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_has_replica_ko_lag.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_has_replica_ko_lag")
def test_cluster_has_replica_ko_with_count_thresholds_and_lag( def test_cluster_has_replica_ko_with_count_thresholds_and_lag(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_replica_ko_lag", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_has_replica", "cluster_has_replica",
"--warning", "--warning",
"@1", "@1",

View file

@ -2,12 +2,16 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_cluster_has_scheduled_action_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("cluster_has_scheduled_action_ok") def test_cluster_has_scheduled_action_ok(
result = runner.invoke( runner: CliRunner, patroni_api: PatroniAPI
main, ["-e", "https://10.20.199.3:8008", "cluster_has_scheduled_action"] ) -> None:
) with patroni_api.routes({"cluster": "cluster_has_scheduled_action_ok.json"}):
result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_has_scheduled_action"]
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -16,12 +20,14 @@ def test_cluster_has_scheduled_action_ok(runner: CliRunner, fake_restapi) -> Non
def test_cluster_has_scheduled_action_ko_switchover( def test_cluster_has_scheduled_action_ko_switchover(
runner: CliRunner, fake_restapi runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_scheduled_action_ko_switchover") with patroni_api.routes(
result = runner.invoke( {"cluster": "cluster_has_scheduled_action_ko_switchover.json"}
main, ["-e", "https://10.20.199.3:8008", "cluster_has_scheduled_action"] ):
) result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_has_scheduled_action"]
)
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout
@ -30,12 +36,14 @@ def test_cluster_has_scheduled_action_ko_switchover(
def test_cluster_has_scheduled_action_ko_restart( def test_cluster_has_scheduled_action_ko_restart(
runner: CliRunner, fake_restapi runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_has_scheduled_action_ko_restart") with patroni_api.routes(
result = runner.invoke( {"cluster": "cluster_has_scheduled_action_ko_restart.json"}
main, ["-e", "https://10.20.199.3:8008", "cluster_has_scheduled_action"] ):
) result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_has_scheduled_action"]
)
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout

View file

@ -2,12 +2,16 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_cluster_is_in_maintenance_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("cluster_is_in_maintenance_ok") def test_cluster_is_in_maintenance_ok(
result = runner.invoke( runner: CliRunner, patroni_api: PatroniAPI
main, ["-e", "https://10.20.199.3:8008", "cluster_is_in_maintenance"] ) -> None:
) with patroni_api.routes({"cluster": "cluster_is_in_maintenance_ok.json"}):
result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_is_in_maintenance"]
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -15,11 +19,13 @@ def test_cluster_is_in_maintenance_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_cluster_is_in_maintenance_ko(runner: CliRunner, fake_restapi) -> None: def test_cluster_is_in_maintenance_ko(
fake_restapi("cluster_is_in_maintenance_ko") runner: CliRunner, patroni_api: PatroniAPI
result = runner.invoke( ) -> None:
main, ["-e", "https://10.20.199.3:8008", "cluster_is_in_maintenance"] with patroni_api.routes({"cluster": "cluster_is_in_maintenance_ko.json"}):
) result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_is_in_maintenance"]
)
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout
@ -28,12 +34,14 @@ def test_cluster_is_in_maintenance_ko(runner: CliRunner, fake_restapi) -> None:
def test_cluster_is_in_maintenance_ok_pause_false( def test_cluster_is_in_maintenance_ok_pause_false(
runner: CliRunner, fake_restapi runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_is_in_maintenance_ok_pause_false") with patroni_api.routes(
result = runner.invoke( {"cluster": "cluster_is_in_maintenance_ok_pause_false.json"}
main, ["-e", "https://10.20.199.3:8008", "cluster_is_in_maintenance"] ):
) result = runner.invoke(
main, ["-e", patroni_api.endpoint, "cluster_is_in_maintenance"]
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout

View file

@ -1,15 +1,30 @@
from pathlib import Path
from typing import Iterator, Union
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI, cluster_api_set_replica_running
@pytest.fixture
def cluster_node_count_ok(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_node_count_ok.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_node_count_ok")
def test_cluster_node_count_ok( def test_cluster_node_count_ok(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI, old_replica_state: bool
) -> None: ) -> None:
fake_restapi("cluster_node_count_ok", use_old_replica_state=old_replica_state) result = runner.invoke(main, ["-e", patroni_api.endpoint, "cluster_node_count"])
result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "cluster_node_count"]
)
assert result.exit_code == 0 assert result.exit_code == 0
if old_replica_state: if old_replica_state:
assert ( assert (
@ -23,15 +38,15 @@ def test_cluster_node_count_ok(
) )
@pytest.mark.usefixtures("cluster_node_count_ok")
def test_cluster_node_count_ok_with_thresholds( def test_cluster_node_count_ok_with_thresholds(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI, old_replica_state: bool
) -> None: ) -> None:
fake_restapi("cluster_node_count_ok", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_node_count", "cluster_node_count",
"--warning", "--warning",
"@0:1", "@0:1",
@ -56,17 +71,26 @@ def test_cluster_node_count_ok_with_thresholds(
) )
@pytest.fixture
def cluster_node_count_healthy_warning(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_node_count_healthy_warning.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_node_count_healthy_warning")
def test_cluster_node_count_healthy_warning( def test_cluster_node_count_healthy_warning(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI, old_replica_state: bool
) -> None: ) -> None:
fake_restapi(
"cluster_node_count_healthy_warning", use_old_replica_state=old_replica_state
)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_node_count", "cluster_node_count",
"--healthy-warning", "--healthy-warning",
"@2", "@2",
@ -87,17 +111,26 @@ def test_cluster_node_count_healthy_warning(
) )
@pytest.fixture
def cluster_node_count_healthy_critical(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_node_count_healthy_critical.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_node_count_healthy_critical")
def test_cluster_node_count_healthy_critical( def test_cluster_node_count_healthy_critical(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi(
"cluster_node_count_healthy_critical", use_old_replica_state=old_replica_state
)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_node_count", "cluster_node_count",
"--healthy-warning", "--healthy-warning",
"@2", "@2",
@ -112,15 +145,26 @@ def test_cluster_node_count_healthy_critical(
) )
@pytest.fixture
def cluster_node_count_warning(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_node_count_warning.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_node_count_warning")
def test_cluster_node_count_warning( def test_cluster_node_count_warning(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI, old_replica_state: bool
) -> None: ) -> None:
fake_restapi("cluster_node_count_warning", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_node_count", "cluster_node_count",
"--warning", "--warning",
"@2", "@2",
@ -141,15 +185,26 @@ def test_cluster_node_count_warning(
) )
@pytest.fixture
def cluster_node_count_critical(
patroni_api: PatroniAPI, old_replica_state: bool, datadir: Path, tmp_path: Path
) -> Iterator[None]:
path: Union[str, Path] = "cluster_node_count_critical.json"
if old_replica_state:
path = cluster_api_set_replica_running(datadir / path, tmp_path)
with patroni_api.routes({"cluster": path}):
yield None
@pytest.mark.usefixtures("cluster_node_count_critical")
def test_cluster_node_count_critical( def test_cluster_node_count_critical(
runner: CliRunner, fake_restapi, old_replica_state: bool runner: CliRunner, patroni_api: PatroniAPI
) -> None: ) -> None:
fake_restapi("cluster_node_count_critical", use_old_replica_state=old_replica_state)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"cluster_node_count", "cluster_node_count",
"--warning", "--warning",
"@2", "@2",

View file

@ -1,11 +1,19 @@
from pathlib import Path
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_is_alive_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi(None) def test_node_is_alive_ok(
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_alive"]) runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None:
liveness = tmp_path / "liveness"
liveness.touch()
with patroni_api.routes({"liveness": liveness}):
result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_alive"])
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -13,9 +21,8 @@ def test_node_is_alive_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_alive_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_alive_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi(None, status=404) result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_alive"])
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_alive"])
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout

View file

@ -1,23 +1,37 @@
from typing import Iterator
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_is_leader_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_is_leader_ok") @pytest.fixture
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_leader"]) def node_is_leader_ok(patroni_api: PatroniAPI) -> Iterator[None]:
with patroni_api.routes(
{
"leader": "node_is_leader_ok.json",
"standby-leader": "node_is_leader_ok_standby_leader.json",
}
):
yield None
@pytest.mark.usefixtures("node_is_leader_ok")
def test_node_is_leader_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_leader"])
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
== "NODEISLEADER OK - This node is a leader node. | is_leader=1;;@0\n" == "NODEISLEADER OK - This node is a leader node. | is_leader=1;;@0\n"
) )
fake_restapi("node_is_leader_ok_standby_leader")
result = runner.invoke( result = runner.invoke(
main, main,
["-e", "https://10.20.199.3:8008", "node_is_leader", "--is-standby-leader"], ["-e", patroni_api.endpoint, "node_is_leader", "--is-standby-leader"],
) )
print(result.stdout)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -25,19 +39,17 @@ def test_node_is_leader_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_leader_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_leader_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("node_is_leader_ko", status=503) result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_leader"])
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_leader"])
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout
== "NODEISLEADER CRITICAL - This node is not a leader node. | is_leader=0;;@0\n" == "NODEISLEADER CRITICAL - This node is not a leader node. | is_leader=0;;@0\n"
) )
fake_restapi("node_is_leader_ko_standby_leader", status=503)
result = runner.invoke( result = runner.invoke(
main, main,
["-e", "https://10.20.199.3:8008", "node_is_leader", "--is-standby-leader"], ["-e", patroni_api.endpoint, "node_is_leader", "--is-standby-leader"],
) )
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (

View file

@ -2,12 +2,14 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_is_pending_restart_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_is_pending_restart_ok") def test_node_is_pending_restart_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke( with patroni_api.routes({"patroni": "node_is_pending_restart_ok.json"}):
main, ["-e", "https://10.20.199.3:8008", "node_is_pending_restart"] result = runner.invoke(
) main, ["-e", patroni_api.endpoint, "node_is_pending_restart"]
)
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -15,11 +17,11 @@ def test_node_is_pending_restart_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_pending_restart_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_pending_restart_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("node_is_pending_restart_ko") with patroni_api.routes({"patroni": "node_is_pending_restart_ko.json"}):
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_pending_restart"] main, ["-e", patroni_api.endpoint, "node_is_pending_restart"]
) )
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout

View file

@ -2,10 +2,12 @@ from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_is_primary_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_is_primary_ok") def test_node_is_primary_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_primary"]) with patroni_api.routes({"primary": "node_is_primary_ok.json"}):
result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_primary"])
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -13,9 +15,8 @@ def test_node_is_primary_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_primary_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_primary_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("node_is_primary_ko", status=503) result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_primary"])
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_primary"])
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout

View file

@ -1,11 +1,27 @@
from typing import Iterator
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_is_replica_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_is_replica_ok") @pytest.fixture
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_replica"]) def node_is_replica_ok(patroni_api: PatroniAPI) -> Iterator[None]:
with patroni_api.routes(
{
k: "node_is_replica_ok.json"
for k in ("replica", "synchronous", "asynchronous")
}
):
yield None
@pytest.mark.usefixtures("node_is_replica_ok")
def test_node_is_replica_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_replica"])
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
result.stdout result.stdout
@ -13,9 +29,8 @@ def test_node_is_replica_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_replica_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("node_is_replica_ko", status=503) result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_is_replica"])
result = runner.invoke(main, ["-e", "https://10.20.199.3:8008", "node_is_replica"])
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
result.stdout result.stdout
@ -23,11 +38,10 @@ def test_node_is_replica_ko(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_ko_lag(runner: CliRunner, fake_restapi) -> None: def test_node_is_replica_ko_lag(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok", status=503)
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_replica", "--max-lag", "100"] main, ["-e", patroni_api.endpoint, "node_is_replica", "--max-lag", "100"]
) )
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
@ -35,12 +49,11 @@ def test_node_is_replica_ko_lag(runner: CliRunner, fake_restapi) -> None:
== "NODEISREPLICA CRITICAL - This node is not a running replica with no noloadbalance tag and a lag under 100. | is_replica=0;;@0\n" == "NODEISREPLICA CRITICAL - This node is not a running replica with no noloadbalance tag and a lag under 100. | is_replica=0;;@0\n"
) )
fake_restapi("node_is_replica_ok", status=503)
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_is_replica", "node_is_replica",
"--is-async", "--is-async",
"--max-lag", "--max-lag",
@ -54,11 +67,11 @@ def test_node_is_replica_ko_lag(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_sync_ok(runner: CliRunner, fake_restapi) -> None: @pytest.mark.usefixtures("node_is_replica_ok")
def test_node_is_replica_sync_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok")
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_replica", "--is-sync"] main, ["-e", patroni_api.endpoint, "node_is_replica", "--is-sync"]
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
@ -67,11 +80,10 @@ def test_node_is_replica_sync_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_sync_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_replica_sync_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok", status=503)
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_replica", "--is-sync"] main, ["-e", patroni_api.endpoint, "node_is_replica", "--is-sync"]
) )
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
@ -80,11 +92,11 @@ def test_node_is_replica_sync_ko(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_async_ok(runner: CliRunner, fake_restapi) -> None: @pytest.mark.usefixtures("node_is_replica_ok")
def test_node_is_replica_async_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok")
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_replica", "--is-async"] main, ["-e", patroni_api.endpoint, "node_is_replica", "--is-async"]
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert ( assert (
@ -93,11 +105,10 @@ def test_node_is_replica_async_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_async_ko(runner: CliRunner, fake_restapi) -> None: def test_node_is_replica_async_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok", status=503)
result = runner.invoke( result = runner.invoke(
main, ["-e", "https://10.20.199.3:8008", "node_is_replica", "--is-async"] main, ["-e", patroni_api.endpoint, "node_is_replica", "--is-async"]
) )
assert result.exit_code == 2 assert result.exit_code == 2
assert ( assert (
@ -106,14 +117,14 @@ def test_node_is_replica_async_ko(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_is_replica_params(runner: CliRunner, fake_restapi) -> None: @pytest.mark.usefixtures("node_is_replica_ok")
def test_node_is_replica_params(runner: CliRunner, patroni_api: PatroniAPI) -> None:
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_is_replica", "node_is_replica",
"--is-async", "--is-async",
"--is-sync", "--is-sync",
@ -126,12 +137,11 @@ def test_node_is_replica_params(runner: CliRunner, fake_restapi) -> None:
) )
# We don't do the check ourselves, patroni does it and changes the return code # We don't do the check ourselves, patroni does it and changes the return code
fake_restapi("node_is_replica_ok")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_is_replica", "node_is_replica",
"--is-sync", "--is-sync",
"--max-lag", "--max-lag",

View file

@ -1,15 +1,25 @@
from typing import Iterator
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_patroni_version_ok(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_patroni_version") @pytest.fixture(scope="module", autouse=True)
def node_patroni_version(patroni_api: PatroniAPI) -> Iterator[None]:
with patroni_api.routes({"patroni": "node_patroni_version.json"}):
yield None
def test_node_patroni_version_ok(runner: CliRunner, patroni_api: PatroniAPI) -> None:
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_patroni_version", "node_patroni_version",
"--patroni-version", "--patroni-version",
"2.0.2", "2.0.2",
@ -22,13 +32,12 @@ def test_node_patroni_version_ok(runner: CliRunner, fake_restapi) -> None:
) )
def test_node_patroni_version_ko(runner: CliRunner, fake_restapi) -> None: def test_node_patroni_version_ko(runner: CliRunner, patroni_api: PatroniAPI) -> None:
fake_restapi("node_patroni_version")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_patroni_version", "node_patroni_version",
"--patroni-version", "--patroni-version",
"1.0.0", "1.0.0",

View file

@ -1,18 +1,30 @@
from pathlib import Path from pathlib import Path
from typing import Iterator
import nagiosplugin import nagiosplugin
import pytest
from click.testing import CliRunner from click.testing import CliRunner
from check_patroni.cli import main from check_patroni.cli import main
from . import PatroniAPI
def test_node_tl_has_changed_ok_with_timeline(runner: CliRunner, fake_restapi) -> None:
fake_restapi("node_tl_has_changed") @pytest.fixture
def node_tl_has_changed(patroni_api: PatroniAPI) -> Iterator[None]:
with patroni_api.routes({"patroni": "node_tl_has_changed.json"}):
yield None
@pytest.mark.usefixtures("node_tl_has_changed")
def test_node_tl_has_changed_ok_with_timeline(
runner: CliRunner, patroni_api: PatroniAPI
) -> None:
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--timeline", "--timeline",
"58", "58",
@ -25,19 +37,19 @@ def test_node_tl_has_changed_ok_with_timeline(runner: CliRunner, fake_restapi) -
) )
@pytest.mark.usefixtures("node_tl_has_changed")
def test_node_tl_has_changed_ok_with_state_file( def test_node_tl_has_changed_ok_with_state_file(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
state_file = tmp_path / "node_tl_has_changed.state_file" state_file = tmp_path / "node_tl_has_changed.state_file"
with state_file.open("w") as f: with state_file.open("w") as f:
f.write('{"timeline": 58}') f.write('{"timeline": 58}')
fake_restapi("node_tl_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -50,13 +62,15 @@ def test_node_tl_has_changed_ok_with_state_file(
) )
def test_node_tl_has_changed_ko_with_timeline(runner: CliRunner, fake_restapi) -> None: @pytest.mark.usefixtures("node_tl_has_changed")
fake_restapi("node_tl_has_changed") def test_node_tl_has_changed_ko_with_timeline(
runner: CliRunner, patroni_api: PatroniAPI
) -> None:
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--timeline", "--timeline",
"700", "700",
@ -69,20 +83,20 @@ def test_node_tl_has_changed_ko_with_timeline(runner: CliRunner, fake_restapi) -
) )
@pytest.mark.usefixtures("node_tl_has_changed")
def test_node_tl_has_changed_ko_with_state_file_and_save( def test_node_tl_has_changed_ko_with_state_file_and_save(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
state_file = tmp_path / "node_tl_has_changed.state_file" state_file = tmp_path / "node_tl_has_changed.state_file"
with state_file.open("w") as f: with state_file.open("w") as f:
f.write('{"timeline": 700}') f.write('{"timeline": 700}')
fake_restapi("node_tl_has_changed")
# test without saving the new tl # test without saving the new tl
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -106,7 +120,7 @@ def test_node_tl_has_changed_ko_with_state_file_and_save(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--state-file", "--state-file",
str(state_file), str(state_file),
@ -127,17 +141,17 @@ def test_node_tl_has_changed_ko_with_state_file_and_save(
assert new_tl == 58 assert new_tl == 58
@pytest.mark.usefixtures("node_tl_has_changed")
def test_node_tl_has_changed_params( def test_node_tl_has_changed_params(
runner: CliRunner, fake_restapi, tmp_path: Path runner: CliRunner, patroni_api: PatroniAPI, tmp_path: Path
) -> None: ) -> None:
# This one is placed last because it seems like the exceptions are not flushed from stderr for the next tests. # This one is placed last because it seems like the exceptions are not flushed from stderr for the next tests.
fake_state_file = tmp_path / "fake_file_name.state_file" fake_state_file = tmp_path / "fake_file_name.state_file"
fake_restapi("node_tl_has_changed")
result = runner.invoke( result = runner.invoke(
main, main,
[ [
"-e", "-e",
"https://10.20.199.3:8008", patroni_api.endpoint,
"node_tl_has_changed", "node_tl_has_changed",
"--timeline", "--timeline",
"58", "58",
@ -151,9 +165,7 @@ def test_node_tl_has_changed_params(
== "NODETLHASCHANGED UNKNOWN: click.exceptions.UsageError: Either --timeline or --state-file should be provided for this service\n" == "NODETLHASCHANGED UNKNOWN: click.exceptions.UsageError: Either --timeline or --state-file should be provided for this service\n"
) )
result = runner.invoke( result = runner.invoke(main, ["-e", patroni_api.endpoint, "node_tl_has_changed"])
main, ["-e", "https://10.20.199.3:8008", "node_tl_has_changed"]
)
assert result.exit_code == 3 assert result.exit_code == 3
assert ( assert (
result.stdout result.stdout

View file

@ -1,50 +0,0 @@
import json
import pathlib
from typing import Any
from pytest_mock import MockerFixture
from check_patroni.types import APIError, PatroniResource
here = pathlib.Path(__file__).parent
def getjson(name: str) -> Any:
path = here / "json" / f"{name}.json"
if not path.exists():
raise Exception(f"path does not exist : {path}")
with path.open() as f:
return json.load(f)
def my_mock(
mocker: MockerFixture,
json_file: str,
*,
status: int = 200,
use_old_replica_state: bool = False,
) -> None:
def mock_rest_api(self: PatroniResource, service: str) -> Any:
if status != 200:
raise APIError("Test en erreur pour status code 200")
if json_file:
if use_old_replica_state:
assert json_file.startswith(
"cluster_has_replica"
) or json_file.startswith("cluster_node_count")
return cluster_api_set_replica_running(getjson(json_file))
return getjson(json_file)
return None
mocker.resetall()
mocker.patch("check_patroni.types.PatroniResource.rest_api", mock_rest_api)
def cluster_api_set_replica_running(js: Any) -> Any:
# starting from 3.0.4 the state of replicas is streaming instead of running
for node in js["members"]:
if node["role"] in ["replica", "sync_standby"]:
if node["state"] == "streaming":
node["state"] = "running"
return js