From 2aa4319dbb5490e095ef88b9aeaf5f60b4bf4e21 Mon Sep 17 00:00:00 2001 From: Chris Berkhout Date: Fri, 9 Jul 2021 12:34:35 +0200 Subject: [PATCH] Live tests for ECB source. --- .gitignore | 2 + Makefile | 14 +++- poetry.lock | 99 ++++++++++++++++++++++++++++- pyproject.toml | 7 ++ src/pricehist/sources/basesource.py | 20 +++--- tests/pricehist/sources/test_ecb.py | 98 ++++++++++++++++++++++++++++ tests/test_pricehist.py | 5 -- 7 files changed, 227 insertions(+), 18 deletions(-) create mode 100644 tests/pricehist/sources/test_ecb.py delete mode 100644 tests/test_pricehist.py diff --git a/.gitignore b/.gitignore index 0fe68b5..ddf4605 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ .pytest_cache .vimrc dist/ +.coverage +htmlcov/ diff --git a/Makefile b/Makefile index 6b6ed3d..c76460c 100644 --- a/Makefile +++ b/Makefile @@ -10,5 +10,15 @@ format: ## Format source code poetry run black . .PHONY: test -test: ## Run tests - poetry run pytest +test: ## Run non-live tests + poetry run pytest -m "not live" --color=yes + +.PHONY: test-live +test-live: ## Run live tests + poetry run pytest -m live --color=yes + +.PHONY: coverage +coverage: ## Generate and open coverage report + poetry run coverage run --source=pricehist -m pytest + poetry run coverage html + xdg-open htmlcov/index.html diff --git a/poetry.lock b/poetry.lock index 16da670..63df0c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -82,6 +82,17 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "coverage" +version = "5.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +toml = ["toml"] + [[package]] name = "cssselect" version = "1.1.0" @@ -282,6 +293,30 @@ urllib3 = ">=1.21.1,<1.27" security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +[[package]] +name = "responses" +version = "0.13.3" +description = "A utility library for mocking out the `requests` Python library." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +requests = ">=2.0" +six = "*" +urllib3 = ">=1.25.10" + +[package.extras] +tests = ["coverage (>=3.7.1,<6.0.0)", "pytest-cov", "pytest-localserver", "flake8", "pytest (>=4.6,<5.0)", "pytest (>=4.6)", "mypy"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + [[package]] name = "toml" version = "0.10.2" @@ -322,7 +357,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "a0b4419f4cb60c138eeecce88efa4e617c55f4bd2b83e2c22da471bbc7863d36" +content-hash = "da28bdba188bda9b955066da37862253e0aebadb52fa2d8aeecdea5ad3165efd" [metadata.files] appdirs = [ @@ -356,6 +391,60 @@ colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] +coverage = [ + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, +] cssselect = [ {file = "cssselect-1.1.0-py2.py3-none-any.whl", hash = "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf"}, {file = "cssselect-1.1.0.tar.gz", hash = "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"}, @@ -505,6 +594,14 @@ requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] +responses = [ + {file = "responses-0.13.3-py2.py3-none-any.whl", hash = "sha256:b54067596f331786f5ed094ff21e8d79e6a1c68ef625180a7d34808d6f36c11b"}, + {file = "responses-0.13.3.tar.gz", hash = "sha256:18a5b88eb24143adbf2b4100f328a2f5bfa72fbdacf12d97d41f07c26c45553d"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/pyproject.toml b/pyproject.toml index 07fc9a5..c148eeb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,8 @@ pytest = "^6.2.2" black = "^20.8b1" flake8 = "^3.9.1" isort = "^5.8.0" +responses = "^0.13.3" +coverage = "^5.5" [build-system] requires = ["poetry-core>=1.0.0"] @@ -35,3 +37,8 @@ pricehist = "pricehist.cli:cli" [tool.isort] profile = "black" multi_line_output = 3 + +[tool.pytest.ini_options] +markers = [ + "live: makes a live request to a source" +] diff --git a/src/pricehist/sources/basesource.py b/src/pricehist/sources/basesource.py index c630791..52f0df8 100644 --- a/src/pricehist/sources/basesource.py +++ b/src/pricehist/sources/basesource.py @@ -10,45 +10,45 @@ from pricehist.series import Series class BaseSource(ABC): @abstractmethod def id(self) -> str: - pass + pass # pragma: nocover @abstractmethod def name(self) -> str: - pass + pass # pragma: nocover @abstractmethod def description(self) -> str: - pass + pass # pragma: nocover @abstractmethod def source_url(self) -> str: - pass + pass # pragma: nocover @abstractmethod def start(self) -> str: - pass + pass # pragma: nocover @abstractmethod def types(self) -> list[str]: - pass + pass # pragma: nocover @abstractmethod def notes(self) -> str: - pass + pass # pragma: nocover def normalizesymbol(self, str) -> str: return str.upper() @abstractmethod def symbols(self) -> list[(str, str)]: - pass + pass # pragma: nocover def search(self, query) -> list[(str, str)]: - pass + pass # pragma: nocover @abstractmethod def fetch(self, series: Series) -> Series: - pass + pass # pragma: nocover def log_curl(self, response): curl = curlify.to_curl(response.request, compressed=True) diff --git a/tests/pricehist/sources/test_ecb.py b/tests/pricehist/sources/test_ecb.py new file mode 100644 index 0000000..4b9f0bf --- /dev/null +++ b/tests/pricehist/sources/test_ecb.py @@ -0,0 +1,98 @@ +from decimal import Decimal + +import pytest + +from pricehist.price import Price +from pricehist.series import Series +from pricehist.sources.ecb import ECB + +# import responses +# @responses.activate + + +def in_log(caplog, levelname, substr): + return any( + [levelname == r.levelname and substr in r.message for r in caplog.records] + ) + + +@pytest.fixture +def src(): + return ECB() + + +@pytest.fixture +def type(src): + return src.types()[0] + + +def test_normalizesymbol(src): + assert src.normalizesymbol("eur") == "EUR" + assert src.normalizesymbol("symbol") == "SYMBOL" + + +@pytest.mark.live +def test_known_pair(src, type): + series = src.fetch(Series("EUR", "AUD", type, "2021-01-11", "2021-01-22")) + assert series.prices[0] == Price("2021-01-11", Decimal("1.5783")) + assert series.prices[-1] == Price("2021-01-22", Decimal("1.577")) + assert len(series.prices) == 10 + + +@pytest.mark.live +def test_long_hist_from_start(src, type): + series = src.fetch(Series("EUR", "AUD", type, src.start(), "2021-07-01")) + assert series.prices[0] == Price("1999-01-04", Decimal("1.91")) + assert series.prices[-1] == Price("2021-07-01", Decimal("1.5836")) + assert len(series.prices) == 5759 + + +@pytest.mark.live +def test_from_before_start(src, type): + series = src.fetch(Series("EUR", "AUD", type, "1998-12-01", "1999-01-10")) + assert series.prices[0] == Price("1999-01-04", Decimal("1.91")) + assert series.prices[-1] == Price("1999-01-08", Decimal("1.8406")) + assert len(series.prices) == 5 + + +@pytest.mark.live +def test_to_future(src, type): + series = src.fetch(Series("EUR", "AUD", type, "2021-07-01", "2100-01-01")) + assert len(series.prices) > 0 + + +@pytest.mark.live +def test_known_pair_no_data(src, type): + series = src.fetch(Series("EUR", "ROL", type, "2020-01-01", "2021-01-01")) + assert len(series.prices) == 0 + + +def test_non_eur_base(src, type, caplog): + with pytest.raises(SystemExit) as e: + src.fetch(Series("USD", "AUD", type, "2021-01-01", "2021-02-01")) + assert e.value.code == 1 + assert in_log(caplog, "CRITICAL", "Invalid pair") + + +@pytest.mark.xfail +@pytest.mark.live +def test_unknown_quote(src, type, caplog): + with pytest.raises(SystemExit) as e: + src.fetch(Series("EUR", "XZY", type, "2021-01-01", "2021-02-01")) + assert e.value.code == 1 + assert in_log(caplog, "CRITICAL", "Invalid pair") + + +@pytest.mark.xfail +def test_no_quote(src, type, caplog): + with pytest.raises(SystemExit) as e: + src.fetch(Series("EUR", "", type, "2021-01-01", "2021-02-01")) + assert e.value.code == 1 + assert in_log(caplog, "CRITICAL", "Invalid pair") + + +def test_unknown_pair(src, type, caplog): + with pytest.raises(SystemExit) as e: + src.fetch(Series("ABC", "XZY", type, "2021-01-01", "2021-02-01")) + assert e.value.code == 1 + assert in_log(caplog, "CRITICAL", "Invalid pair") diff --git a/tests/test_pricehist.py b/tests/test_pricehist.py deleted file mode 100644 index 114ed54..0000000 --- a/tests/test_pricehist.py +++ /dev/null @@ -1,5 +0,0 @@ -from pricehist import __version__ - - -def test_version(): - assert __version__ == "0.1.0"