Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • duncan.meacher/gwdatafind
  • duncanmmacleod/gwdatafind
  • computing/gwdatafind/client
3 results
Show changes
Commits on Source (2)
...@@ -51,3 +51,16 @@ include: ...@@ -51,3 +51,16 @@ include:
- component: $CI_SERVER_FQDN/computing/gitlab/components/sphinx/build@1 - component: $CI_SERVER_FQDN/computing/gitlab/components/sphinx/build@1
inputs: inputs:
requirements: ".[docs]" 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"
...@@ -14,7 +14,7 @@ Build-Depends: ...@@ -14,7 +14,7 @@ Build-Depends:
python3-argparse-manpage, python3-argparse-manpage,
python3-igwn-auth-utils (>= 0.3.1), python3-igwn-auth-utils (>= 0.3.1),
python3-igwn-segments, python3-igwn-segments,
python3-pytest (>= 2.8.0), python3-pytest (>= 3.9.3),
python3-requests-mock, python3-requests-mock,
python3-setuptools, python3-setuptools,
......
# 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")
...@@ -15,14 +15,6 @@ ...@@ -15,14 +15,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. # 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>" __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
import pytest
if pytest.__version__ < "3.0":
yield_fixture = pytest.yield_fixture
else:
yield_fixture = pytest.fixture
# 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)
...@@ -23,56 +23,63 @@ from the v1 API for gwdatfind_server. ...@@ -23,56 +23,63 @@ from the v1 API for gwdatfind_server.
import pytest import pytest
from .. import api from gwdatafind import api
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>" __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
def test_ping_path(): def test_ping_path():
"""Test `ping_path()`."""
assert api.ping_path() == "LDR/services/data/v1/gwf/H/R/1,2" assert api.ping_path() == "LDR/services/data/v1/gwf/H/R/1,2"
def test_find_observatories_path(): def test_find_observatories_path():
"""Test `find_observatories_path()`."""
assert api.find_observatories_path() == "LDR/services/data/v1/gwf.json" 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"), (None, "LDR/services/data/v1/gwf/all.json"),
("X", "LDR/services/data/v1/gwf/X.json"), ("X", "LDR/services/data/v1/gwf/X.json"),
("XY", "LDR/services/data/v1/gwf/XY.json"), ("XY", "LDR/services/data/v1/gwf/XY.json"),
)) ])
def test_find_types_path(site, result): def test_find_types_path(site, result):
"""Test `find_types_path()`."""
assert api.find_types_path(site) == result assert api.find_types_path(site) == result
def test_find_times_path(): def test_find_times_path():
"""Test `find_times_path()`."""
assert api.find_times_path("X", "TEST", 0, 1) == ( assert api.find_times_path("X", "TEST", 0, 1) == (
"LDR/services/data/v1/gwf/X/TEST/segments/0,1.json" "LDR/services/data/v1/gwf/X/TEST/segments/0,1.json"
) )
def test_find_url_path(): def test_find_url_path():
"""Test `find_url_path()`."""
assert api.find_url_path("/data/X-TEST-0-1.gwf") == ( assert api.find_url_path("/data/X-TEST-0-1.gwf") == (
"LDR/services/data/v1/gwf/X/TEST/X-TEST-0-1.gwf.json" "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"), (None, "LDR/services/data/v1/gwf/X/TEST/latest.json"),
("file", "LDR/services/data/v1/gwf/X/TEST/latest/file.json"), ("file", "LDR/services/data/v1/gwf/X/TEST/latest/file.json"),
)) ])
def test_find_latest_path(urltype, result): def test_find_latest_path(urltype, result):
"""Test `find_latest_path()`."""
assert api.find_latest_path("X", "TEST", urltype) == result 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"), (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"), ("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"), (None, "test", "LDR/services/data/v1/gwf/X/TEST/0,1.json?match=test"),
("file", "test", ("file", "test",
"LDR/services/data/v1/gwf/X/TEST/0,1/file.json?match=test"), "LDR/services/data/v1/gwf/X/TEST/0,1/file.json?match=test"),
)) ])
def find_urls_path(urltype, match, result): def find_urls_path(urltype, match, result):
"""Test `find_urls_path()`."""
assert api.find_urls_path( assert api.find_urls_path(
"X", "X",
"TEST", "TEST",
......
...@@ -15,8 +15,7 @@ ...@@ -15,8 +15,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. # 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 argparse
import os import os
...@@ -26,7 +25,7 @@ from unittest import mock ...@@ -26,7 +25,7 @@ from unittest import mock
import pytest import pytest
from igwn_segments import segment from igwn_segments import segment
from .. import __main__ as main from gwdatafind import __main__ as main
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>" __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
...@@ -67,6 +66,7 @@ GAPS = [(3, 7)] ...@@ -67,6 +66,7 @@ GAPS = [(3, 7)]
@mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"})
def test_command_line(): def test_command_line():
"""Test `command_line()`."""
parser = main.command_line() parser = main.command_line()
assert isinstance(parser, argparse.ArgumentParser) assert isinstance(parser, argparse.ArgumentParser)
assert parser.description == main.__doc__ assert parser.description == main.__doc__
...@@ -87,17 +87,19 @@ def test_command_line(): ...@@ -87,17 +87,19 @@ def test_command_line():
@mock.patch.dict("os.environ", clear=True) @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): def test_command_line_server(defserv):
"""Test default setting for ``-r/--server``."""
if defserv: if defserv:
os.environ["GWDATAFIND_SERVER"] = defserv os.environ["GWDATAFIND_SERVER"] = defserv
parser = main.command_line() 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) assert serveract.required is (not defserv)
@mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"})
def test_sanity_check_pass(): def test_sanity_check_pass():
"""Test `DataFindArgumentParser.sanity_check()`."""
parser = main.command_line() parser = main.command_line()
parser.parse_args(["-o", "X", "-t", "test", "-s", "0", "-e", "1"]) parser.parse_args(["-o", "X", "-t", "test", "-s", "0", "-e", "1"])
...@@ -110,13 +112,18 @@ def test_sanity_check_pass(): ...@@ -110,13 +112,18 @@ def test_sanity_check_pass():
("--gaps", "--show-observatories"), ("--gaps", "--show-observatories"),
]) ])
def test_sanity_check_fail(clargs): def test_sanity_check_fail(clargs):
"""Test `DataFindArgumentParser.sanity_check()` error reporting."""
parser = main.command_line() parser = main.command_line()
with pytest.raises(SystemExit): with pytest.raises(
SystemExit,
match="2",
):
parser.parse_args(clargs) parser.parse_args(clargs)
@mock.patch("gwdatafind.ui.ping") @mock.patch("gwdatafind.ui.ping")
def test_ping(mping): def test_ping(mping):
"""Test `ping()."""
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
extension="gwf", extension="gwf",
...@@ -134,6 +141,7 @@ def test_ping(mping): ...@@ -134,6 +141,7 @@ def test_ping(mping):
@mock.patch("gwdatafind.ui.find_observatories") @mock.patch("gwdatafind.ui.find_observatories")
def test_show_observatories(mfindobs): def test_show_observatories(mfindobs):
"""Test `show_observatories()."""
mfindobs.return_value = ["A", "B", "C"] mfindobs.return_value = ["A", "B", "C"]
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
...@@ -153,6 +161,7 @@ def test_show_observatories(mfindobs): ...@@ -153,6 +161,7 @@ def test_show_observatories(mfindobs):
@mock.patch("gwdatafind.ui.find_types") @mock.patch("gwdatafind.ui.find_types")
def test_show_types(mfindtypes): def test_show_types(mfindtypes):
"""Test `show_types()."""
mfindtypes.return_value = ["A", "B", "C"] mfindtypes.return_value = ["A", "B", "C"]
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
...@@ -174,6 +183,7 @@ def test_show_types(mfindtypes): ...@@ -174,6 +183,7 @@ def test_show_types(mfindtypes):
@mock.patch("gwdatafind.ui.find_times") @mock.patch("gwdatafind.ui.find_times")
def test_show_times(mfindtimes): def test_show_times(mfindtimes):
"""Test `show_times()."""
mfindtimes.return_value = [segment(0, 1), segment(1, 2), segment(3, 4)] mfindtimes.return_value = [segment(0, 1), segment(1, 2), segment(3, 4)]
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
...@@ -201,6 +211,7 @@ def test_show_times(mfindtimes): ...@@ -201,6 +211,7 @@ def test_show_times(mfindtimes):
@mock.patch("gwdatafind.ui.find_latest") @mock.patch("gwdatafind.ui.find_latest")
def test_latest(mlatest): def test_latest(mlatest):
"""Test `latest()`."""
mlatest.return_value = ["file:///test/X-test-0-10.gwf"] mlatest.return_value = ["file:///test/X-test-0-10.gwf"]
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
...@@ -227,6 +238,7 @@ def test_latest(mlatest): ...@@ -227,6 +238,7 @@ def test_latest(mlatest):
@mock.patch("gwdatafind.ui.find_url") @mock.patch("gwdatafind.ui.find_url")
def test_filename(mfindurl): def test_filename(mfindurl):
"""Test `filename()`."""
mfindurl.return_value = ["file:///test/X-test-0-10.gwf"] mfindurl.return_value = ["file:///test/X-test-0-10.gwf"]
args = argparse.Namespace( args = argparse.Namespace(
server="test.datafind.com:443", server="test.datafind.com:443",
...@@ -254,6 +266,7 @@ def test_filename(mfindurl): ...@@ -254,6 +266,7 @@ def test_filename(mfindurl):
"h5", "h5",
]) ])
def test_show_urls(mfindurls, ext): def test_show_urls(mfindurls, ext):
"""Test `show_urls()`."""
urls = [x for x in URLS if x.endswith(f".{ext}")] urls = [x for x in URLS if x.endswith(f".{ext}")]
mfindurls.return_value = urls mfindurls.return_value = urls
args = argparse.Namespace( args = argparse.Namespace(
...@@ -285,13 +298,14 @@ def test_show_urls(mfindurls, ext): ...@@ -285,13 +298,14 @@ def test_show_urls(mfindurls, ext):
assert list(map(str.rstrip, out.readlines())) == urls assert list(map(str.rstrip, out.readlines())) == urls
@pytest.mark.parametrize("fmt,result", [ @pytest.mark.parametrize(("fmt", "result"), [
("urls", GWF_OUTPUT_URLS), ("urls", GWF_OUTPUT_URLS),
("lal", GWF_OUTPUT_LAL_CACHE), ("lal", GWF_OUTPUT_LAL_CACHE),
("names", GWF_OUTPUT_NAMES_ONLY), ("names", GWF_OUTPUT_NAMES_ONLY),
("omega", GWF_OUTPUT_OMEGA_CACHE), ("omega", GWF_OUTPUT_OMEGA_CACHE),
]) ])
def test_postprocess_cache_format(fmt, result): def test_postprocess_cache_format(fmt, result):
"""Test `postprocess_cache` with ``--format``."""
# create namespace for parsing # create namespace for parsing
args = argparse.Namespace( args = argparse.Namespace(
type=None, type=None,
...@@ -309,6 +323,7 @@ def test_postprocess_cache_format(fmt, result): ...@@ -309,6 +323,7 @@ def test_postprocess_cache_format(fmt, result):
def test_postprocess_cache_sft(): def test_postprocess_cache_sft():
"""Test `postprocess_cache` for SFTs."""
args = argparse.Namespace( args = argparse.Namespace(
type="TEST_1800SFT", type="TEST_1800SFT",
format=None, format=None,
...@@ -321,6 +336,7 @@ def test_postprocess_cache_sft(): ...@@ -321,6 +336,7 @@ def test_postprocess_cache_sft():
def test_postprocess_cache_gaps(capsys): def test_postprocess_cache_gaps(capsys):
"""Test `postprocess_cache()`."""
args = argparse.Namespace( args = argparse.Namespace(
gpsstart=0, gpsstart=0,
gpsend=10, gpsend=10,
...@@ -340,7 +356,7 @@ def test_postprocess_cache_gaps(capsys): ...@@ -340,7 +356,7 @@ def test_postprocess_cache_gaps(capsys):
@mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"}) @mock.patch.dict(os.environ, {"GWDATAFIND_SERVER": "something"})
@pytest.mark.parametrize("args,patch", [ @pytest.mark.parametrize(("args", "patch"), [
(["--ping"], "ping"), (["--ping"], "ping"),
(["--show-observatories"], "show_observatories"), (["--show-observatories"], "show_observatories"),
(["--show-types"], "show_types"), (["--show-types"], "show_types"),
...@@ -349,12 +365,15 @@ def test_postprocess_cache_gaps(capsys): ...@@ -349,12 +365,15 @@ def test_postprocess_cache_gaps(capsys):
(["--filename", "X-test-0-1.gwf"], "filename"), (["--filename", "X-test-0-1.gwf"], "filename"),
(["-o", "X", "-t", "test", "-s", "0", "-e", "10"], "show_urls"), (["-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: with mock.patch(f"gwdatafind.__main__.{patch}") as mocked:
main.main(args) main.main(args)
assert mocked.call_count == 1 assert mocked.call_count == 1
# call again with output file # 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: with mock.patch(f"gwdatafind.__main__.{patch}") as mocked:
main.main(args) main.main(args)
assert mocked.call_count == 1 assert mocked.call_count == 1
...@@ -15,18 +15,24 @@ ...@@ -15,18 +15,24 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with GWDataFind. If not, see <http://www.gnu.org/licenses/>. # along with GWDataFind. If not, see <http://www.gnu.org/licenses/>.
"""Test :mod:`gwdatafind.ui`."""
import warnings import warnings
from contextlib import contextmanager
from functools import partial from functools import partial
from math import (
ceil,
floor,
)
from unittest import mock from unittest import mock
import igwn_segments as segments import igwn_segments as segments
import pytest import pytest
from .. import ( from gwdatafind import (
api, api,
ui, ui,
) )
from . import yield_fixture
__author__ = "Duncan Macleod <duncan.macleod@ligo.org>" __author__ = "Duncan Macleod <duncan.macleod@ligo.org>"
...@@ -47,12 +53,22 @@ TEST_DATA = { ...@@ -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): def _url(suffix):
"""Return a fully-qualified URL with the given suffix."""
return f"{TEST_URL_BASE}/{suffix}" return f"{TEST_URL_BASE}/{suffix}"
@yield_fixture(autouse=True) @pytest.fixture(autouse=True)
def gwdatafind_server_env(): def gwdatafind_server_env():
"""Patch `os.environ` with our value for ``GWDATAFIND_SERVER``."""
with mock.patch.dict( with mock.patch.dict(
"os.environ", "os.environ",
{"GWDATAFIND_SERVER": TEST_SERVER}, {"GWDATAFIND_SERVER": TEST_SERVER},
...@@ -60,45 +76,77 @@ def gwdatafind_server_env(): ...@@ -60,45 +76,77 @@ def gwdatafind_server_env():
yield yield
@yield_fixture(autouse=True, scope="module") @pytest.fixture(autouse=True, scope="module")
def noauth(): def noauth():
"""Force the underlying _get() function to use no authentication. """Force the underlying _get() function to use no authentication.
So that the tests don't fall over if the test runner has bad creds. 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(
with mock.patch("gwdatafind.ui._get", _get_noauth): "gwdatafind.ui._get",
partial(ui._get, cert=False, token=False), # noqa: SLF001
):
yield yield
@pytest.mark.parametrize(("in_", "url"), ( @pytest.mark.parametrize(("in_", "url"), [
# no scheme and no port, default to https # 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 # scheme specified, do nothing
("test://datafind.example.com", "test://datafind.example.com"), pytest.param(
("test://datafind.example.com:1234", "test://datafind.example.com:1234"), "test://datafind.example.com",
("https://datafind.example.com:80", "https://datafind.example.com:80"), "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 # 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 # 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 # default host
(None, TEST_URL_BASE), pytest.param(
)) None,
TEST_URL_BASE,
),
])
def test_url_scheme_handling(in_, url): 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): def test_ping(requests_mock):
"""Test `ping()`."""
requests_mock.get(_url(api.ping_path()), status_code=200) requests_mock.get(_url(api.ping_path()), status_code=200)
ui.ping() ui.ping()
@pytest.mark.parametrize(("match", "result"), ( @pytest.mark.parametrize(("match", "result"), [
(None, ("A", "B", "C")), pytest.param(None, ("A", "B", "C"), id="all"),
("[AB]", ("A", "B")), pytest.param("[AB]", ("A", "B"), id="regex"),
)) ])
def test_find_observatories(match, result, requests_mock): def test_find_observatories(match, result, requests_mock):
"""Test `find_observatories()`."""
requests_mock.get( requests_mock.get(
_url(api.find_observatories_path()), _url(api.find_observatories_path()),
json=list(TEST_DATA), json=list(TEST_DATA),
...@@ -106,12 +154,28 @@ def test_find_observatories(match, result, requests_mock): ...@@ -106,12 +154,28 @@ def test_find_observatories(match, result, requests_mock):
assert ui.find_observatories(match=match) == list(set(result)) assert ui.find_observatories(match=match) == list(set(result))
@pytest.mark.parametrize(("site", "match", "result"), ( @pytest.mark.parametrize(("site", "match", "result"), [
(None, None, [ft for site in TEST_DATA for ft in TEST_DATA[site]]), pytest.param(
("A", None, list(TEST_DATA["A"])), None,
("A", "PROD", ["A1_PROD"]), 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): def test_find_types(site, match, result, requests_mock):
"""Test `find_types()`."""
if site: if site:
respdata = list(TEST_DATA[site]) respdata = list(TEST_DATA[site])
else: else:
...@@ -127,6 +191,7 @@ def test_find_types(site, match, result, requests_mock): ...@@ -127,6 +191,7 @@ def test_find_types(site, match, result, requests_mock):
def test_find_times(requests_mock): def test_find_times(requests_mock):
"""Test `find_times()`."""
site = "A" site = "A"
frametype = "A1_TEST" frametype = "A1_TEST"
requests_mock.get( requests_mock.get(
...@@ -141,6 +206,7 @@ def test_find_times(requests_mock): ...@@ -141,6 +206,7 @@ def test_find_times(requests_mock):
def test_find_url(requests_mock): def test_find_url(requests_mock):
"""Test `find_url()`."""
urls = [ urls = [
"file:///data/A/A1_TEST/A-A1_TEST-0-1.gwf", "file:///data/A/A1_TEST/A-A1_TEST-0-1.gwf",
"gsiftp://localhost:2811/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): ...@@ -157,27 +223,45 @@ def test_find_url(requests_mock):
) == urls[1:] ) == 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( requests_mock.get(
_url(api.find_url_path("A-A1_TEST-0-1.gwf")), _url(api.find_url_path("A-A1_TEST-0-1.gwf")),
json=[], json=[],
) )
# on_missing="ignore" with ctx:
with warnings.catch_warnings(): assert ui.find_url(
warnings.simplefilter("error") "A-A1_TEST-0-1.gwf",
assert ui.find_url("A-A1_TEST-0-1.gwf", on_missing="ignore") == [] on_missing=on_missing,
) == []
# 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")
def test_find_latest(requests_mock): def test_find_latest(requests_mock):
"""Test `find_latest()`."""
# NOTE: the target function is essentially identical to # NOTE: the target function is essentially identical to
# find_url, so we just do a minimal smoke test here # find_url, so we just do a minimal smoke test here
urls = [ urls = [
...@@ -192,10 +276,12 @@ def test_find_latest(requests_mock): ...@@ -192,10 +276,12 @@ def test_find_latest(requests_mock):
def _file_url(seg): 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): def test_find_urls(requests_mock):
"""Test `find_urls()`."""
urls = list(map(_file_url, TEST_DATA["A"]["A1_TEST"][:2])) urls = list(map(_file_url, TEST_DATA["A"]["A1_TEST"][:2]))
requests_mock.get( requests_mock.get(
_url(api.find_urls_path("A", "A1_TEST", 0, 20, "file")), _url(api.find_urls_path("A", "A1_TEST", 0, 20, "file")),
...@@ -204,22 +290,43 @@ def test_find_urls(requests_mock): ...@@ -204,22 +290,43 @@ def test_find_urls(requests_mock):
assert ui.find_urls("A", "A1_TEST", 0, 20, on_gaps="error") == urls 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"])) urls = list(map(_file_url, TEST_DATA["A"]["A1_TEST"]))
requests_mock.get( requests_mock.get(
_url(api.find_urls_path("A", "A1_TEST", 0, 100, "file")), _url(api.find_urls_path("A", "A1_TEST", 0, 100, "file")),
json=urls, json=urls,
) )
# on_gaps="ignore" # make the request
with warnings.catch_warnings(): with ctx:
warnings.simplefilter("error") assert ui.find_urls(
assert ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="ignore") == urls "A",
"A1_TEST",
# on_missing="warn" 0,
with pytest.warns(UserWarning): 100,
assert ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="warn") == urls on_gaps=on_gaps,
) == urls
# on_missing="error"
with pytest.raises(RuntimeError):
ui.find_urls("A", "A1_TEST", 0, 100, on_gaps="error")
...@@ -16,14 +16,13 @@ ...@@ -16,14 +16,13 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""Tests for :mod:`gwdatafind.utils`. """Tests for :mod:`gwdatafind.utils`."""
"""
from unittest import mock from unittest import mock
import pytest import pytest
from .. import utils from gwdatafind import utils
@mock.patch.dict( @mock.patch.dict(
...@@ -34,6 +33,7 @@ from .. import utils ...@@ -34,6 +33,7 @@ from .. import utils
}, },
) )
def test_get_default_host(): def test_get_default_host():
"""Test `get_default_host()` with both environment variables."""
assert utils.get_default_host() == "gwtest" assert utils.get_default_host() == "gwtest"
...@@ -43,12 +43,17 @@ def test_get_default_host(): ...@@ -43,12 +43,17 @@ def test_get_default_host():
clear=True, clear=True,
) )
def test_get_default_host_ligo(): def test_get_default_host_ligo():
"""Test `get_default_host()` with ``LIGO_DATAFIND_SERVER`` env only."""
assert utils.get_default_host() == "ligotest" assert utils.get_default_host() == "ligotest"
@mock.patch.dict("os.environ", clear=True) @mock.patch.dict("os.environ", clear=True)
def test_get_default_host_error(): 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() utils.get_default_host()
...@@ -56,7 +61,8 @@ def test_get_default_host_error(): ...@@ -56,7 +61,8 @@ def test_get_default_host_error():
"igwn_auth_utils.x509.validate_certificate", "igwn_auth_utils.x509.validate_certificate",
side_effect=(None, RuntimeError), 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' # check that no error ends up as 'True'
with pytest.warns(DeprecationWarning): with pytest.warns(DeprecationWarning):
assert utils.validate_proxy("something") is True assert utils.validate_proxy("something") is True
...@@ -69,7 +75,8 @@ def test_validate_proxy(_): ...@@ -69,7 +75,8 @@ def test_validate_proxy(_):
"igwn_auth_utils.x509.find_credentials", "igwn_auth_utils.x509.find_credentials",
side_effect=("cert", ("cert", "key")), 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 # check that if upstream returns a single cert, we still get a tuple
with pytest.warns(DeprecationWarning): with pytest.warns(DeprecationWarning):
assert utils.find_credential() == ("cert", "cert") assert utils.find_credential() == ("cert", "cert")
......
...@@ -52,7 +52,7 @@ docs = [ ...@@ -52,7 +52,7 @@ docs = [
"sphinx-copybutton", "sphinx-copybutton",
] ]
test = [ test = [
"pytest >= 2.8.0", "pytest >= 3.9.3",
"pytest-cov", "pytest-cov",
"requests-mock", "requests-mock",
] ]
...@@ -99,6 +99,7 @@ exclude = [ ...@@ -99,6 +99,7 @@ exclude = [
ignore_missing_imports = true ignore_missing_imports = true
[tool.pytest.ini_options] [tool.pytest.ini_options]
minversion = "3.9.3"
addopts = "-r a --color=yes" addopts = "-r a --color=yes"
filterwarnings = [ filterwarnings = [
"error", "error",
...@@ -115,6 +116,8 @@ ignore = [ ...@@ -115,6 +116,8 @@ ignore = [
"PLR0913", # too many arguments "PLR0913", # too many arguments
"SIM108", # if-else instead of ternary if "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] [tool.ruff.lint.isort]
combine-as-imports = true combine-as-imports = true
......
...@@ -28,7 +28,7 @@ BuildRequires: python3dist(igwn-segments) ...@@ -28,7 +28,7 @@ BuildRequires: python3dist(igwn-segments)
# testing dependencies # testing dependencies
BuildRequires: man-db BuildRequires: man-db
BuildRequires: python3dist(pytest) >= 2.8.0 BuildRequires: python3dist(pytest) >= 3.1.0
BuildRequires: python3dist(requests-mock) BuildRequires: python3dist(requests-mock)
# -- src.rpm # -- src.rpm
...@@ -100,6 +100,12 @@ console_scripts = ...@@ -100,6 +100,12 @@ console_scripts =
[build_manpages] [build_manpages]
manpages = manpages =
man/gw_data_find.1:prog=gwdatafind:function=command_line:module=gwdatafind.__main__ 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 SETUP_CFG
%endif %endif
%if %{undefined pyproject_wheel} %if %{undefined pyproject_wheel}
...@@ -136,7 +142,12 @@ export PYTHONPATH="%{buildroot}%{python3_sitelib}" ...@@ -136,7 +142,12 @@ export PYTHONPATH="%{buildroot}%{python3_sitelib}"
%{__python3} -m gwdatafind --help %{__python3} -m gwdatafind --help
%{buildroot}%{_bindir}/gw_data_find --help %{buildroot}%{_bindir}/gw_data_find --help
# run test suite # run test suite
%if 0%{?rhel} == 0 || 0%{?rhel} >= 9
%{pytest} --pyargs gwdatafind %{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 # test man pages
env MANPATH="%{buildroot}%{_mandir}" man -P cat gw_data_find env MANPATH="%{buildroot}%{_mandir}" man -P cat gw_data_find
......