diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 977b882177edbd76dcc810c58abf9cccedafe0f9..b9dbe574a70359bd7a0d75df2885269d4b286b33 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,3 +50,16 @@ include: - component: $CI_SERVER_FQDN/computing/gitlab/components/sphinx/build@1 inputs: requirements: ".[docs]" + + +# -- customisations ------------------ + +redhat_test_el8: + before_script: + - !reference [redhat_test, before_script] + # install a newer version of pytest and friends on EL8 + - dnf install -y -q python3-pip && + python3 -m pip install + --upgrade-strategy=only-if-needed + "coverage[toml]==5.3" + "pytest==3.9.3" diff --git a/debian/control b/debian/control index 5e2c44def5ae3929316c5b0b7908aee20ef1537d..608f44cdd3bc59931dbc19ed497dd505ccead706 100644 --- a/debian/control +++ b/debian/control @@ -14,7 +14,7 @@ Build-Depends: python3-argparse-manpage, python3-igwn-auth-utils (>= 0.3.1), python3-igwn-segments, - python3-pytest (>= 2.8.0), + python3-pytest (>= 3.9.3), python3-requests-mock, python3-setuptools, diff --git a/gwdatafind/conftest.py b/gwdatafind/conftest.py deleted file mode 100644 index e772c6e53c239e4480761a18f666e4c25ce04ce4..0000000000000000000000000000000000000000 --- a/gwdatafind/conftest.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright (C) Cardiff University (2018-2022) -# -# This file is part of GWDataFind. -# -# GWDataFind is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# GWDataFind is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. - -"""Pytest hooks for gwdatafind. -""" - -import warnings - -__author__ = "Duncan Macleod <duncan.macleod@ligo.org>" - -# always present warnings during testing -warnings.simplefilter("always") diff --git a/gwdatafind/tests/__init__.py b/gwdatafind/tests/__init__.py index 326a540735d65b712700a4293bf7ee464b08384a..8675b09e078a29593aa96962c34f69010931f698 100644 --- a/gwdatafind/tests/__init__.py +++ b/gwdatafind/tests/__init__.py @@ -15,14 +15,6 @@ # You should have received a copy of the GNU General Public License # along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. -"""Tests for :mod:`gwdatafind`. -""" +"""Test :mod:`gwdatafind`.""" __author__ = "Duncan Macleod <duncan.macleod@ligo.org>" - -import pytest - -if pytest.__version__ < "3.0": - yield_fixture = pytest.yield_fixture -else: - yield_fixture = pytest.fixture diff --git a/gwdatafind/tests/conftest.py b/gwdatafind/tests/conftest.py deleted file mode 100644 index fd2e1dd9bf14bd8764491b9701b01b75e314a5f5..0000000000000000000000000000000000000000 --- a/gwdatafind/tests/conftest.py +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright (C) Cardiff University (2018-2022) -# -# This program is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by the -# Free Software Foundation; either version 2 of the License, or (at your -# option) any later version. -# -# This program is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General -# Public License for more details. -# -# You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -"""Test utilities. -""" - -import os -import tempfile - -from . import yield_fixture - - -@yield_fixture -def tmpname(): - """Return a temporary file name, cleaning up after the method returns. - """ - name = tempfile.mktemp() - open(name, "w").close() - try: - yield name - finally: - if os.path.isfile(name): - os.remove(name) diff --git a/gwdatafind/tests/test_api.py b/gwdatafind/tests/test_api.py index fbcad765d3d29291c20437bde31e56edbe9b1820..ee5e825fd4d29cb7fe2db4682ef592571eb76666 100644 --- a/gwdatafind/tests/test_api.py +++ b/gwdatafind/tests/test_api.py @@ -23,56 +23,63 @@ from the v1 API for gwdatfind_server. import pytest -from .. import api +from gwdatafind import api __author__ = "Duncan Macleod <duncan.macleod@ligo.org>" def test_ping_path(): + """Test `ping_path()`.""" assert api.ping_path() == "LDR/services/data/v1/gwf/H/R/1,2" def test_find_observatories_path(): + """Test `find_observatories_path()`.""" assert api.find_observatories_path() == "LDR/services/data/v1/gwf.json" -@pytest.mark.parametrize(("site", "result"), ( +@pytest.mark.parametrize(("site", "result"), [ (None, "LDR/services/data/v1/gwf/all.json"), ("X", "LDR/services/data/v1/gwf/X.json"), ("XY", "LDR/services/data/v1/gwf/XY.json"), -)) +]) def test_find_types_path(site, result): + """Test `find_types_path()`.""" assert api.find_types_path(site) == result def test_find_times_path(): + """Test `find_times_path()`.""" assert api.find_times_path("X", "TEST", 0, 1) == ( "LDR/services/data/v1/gwf/X/TEST/segments/0,1.json" ) def test_find_url_path(): + """Test `find_url_path()`.""" assert api.find_url_path("/data/X-TEST-0-1.gwf") == ( "LDR/services/data/v1/gwf/X/TEST/X-TEST-0-1.gwf.json" ) -@pytest.mark.parametrize(("urltype", "result"), ( +@pytest.mark.parametrize(("urltype", "result"), [ (None, "LDR/services/data/v1/gwf/X/TEST/latest.json"), ("file", "LDR/services/data/v1/gwf/X/TEST/latest/file.json"), -)) +]) def test_find_latest_path(urltype, result): + """Test `find_latest_path()`.""" assert api.find_latest_path("X", "TEST", urltype) == result -@pytest.mark.parametrize(("urltype", "match", "result"), ( +@pytest.mark.parametrize(("urltype", "match", "result"), [ (None, None, "LDR/services/data/v1/gwf/X/TEST/0,1.json"), ("gsiftp", None, "LDR/services/data/v1/gwf/X/TEST/0,1/gsiftp.json"), (None, "test", "LDR/services/data/v1/gwf/X/TEST/0,1.json?match=test"), ("file", "test", "LDR/services/data/v1/gwf/X/TEST/0,1/file.json?match=test"), -)) +]) def find_urls_path(urltype, match, result): + """Test `find_urls_path()`.""" assert api.find_urls_path( "X", "TEST", diff --git a/gwdatafind/tests/test_main.py b/gwdatafind/tests/test_main.py index 7d2caa132b7c1ab397813be81fb2bac5829e95c3..f6a88d40e822f59139f627f3a1347aa911cd45c5 100644 --- a/gwdatafind/tests/test_main.py +++ b/gwdatafind/tests/test_main.py @@ -15,8 +15,7 @@ # You should have received a copy of the GNU General Public License # along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. -"""Tests for :mod:`gwdatafind.__main__` (the CLI). -""" +"""Tests for :mod:`gwdatafind.__main__` (the CLI).""" import argparse import os @@ -26,7 +25,7 @@ from unittest import mock import pytest from igwn_segments import segment -from .. import __main__ as main +from gwdatafind import __main__ as main __author__ = "Duncan Macleod <duncan.macleod@ligo.org>" @@ -67,6 +66,7 @@ GAPS = [(3, 7)] @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) def test_command_line(): + """Test `command_line()`.""" parser = main.command_line() assert isinstance(parser, argparse.ArgumentParser) assert parser.description == main.__doc__ @@ -87,17 +87,19 @@ def test_command_line(): @mock.patch.dict("os.environ", clear=True) -@pytest.mark.parametrize("defserv", (None, "test.datafind.com:443")) +@pytest.mark.parametrize("defserv", [None, "test.datafind.com:443"]) def test_command_line_server(defserv): + """Test default setting for ``-r/--server``.""" if defserv: os.environ["GWDATAFIND_SERVER"] = defserv parser = main.command_line() - serveract = [act for act in parser._actions if act.dest == "server"][0] + serveract = next(act for act in parser._actions if act.dest == "server") assert serveract.required is (not defserv) @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) def test_sanity_check_pass(): + """Test `DataFindArgumentParser.sanity_check()`.""" parser = main.command_line() parser.parse_args(["-o", "X", "-t", "test", "-s", "0", "-e", "1"]) @@ -110,13 +112,18 @@ def test_sanity_check_pass(): ("--gaps", "--show-observatories"), ]) def test_sanity_check_fail(clargs): + """Test `DataFindArgumentParser.sanity_check()` error reporting.""" parser = main.command_line() - with pytest.raises(SystemExit): + with pytest.raises( + SystemExit, + match="2", + ): parser.parse_args(clargs) @mock.patch("gwdatafind.ui.ping") def test_ping(mping): + """Test `ping().""" args = argparse.Namespace( server="test.datafind.com:443", extension="gwf", @@ -134,6 +141,7 @@ def test_ping(mping): @mock.patch("gwdatafind.ui.find_observatories") def test_show_observatories(mfindobs): + """Test `show_observatories().""" mfindobs.return_value = ["A", "B", "C"] args = argparse.Namespace( server="test.datafind.com:443", @@ -153,6 +161,7 @@ def test_show_observatories(mfindobs): @mock.patch("gwdatafind.ui.find_types") def test_show_types(mfindtypes): + """Test `show_types().""" mfindtypes.return_value = ["A", "B", "C"] args = argparse.Namespace( server="test.datafind.com:443", @@ -174,6 +183,7 @@ def test_show_types(mfindtypes): @mock.patch("gwdatafind.ui.find_times") def test_show_times(mfindtimes): + """Test `show_times().""" mfindtimes.return_value = [segment(0, 1), segment(1, 2), segment(3, 4)] args = argparse.Namespace( server="test.datafind.com:443", @@ -201,6 +211,7 @@ def test_show_times(mfindtimes): @mock.patch("gwdatafind.ui.find_latest") def test_latest(mlatest): + """Test `latest()`.""" mlatest.return_value = ["file:///test/X-test-0-10.gwf"] args = argparse.Namespace( server="test.datafind.com:443", @@ -227,6 +238,7 @@ def test_latest(mlatest): @mock.patch("gwdatafind.ui.find_url") def test_filename(mfindurl): + """Test `filename()`.""" mfindurl.return_value = ["file:///test/X-test-0-10.gwf"] args = argparse.Namespace( server="test.datafind.com:443", @@ -254,6 +266,7 @@ def test_filename(mfindurl): "h5", ]) def test_show_urls(mfindurls, ext): + """Test `show_urls()`.""" urls = [x for x in URLS if x.endswith(f".{ext}")] mfindurls.return_value = urls args = argparse.Namespace( @@ -285,13 +298,14 @@ def test_show_urls(mfindurls, ext): assert list(map(str.rstrip, out.readlines())) == urls -@pytest.mark.parametrize("fmt,result", [ +@pytest.mark.parametrize(("fmt", "result"), [ ("urls", GWF_OUTPUT_URLS), ("lal", GWF_OUTPUT_LAL_CACHE), ("names", GWF_OUTPUT_NAMES_ONLY), ("omega", GWF_OUTPUT_OMEGA_CACHE), ]) def test_postprocess_cache_format(fmt, result): + """Test `postprocess_cache` with ``--format``.""" # create namespace for parsing args = argparse.Namespace( type=None, @@ -309,6 +323,7 @@ def test_postprocess_cache_format(fmt, result): def test_postprocess_cache_sft(): + """Test `postprocess_cache` for SFTs.""" args = argparse.Namespace( type="TEST_1800SFT", format=None, @@ -321,6 +336,7 @@ def test_postprocess_cache_sft(): def test_postprocess_cache_gaps(capsys): + """Test `postprocess_cache()`.""" args = argparse.Namespace( gpsstart=0, gpsend=10, @@ -340,7 +356,7 @@ def test_postprocess_cache_gaps(capsys): @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) -@pytest.mark.parametrize("args,patch", [ +@pytest.mark.parametrize(("args", "patch"), [ (["--ping"], "ping"), (["--show-observatories"], "show_observatories"), (["--show-types"], "show_types"), @@ -349,12 +365,15 @@ def test_postprocess_cache_gaps(capsys): (["--filename", "X-test-0-1.gwf"], "filename"), (["-o", "X", "-t", "test", "-s", "0", "-e", "10"], "show_urls"), ]) -def test_main(args, patch, tmpname): +def test_main(args, patch, tmp_path): + """Test `main()`.""" + outfile = tmp_path / "out" + with mock.patch(f"gwdatafind.__main__.{patch}") as mocked: main.main(args) assert mocked.call_count == 1 # call again with output file - args.extend(("--output-file", tmpname)) + args.extend(("--output-file", str(outfile))) with mock.patch(f"gwdatafind.__main__.{patch}") as mocked: main.main(args) assert mocked.call_count == 1 diff --git a/gwdatafind/tests/test_ui.py b/gwdatafind/tests/test_ui.py index ef1d523f197052bf5ab877ddf8b6924f23c79eaa..c38bab42c09c858ab9bf8cea4565aa5f4a0a8ef9 100644 --- a/gwdatafind/tests/test_ui.py +++ b/gwdatafind/tests/test_ui.py @@ -15,18 +15,24 @@ # You should have received a copy of the GNU General Public License # along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. +"""Test :mod:`gwdatafind.ui`.""" + import warnings +from contextlib import contextmanager from functools import partial +from math import ( + ceil, + floor, +) from unittest import mock import igwn_segments as segments import pytest -from .. import ( +from gwdatafind import ( api, ui, ) -from . import yield_fixture __author__ = "Duncan Macleod <duncan.macleod@ligo.org>" @@ -47,12 +53,22 @@ TEST_DATA = { } +@contextmanager +def no_warning(): + """Context manager to ensure no warnings are emitted.""" + with warnings.catch_warnings() as ctx: + warnings.simplefilter("error") + yield ctx + + def _url(suffix): + """Return a fully-qualified URL with the given suffix.""" return f"{TEST_URL_BASE}/{suffix}" -@yield_fixture(autouse=True) +@pytest.fixture(autouse=True) def gwdatafind_server_env(): + """Patch `os.environ` with our value for ``GWDATAFIND_SERVER``.""" with mock.patch.dict( "os.environ", {"GWDATAFIND_SERVER": TEST_SERVER}, @@ -60,45 +76,77 @@ def gwdatafind_server_env(): yield -@yield_fixture(autouse=True, scope="module") +@pytest.fixture(autouse=True, scope="module") def noauth(): """Force the underlying _get() function to use no authentication. So that the tests don't fall over if the test runner has bad creds. """ - _get_noauth = partial(ui._get, cert=False, token=False) - with mock.patch("gwdatafind.ui._get", _get_noauth): + with mock.patch( + "gwdatafind.ui._get", + partial(ui._get, cert=False, token=False), # noqa: SLF001 + ): yield -@pytest.mark.parametrize(("in_", "url"), ( +@pytest.mark.parametrize(("in_", "url"), [ # no scheme and no port, default to https - ("datafind.example.com", "https://datafind.example.com"), + pytest.param( + "datafind.example.com", + "https://datafind.example.com", + id="scheme-default", + ), # scheme specified, do nothing - ("test://datafind.example.com", "test://datafind.example.com"), - ("test://datafind.example.com:1234", "test://datafind.example.com:1234"), - ("https://datafind.example.com:80", "https://datafind.example.com:80"), + pytest.param( + "test://datafind.example.com", + "test://datafind.example.com", + id="scheme-noop", + ), + pytest.param( + "test://datafind.example.com:1234", + "test://datafind.example.com:1234", + id="port-noop", + ), + pytest.param( + "https://datafind.example.com:80", + "https://datafind.example.com:80", + id="scheme-port-noop", + ), # no scheme and port 80, use http - ("datafind.example.com:80", "http://datafind.example.com:80"), + pytest.param( + "datafind.example.com:80", + "http://datafind.example.com:80", + id="scheme-port-http", + ), # no scheme and port != 80, use https - ("datafind.example.com:443", "https://datafind.example.com:443"), + pytest.param( + "datafind.example.com:443", + "https://datafind.example.com:443", + id="scheme-port-https", + ), # default host - (None, TEST_URL_BASE), -)) + pytest.param( + None, + TEST_URL_BASE, + ), +]) def test_url_scheme_handling(in_, url): - assert ui._url(in_, lambda: "test") == f"{url}/test" + """Test URL scheme handling in `_url()`.""" + assert ui._url(in_, lambda: "test") == f"{url}/test" # noqa: SLF001 def test_ping(requests_mock): + """Test `ping()`.""" requests_mock.get(_url(api.ping_path()), status_code=200) ui.ping() -@pytest.mark.parametrize(("match", "result"), ( - (None, ("A", "B", "C")), - ("[AB]", ("A", "B")), -)) +@pytest.mark.parametrize(("match", "result"), [ + pytest.param(None, ("A", "B", "C"), id="all"), + pytest.param("[AB]", ("A", "B"), id="regex"), +]) def test_find_observatories(match, result, requests_mock): + """Test `find_observatories()`.""" requests_mock.get( _url(api.find_observatories_path()), json=list(TEST_DATA), @@ -106,12 +154,28 @@ def test_find_observatories(match, result, requests_mock): assert ui.find_observatories(match=match) == list(set(result)) -@pytest.mark.parametrize(("site", "match", "result"), ( - (None, None, [ft for site in TEST_DATA for ft in TEST_DATA[site]]), - ("A", None, list(TEST_DATA["A"])), - ("A", "PROD", ["A1_PROD"]), -)) +@pytest.mark.parametrize(("site", "match", "result"), [ + pytest.param( + None, + None, + [ft for site in TEST_DATA for ft in TEST_DATA[site]], + id="all", + ), + pytest.param( + "A", + None, + list(TEST_DATA["A"]), + id="site", + ), + pytest.param( + "A", + "PROD", + ["A1_PROD"], + id="site-match", + ), +]) def test_find_types(site, match, result, requests_mock): + """Test `find_types()`.""" if site: respdata = list(TEST_DATA[site]) else: @@ -127,6 +191,7 @@ def test_find_types(site, match, result, requests_mock): def test_find_times(requests_mock): + """Test `find_times()`.""" site = "A" frametype = "A1_TEST" requests_mock.get( @@ -141,6 +206,7 @@ def test_find_times(requests_mock): def test_find_url(requests_mock): + """Test `find_url()`.""" urls = [ "file:///data/A/A1_TEST/A-A1_TEST-0-1.gwf", "gsiftp://localhost:2811/data/A/A1_TEST/A-A1_TEST-0-1.gwf", @@ -157,27 +223,45 @@ def test_find_url(requests_mock): ) == urls[1:] -def test_find_url_on_missing(requests_mock): +@pytest.mark.parametrize(("on_missing", "ctx"), [ + # no warnings, no errors + pytest.param("ignore", no_warning(), id="ignore"), + # a warning about validation + pytest.param( + "warn", + pytest.warns( + UserWarning, + match="no files found", + ), + id="warn", + ), + # an exception about validation + pytest.param( + "raise", + pytest.raises( + RuntimeError, + match="no files found", + ), + id="raise", + ), +]) +def test_find_url_on_missing(requests_mock, on_missing, ctx): + """Test `find_url` handling of missing data.""" + # mock the request requests_mock.get( _url(api.find_url_path("A-A1_TEST-0-1.gwf")), json=[], ) - # on_missing="ignore" - with warnings.catch_warnings(): - warnings.simplefilter("error") - assert ui.find_url("A-A1_TEST-0-1.gwf", on_missing="ignore") == [] - - # on_missing="warn" - with pytest.warns(UserWarning): - assert ui.find_url("A-A1_TEST-0-1.gwf", on_missing="warn") == [] - - # on_missing="error" - with pytest.raises(RuntimeError): - ui.find_url("A-A1_TEST-0-1.gwf", on_missing="error") + with ctx: + assert ui.find_url( + "A-A1_TEST-0-1.gwf", + on_missing=on_missing, + ) == [] def test_find_latest(requests_mock): + """Test `find_latest()`.""" # NOTE: the target function is essentially identical to # find_url, so we just do a minimal smoke test here urls = [ @@ -192,10 +276,12 @@ def test_find_latest(requests_mock): def _file_url(seg): - return f"file:///data/A/A1_TEST/A-A1_TEST-{seg[0]}-{seg[1]-seg[0]}.gwf" + seg = segments.segment(floor(seg[0]), ceil(seg[1])) + return f"file:///data/A/A1_TEST/A-A1_TEST-{seg[0]}-{abs(seg)}.gwf" def test_find_urls(requests_mock): + """Test `find_urls()`.""" urls = list(map(_file_url, TEST_DATA["A"]["A1_TEST"][:2])) requests_mock.get( _url(api.find_urls_path("A", "A1_TEST", 0, 20, "file")), @@ -204,22 +290,43 @@ def test_find_urls(requests_mock): assert ui.find_urls("A", "A1_TEST", 0, 20, on_gaps="error") == urls -def test_find_urls_on_gaps(requests_mock): +@pytest.mark.parametrize(("on_gaps", "ctx"), [ + # no warnings, no errors + pytest.param("ignore", no_warning(), id="ignore"), + # a warning about validation + pytest.param( + "warn", + pytest.warns( + UserWarning, + match="^Missing segments", + ), + id="warn", + ), + # an exception about validation + pytest.param( + "raise", + pytest.raises( + RuntimeError, + match=r"^Missing segments", + ), + id="raise", + ), +]) +def test_find_urls_on_gaps(requests_mock, on_gaps, ctx): + """Test `find_urls` handling of gaps with ``on_gaps``.""" + # configure the mock request urls = list(map(_file_url, TEST_DATA["A"]["A1_TEST"])) requests_mock.get( _url(api.find_urls_path("A", "A1_TEST", 0, 100, "file")), json=urls, ) - # on_gaps="ignore" - with warnings.catch_warnings(): - warnings.simplefilter("error") - assert ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="ignore") == urls - - # on_missing="warn" - with pytest.warns(UserWarning): - assert ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="warn") == urls - - # on_missing="error" - with pytest.raises(RuntimeError): - ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="error") + # make the request + with ctx: + assert ui.find_urls( + "A", + "A1_TEST", + 0, + 100, + on_gaps=on_gaps, + ) == urls diff --git a/gwdatafind/tests/test_utils.py b/gwdatafind/tests/test_utils.py index 7d008bd9fe57202e18bd7106517505f39f4a1d8d..d7a6c4056ed902ff81bb2fb9d1c0e001849521d3 100644 --- a/gwdatafind/tests/test_utils.py +++ b/gwdatafind/tests/test_utils.py @@ -16,14 +16,13 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -"""Tests for :mod:`gwdatafind.utils`. -""" +"""Tests for :mod:`gwdatafind.utils`.""" from unittest import mock import pytest -from .. import utils +from gwdatafind import utils @mock.patch.dict( @@ -34,6 +33,7 @@ from .. import utils }, ) def test_get_default_host(): + """Test `get_default_host()` with both environment variables.""" assert utils.get_default_host() == "gwtest" @@ -43,12 +43,17 @@ def test_get_default_host(): clear=True, ) def test_get_default_host_ligo(): + """Test `get_default_host()` with ``LIGO_DATAFIND_SERVER`` env only.""" assert utils.get_default_host() == "ligotest" @mock.patch.dict("os.environ", clear=True) def test_get_default_host_error(): - with pytest.raises(ValueError): + """Test `get_default_host()` error handling.""" + with pytest.raises( + ValueError, + match="Failed to determine default gwdatafind host", + ): utils.get_default_host() @@ -56,7 +61,8 @@ def test_get_default_host_error(): "igwn_auth_utils.x509.validate_certificate", side_effect=(None, RuntimeError), ) -def test_validate_proxy(_): +def test_validate_proxy(mock_validate): + """Test `validate_proxy()`.""" # check that no error ends up as 'True' with pytest.warns(DeprecationWarning): assert utils.validate_proxy("something") is True @@ -69,7 +75,8 @@ def test_validate_proxy(_): "igwn_auth_utils.x509.find_credentials", side_effect=("cert", ("cert", "key")), ) -def test_find_credential(_): +def test_find_credential(mock_find_credentials): + """Test `find_credential()`.""" # check that if upstream returns a single cert, we still get a tuple with pytest.warns(DeprecationWarning): assert utils.find_credential() == ("cert", "cert") diff --git a/pyproject.toml b/pyproject.toml index 042ed1642873526fba69aff2698341aa5768e192..82f010be538cf28aa969a1d28e3dc0a844d83f09 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,7 +52,7 @@ docs = [ "sphinx-copybutton", ] test = [ - "pytest >= 2.8.0", + "pytest >= 3.9.3", "pytest-cov", "requests-mock", ] @@ -86,6 +86,7 @@ precision = 1 source = ["gwdatafind"] [tool.pytest.ini_options] +minversion = "3.9.3" addopts = "-r a --color=yes" filterwarnings = [ "error", @@ -102,6 +103,8 @@ ignore = [ "PLR0913", # too many arguments "SIM108", # if-else instead of ternary if ] +# allow 'mock_...' variables to go unused in tests +dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?)|mock_.*)$" [tool.ruff.lint.isort] combine-as-imports = true diff --git a/rpm/python-gwdatafind.spec b/rpm/python-gwdatafind.spec index 83253fd617bda949d75bee2564536da392bb41d6..45a6dc47fec07e4a759dbb6814014bc63119471c 100644 --- a/rpm/python-gwdatafind.spec +++ b/rpm/python-gwdatafind.spec @@ -28,7 +28,7 @@ BuildRequires: python3dist(igwn-segments) # testing dependencies BuildRequires: man-db -BuildRequires: python3dist(pytest) >= 2.8.0 +BuildRequires: python3dist(pytest) >= 3.1.0 BuildRequires: python3dist(requests-mock) # -- src.rpm @@ -100,6 +100,12 @@ console_scripts = [build_manpages] manpages = man/gw_data_find.1:prog=gwdatafind:function=command_line:module=gwdatafind.__main__ +[tool:pytest] +minversion = 3.1.0 +addopts = -r a +filterwarnings = + error + ignore:.*pkg_resources SETUP_CFG %endif %if %{undefined pyproject_wheel} @@ -136,7 +142,12 @@ export PYTHONPATH="%{buildroot}%{python3_sitelib}" %{__python3} -m gwdatafind --help %{buildroot}%{_bindir}/gw_data_find --help # run test suite +%if 0%{?rhel} == 0 || 0%{?rhel} >= 9 %{pytest} --pyargs gwdatafind +%else +# pytest < 3.9 (EPEL8) can't handle 'tmp_path' fixture +%{pytest} --pyargs gwdatafind -k "not test_main[" +%endif # test man pages env MANPATH="%{buildroot}%{_mandir}" man -P cat gw_data_find