From e668f76b4cdbdea654977abd5515b696f23d7073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 12:07:27 +0100 Subject: [PATCH 1/9] requirements: add setuptools --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ca6d6b6..6079683 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests~=2.32.3 -python-dotenv~=1.0.1 \ No newline at end of file +python-dotenv~=1.0.1 +setuptools~=75.8.0 \ No newline at end of file From 886004a20aeeff4b757c96160ffefa1b35a70fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 12:07:46 +0100 Subject: [PATCH 2/9] test: introduce basic tests --- requirements_dev.txt | 3 ++ test/__init__.py | 0 test/test_superfaktura_api.py | 68 +++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+) create mode 100644 requirements_dev.txt create mode 100644 test/__init__.py create mode 100644 test/test_superfaktura_api.py diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000..2abf170 --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,3 @@ +requests~=2.32.3 +python-dotenv~=1.0.1 +pytest~=8.3.5 diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_superfaktura_api.py b/test/test_superfaktura_api.py new file mode 100644 index 0000000..2bf96b5 --- /dev/null +++ b/test/test_superfaktura_api.py @@ -0,0 +1,68 @@ +import os +import pytest +import requests +from unittest.mock import patch, mock_open +from superfaktura.superfaktura_api import ( + SuperFakturaAPI, + SuperFakturaAPIException, + SuperFakturaAPIMissingCredentialsException, +) + +@pytest.fixture +def api(): + with patch.dict(os.environ, { + "SUPERFAKTURA_API_KEY": "test_key", + "SUPERFAKTURA_API_URL": "https://api.superfaktura.cz", + "SUPERFAKTURA_API_EMAIL": "test_email", + "SUPERFAKTURA_API_COMPANY_ID": "test_company_id" + }): + return SuperFakturaAPI() + +def test_missing_credentials(): + with patch.dict(os.environ, {}, clear=True): + with pytest.raises(SuperFakturaAPIMissingCredentialsException): + SuperFakturaAPI() + +def test_get(api): + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.json.return_value = {"data": "test"} + response = api.get("test_endpoint") + assert response == {"data": "test"} + +def test_get_failure(api): + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 404 + with pytest.raises(SuperFakturaAPIException): + api.get("test_endpoint") + +def test_download(api): + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.content = b"test_content" + with patch("builtins.open", mock_open()) as mock_file: + with open("test_file", "wb") as f: + api.download("test_endpoint", f) + mock_file().write.assert_called_once_with(b"test_content") + +def test_download_failure(api): + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 404 + with patch("builtins.open", mock_open()) as mock_file: + with open("test_file", "wb") as f: + with pytest.raises(SuperFakturaAPIException): + api.download("test_endpoint", f) + +def test_post(api): + with patch("requests.post") as mock_post: + mock_post.return_value.status_code = 200 + mock_post.return_value.json.return_value = {"data": "test"} + response = api.post("test_endpoint", '{"name": "Example"}') + assert response == {"data": "test"} + +def test_post_failure(api): + with patch("requests.post") as mock_post: + mock_post.return_value.status_code = 404 + mock_post.return_value.json.return_value = {"error": "not found"} + with pytest.raises(SuperFakturaAPIException): + api.post("test_endpoint", '{"name": "Example"}') \ No newline at end of file From e2f89cf75411c3517eb24615b7890b954ba773cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 13:00:04 +0100 Subject: [PATCH 3/9] requirements_dev: add pytest-cov --- requirements_dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements_dev.txt b/requirements_dev.txt index 2abf170..a34cc3b 100644 --- a/requirements_dev.txt +++ b/requirements_dev.txt @@ -1,3 +1,4 @@ requests~=2.32.3 python-dotenv~=1.0.1 pytest~=8.3.5 +pytest-cov From 16cb1b39ba6eba331657d5f3ee27298061be5afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 13:00:58 +0100 Subject: [PATCH 4/9] ga: add workflow for pytest --- .github/workflows/pytest.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/pytest.yml diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..d1559f4 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,29 @@ +name: PyTest + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.10' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements_dev.txt + - name: Test with pytest + run: | + pip install pytest pytest-cov + pytest test/ --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html + - name: Upload pytest test results + uses: actions/upload-artifact@v4 + with: + name: pytest-results + path: junit/test-results.xml + # Use always() to always run this step to publish test results when there are test failures + if: ${{ always() }} From 5b50f2c3eb006097d56077a1fb98ff60687303c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 13:15:43 +0100 Subject: [PATCH 5/9] ga: fix workflow for pylint --- .github/workflows/pylint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index f9dcc8e..56cb694 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -20,7 +20,7 @@ jobs: pip install pylint - name: pylint run: | - pylint $(git ls-files '*.py') --fail-under=9.0 --output-format=json > pylint-report.json + pylint $(git ls-files '*.py' | grep -v '^test/') --fail-under=9.0 --output-format=json > pylint-report.json - name: Generate Pylint Score id: pylint_score From 45259f8f27fd50166c6930827789feedda770c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Wed, 5 Mar 2025 22:24:27 +0100 Subject: [PATCH 6/9] Update pytest.yml --- .github/workflows/pytest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index d1559f4..45bcc80 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -18,8 +18,8 @@ jobs: pip install -r requirements_dev.txt - name: Test with pytest run: | - pip install pytest pytest-cov - pytest test/ --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html + mkdir -p junit + pytest test/ --doctest-modules --junitxml=junit/test-results.xml --cov=superfaktura --cov-report=xml --cov-report=html - name: Upload pytest test results uses: actions/upload-artifact@v4 with: From d2cbbd059685145d8387d1f87e8c4beaa8140dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Tue, 11 Mar 2025 21:09:32 +0100 Subject: [PATCH 7/9] test_superfaktura_api: add test invalid json as repsonse from the server --- test/test_superfaktura_api.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/test/test_superfaktura_api.py b/test/test_superfaktura_api.py index 2bf96b5..dd034fe 100644 --- a/test/test_superfaktura_api.py +++ b/test/test_superfaktura_api.py @@ -1,7 +1,9 @@ import os import pytest +from unittest.mock import patch, mock_open, MagicMock + import requests -from unittest.mock import patch, mock_open + from superfaktura.superfaktura_api import ( SuperFakturaAPI, SuperFakturaAPIException, @@ -48,7 +50,7 @@ def test_download(api): def test_download_failure(api): with patch("requests.get") as mock_get: mock_get.return_value.status_code = 404 - with patch("builtins.open", mock_open()) as mock_file: + with patch("builtins.open", mock_open()): with open("test_file", "wb") as f: with pytest.raises(SuperFakturaAPIException): api.download("test_endpoint", f) @@ -65,4 +67,13 @@ def test_post_failure(api): mock_post.return_value.status_code = 404 mock_post.return_value.json.return_value = {"error": "not found"} with pytest.raises(SuperFakturaAPIException): - api.post("test_endpoint", '{"name": "Example"}') \ No newline at end of file + api.post("test_endpoint", '{"name": "Example"}') + +def test_get_invalid_json(api): + with patch("requests.get") as mock_get: + mock_response = MagicMock() + mock_response.status_code = 200 + mock_response.json.side_effect = requests.exceptions.JSONDecodeError("msg", "doc", 0) + mock_get.return_value = mock_response + with pytest.raises(SuperFakturaAPIException, match="Unable to decode response as JSON"): + api.get("test_endpoint") From 8d3b2589b0c8ed4ab391ee022067ea506d3f36b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Tue, 11 Mar 2025 21:16:39 +0100 Subject: [PATCH 8/9] test_superfaktura_api: add test download with not writable file descriptor --- test/test_superfaktura_api.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_superfaktura_api.py b/test/test_superfaktura_api.py index dd034fe..919140a 100644 --- a/test/test_superfaktura_api.py +++ b/test/test_superfaktura_api.py @@ -77,3 +77,13 @@ def test_get_invalid_json(api): mock_get.return_value = mock_response with pytest.raises(SuperFakturaAPIException, match="Unable to decode response as JSON"): api.get("test_endpoint") + +def test_download_not_writable_descriptor(api): + with patch("requests.get") as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.content = b"test_content" + mock_descriptor = MagicMock() + mock_descriptor.writable.return_value = False + with patch("builtins.open", mock_open()) as mock_file: + with pytest.raises(SuperFakturaAPIException, match=" is not writable"): + api.download("test_endpoint", mock_descriptor) From b41bf28a34f71df024af0a36060c260ebd5288ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20Kub=C3=AD=C4=8Dek?= Date: Tue, 11 Mar 2025 21:23:56 +0100 Subject: [PATCH 9/9] test_superfaktura_api: fix unnecessary mock file open --- test/test_superfaktura_api.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_superfaktura_api.py b/test/test_superfaktura_api.py index 919140a..c4e295e 100644 --- a/test/test_superfaktura_api.py +++ b/test/test_superfaktura_api.py @@ -84,6 +84,5 @@ def test_download_not_writable_descriptor(api): mock_get.return_value.content = b"test_content" mock_descriptor = MagicMock() mock_descriptor.writable.return_value = False - with patch("builtins.open", mock_open()) as mock_file: - with pytest.raises(SuperFakturaAPIException, match=" is not writable"): - api.download("test_endpoint", mock_descriptor) + with pytest.raises(SuperFakturaAPIException, match=" is not writable"): + api.download("test_endpoint", mock_descriptor)