From d67319e699dc928e881159a1c3cc0189296864c1 Mon Sep 17 00:00:00 2001 From: Alexander Piskun Date: Sun, 11 Dec 2022 21:59:10 +0300 Subject: [PATCH] py: coverage, `get_mimetype_id` fix --- .codecov.yml | 14 ++ .github/workflows/py_analysis-coverage.yml | 271 +++++++++++++++++++++ .gitignore | 1 - .nextcloudignore | 1 + README.md | 7 +- nc_py_api/db_api.py | 2 + nc_py_api/db_connectors.py | 2 +- nc_py_api/db_requests.py | 2 +- nc_py_api/mimetype.py | 6 +- pyproject.toml | 3 +- setup.cfg | 7 + tests/nc_py_api/mimetype_test.py | 13 + tests/nc_py_api/occ_test.py | 53 ++++ 13 files changed, 374 insertions(+), 8 deletions(-) create mode 100644 .codecov.yml create mode 100644 tests/nc_py_api/mimetype_test.py create mode 100644 tests/nc_py_api/occ_test.py diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..cb401da7 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,14 @@ +comment: + require_changes: true + layout: "diff, files" + +coverage: + status: + project: + default: + target: auto + threshold: 2% + patch: + default: + target: auto + threshold: 2% diff --git a/.github/workflows/py_analysis-coverage.yml b/.github/workflows/py_analysis-coverage.yml index 27739eee..2d9552ba 100644 --- a/.github/workflows/py_analysis-coverage.yml +++ b/.github/workflows/py_analysis-coverage.yml @@ -18,6 +18,9 @@ on: - 'pyproject.toml' workflow_dispatch: +env: + APP_NAME: cloud_py_api + jobs: analysis: runs-on: macos-12 @@ -46,3 +49,271 @@ jobs: - name: Run Analysis run: pre-commit run --all-files --verbose --show-diff-on-failure + + tests-pgsql: + needs: [analysis] + runs-on: ubuntu-22.04 + name: ${{ matrix.nextcloud }} • PHP ${{ matrix.php-version }} • PgSQL ${{ matrix.pgsql-version }} + if: "!contains(github.event.head_commit.message, '[docs]')" + strategy: + fail-fast: false + matrix: + nextcloud: [ "25.0.2" ] + php-version: [ "7.4", "8.0" ] + pgsql-version: [ "13", "14", "15" ] + + services: + postgres: + image: postgres:${{ matrix.pgsql-version }} + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: rootpassword + POSTGRES_DB: nextcloud + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Set up php ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, fileinfo, intl, pdo_pgsql, zip, gd + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: cache-nextcloud + id: nextcloud_setup + uses: actions/cache@v3 + with: + path: nextcloud-${{ matrix.nextcloud }}.tar.bz2 + key: ${{ matrix.nextcloud }} + + - name: Download Nextcloud + if: steps.nextcloud_setup.outputs.cache-hit != 'true' + run: wget -q https://download.nextcloud.com/server/releases/nextcloud-${{ matrix.nextcloud }}.tar.bz2 + + - name: Set up Nextcloud + run: | + tar -xjf nextcloud-${{ matrix.nextcloud }}.tar.bz2 --strip-components 1 + mkdir data + php occ maintenance:install --verbose --database=pgsql --database-name=nextcloud \ + --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword \ + --admin-user admin --admin-pass adminpassword + php occ config:system:set debug --value=true --type=boolean + php -S localhost:8080 & + + - uses: actions/checkout@v3 + with: + path: apps/${{ env.APP_NAME }} + + - name: Enable App + run: php occ app:enable ${{ env.APP_NAME }} + + - name: Generate coverage report + working-directory: apps/${{ env.APP_NAME }} + run: | + python3 -m pip -v install ".[dev]" + coverage run -m pytest -s && coverage xml && coverage html + env: + SERVER_ROOT: "../.." + CPA_LOGLEVEL: debug + + - name: HTML coverage to artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage_${{ matrix.nextcloud }}_${{ matrix.php-version }}_${{ matrix.pgsql-version }} + path: apps/${{ env.APP_NAME }}/htmlcov + if-no-files-found: error + + - name: Upload report to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: apps/${{ env.APP_NAME }}/coverage.xml + fail_ci_if_error: true + verbose: true + + tests-mysql: + needs: [analysis] + runs-on: ubuntu-22.04 + name: ${{ matrix.nextcloud }} • PHP ${{ matrix.php-version }} • MySQL ${{ matrix.mysql-version }} + if: "!contains(github.event.head_commit.message, '[docs]')" + strategy: + fail-fast: false + matrix: + nextcloud: [ "25.0.2" ] + php-version: [ "7.4", "8.0" ] + mysql-version: [ "8" ] + + services: + mysql: + image: mysql:${{ matrix.mysql-version }} + env: + MYSQL_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: nextcloud + options: >- + --health-cmd mysqladmin ping + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + + steps: + - name: Set up php ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, fileinfo, intl, pdo_mysql, zip, gd + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: cache-nextcloud + id: nextcloud_setup + uses: actions/cache@v3 + with: + path: nextcloud-${{ matrix.nextcloud }}.tar.bz2 + key: ${{ matrix.nextcloud }} + + - name: Download Nextcloud + if: steps.nextcloud_setup.outputs.cache-hit != 'true' + run: wget -q https://download.nextcloud.com/server/releases/nextcloud-${{ matrix.nextcloud }}.tar.bz2 + + - name: Set up Nextcloud + run: | + tar -xjf nextcloud-${{ matrix.nextcloud }}.tar.bz2 --strip-components 1 + mkdir data + php occ maintenance:install --verbose --database=mysql --database-name=nextcloud \ + --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword \ + --admin-user admin --admin-pass adminpassword + php occ config:system:set debug --value=true --type=boolean + php -S localhost:8080 & + + - uses: actions/checkout@v3 + with: + path: apps/${{ env.APP_NAME }} + + - name: Enable App + run: php occ app:enable ${{ env.APP_NAME }} + + - name: Generate coverage report + working-directory: apps/${{ env.APP_NAME }} + run: | + python3 -m pip -v install ".[dev]" + coverage run -m pytest -s && coverage xml && coverage html + env: + SERVER_ROOT: "../.." + CPA_LOGLEVEL: debug + + - name: HTML coverage to artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage_${{ matrix.nextcloud }}_${{ matrix.php-version }}_${{ matrix.mysql-version }} + path: apps/${{ env.APP_NAME }}/htmlcov + if-no-files-found: error + + - name: Upload report to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: apps/${{ env.APP_NAME }}/coverage.xml + fail_ci_if_error: true + verbose: true + + tests-mariadb: + needs: [analysis] + runs-on: ubuntu-22.04 + name: ${{ matrix.nextcloud }} • PHP ${{ matrix.php-version }} • Maria ${{ matrix.mariadb-version }} + if: "!contains(github.event.head_commit.message, '[docs]')" + strategy: + fail-fast: false + matrix: + nextcloud: [ "25.0.2" ] + php-version: [ "7.4", "8.0" ] + mariadb-version: [ "10.3", "10.6", "10.10" ] + + services: + mariadb: + image: mariadb:${{ matrix.mariadb-version }} + env: + MARIADB_ROOT_PASSWORD: rootpassword + MYSQL_DATABASE: nextcloud + options: >- + --health-cmd mysqladmin ping + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 3306:3306 + + steps: + - name: Set up php ${{ matrix.php-version }} + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-version }} + extensions: mbstring, fileinfo, intl, pdo_mysql, zip, gd + + - uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: cache-nextcloud + id: nextcloud_setup + uses: actions/cache@v3 + with: + path: nextcloud-${{ matrix.nextcloud }}.tar.bz2 + key: ${{ matrix.nextcloud }} + + - name: Download Nextcloud + if: steps.nextcloud_setup.outputs.cache-hit != 'true' + run: wget -q https://download.nextcloud.com/server/releases/nextcloud-${{ matrix.nextcloud }}.tar.bz2 + + - name: Set up Nextcloud + run: | + tar -xjf nextcloud-${{ matrix.nextcloud }}.tar.bz2 --strip-components 1 + mkdir data + php occ maintenance:install --verbose --database=mysql --database-name=nextcloud \ + --database-host=127.0.0.1 --database-user=root --database-pass=rootpassword \ + --admin-user admin --admin-pass adminpassword + php occ config:system:set debug --value=true --type=boolean + php -S localhost:8080 & + + - uses: actions/checkout@v3 + with: + path: apps/${{ env.APP_NAME }} + + - name: Enable App + run: php occ app:enable ${{ env.APP_NAME }} + + - name: Generate coverage report + working-directory: apps/${{ env.APP_NAME }} + run: | + python3 -m pip -v install ".[dev]" + coverage run -m pytest -s && coverage xml && coverage html + env: + SERVER_ROOT: "../.." + CPA_LOGLEVEL: debug + + - name: HTML coverage to artifacts + uses: actions/upload-artifact@v3 + with: + name: coverage_${{ matrix.nextcloud }}_${{ matrix.php-version }}_${{ matrix.mariadb-version }} + path: apps/${{ env.APP_NAME }}/htmlcov + if-no-files-found: error + + - name: Upload report to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + file: apps/${{ env.APP_NAME }}/coverage.xml + fail_ci_if_error: true + verbose: true diff --git a/.gitignore b/.gitignore index 638e657b..9ea45cc1 100644 --- a/.gitignore +++ b/.gitignore @@ -79,7 +79,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/.nextcloudignore b/.nextcloudignore index d86e775a..9e6ea71e 100644 --- a/.nextcloudignore +++ b/.nextcloudignore @@ -37,3 +37,4 @@ tests /proto /tmp .readthedocs.yaml +.codecov.yml diff --git a/README.md b/README.md index fbae776e..55fb335f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # Nextcloud Python Framework +[![(Py)Analysis & Coverage](https://github.com/cloud-py-api/cloud_py_api/actions/workflows/py_analysis-coverage.yml/badge.svg)](https://github.com/cloud-py-api/cloud_py_api/actions/workflows/py_analysis-coverage.yml) +![PythonVersion](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11-blue) +![impl](https://img.shields.io/pypi/implementation/nc_py_api) +![pypi](https://img.shields.io/pypi/v/nc_py_api.svg) + Framework(App) for Nextcloud to develop apps, that using Python. Consists of PHP part(**cloud_py_api app**) and a Python module(**nc-py-api**). @@ -19,7 +24,7 @@ In your Nextcloud, simply enable the `cloud_py_api` app through the Apps managem The Nextcloud `cloud_py_api` app supports Nextcloud version 25 and higher. -#### More information can be found on [Wiki page](https://github.com/cloud_py_api/cloud_py_api/wiki) +#### More information can be found on [Wiki page](https://github.com/cloud-py-api/cloud_py_api/wiki) ## Maintainers diff --git a/nc_py_api/db_api.py b/nc_py_api/db_api.py index 27449098..1739b184 100644 --- a/nc_py_api/db_api.py +++ b/nc_py_api/db_api.py @@ -54,6 +54,7 @@ def execute_fetchall(query: str, args=None, connection_id: int = 0) -> list: break except Exception: # noqa # pylint: disable=broad-except log.exception("DB: Exception during executing fetchall.") + log.debug(query) close_connection(connection_id) return result @@ -87,5 +88,6 @@ def execute_commit(query: str, args=None, connection_id: int = 0) -> int: break except Exception: # noqa # pylint: disable=broad-except log.exception("DB: Exception during executing commit.") + log.debug(query) close_connection(connection_id) return result diff --git a/nc_py_api/db_connectors.py b/nc_py_api/db_connectors.py index 9f7e2f31..af89a45e 100644 --- a/nc_py_api/db_connectors.py +++ b/nc_py_api/db_connectors.py @@ -58,7 +58,7 @@ def create_connection(config: dict, log_errors=True): def connection_test(config: dict, log_errors=False) -> bool: - if environ.get("LOGLEVEL", "").upper() == "DEBUG": + if environ.get("CPA_LOGLEVEL", "").upper() == "DEBUG": log_errors = True connection = create_connection(config, log_errors=log_errors) if connection is not None: diff --git a/nc_py_api/db_requests.py b/nc_py_api/db_requests.py index a13fad64..317cc67f 100644 --- a/nc_py_api/db_requests.py +++ b/nc_py_api/db_requests.py @@ -60,7 +60,7 @@ def get_storages_info(num_id: Optional[int] = None) -> list: def get_mimetype_id(mimetype: str) -> int: """For string mimetype returns it number representation.""" - query = f"SELECT id FROM {TABLES.mimetypes} WHERE mimetype = {mimetype};" + query = f"SELECT id FROM {TABLES.mimetypes} WHERE mimetype = '{mimetype}';" result = execute_fetchall(query) if not result: return 0 diff --git a/nc_py_api/mimetype.py b/nc_py_api/mimetype.py index 7f86e63c..cd9914d3 100644 --- a/nc_py_api/mimetype.py +++ b/nc_py_api/mimetype.py @@ -1,5 +1,5 @@ from .db_requests import get_mimetype_id -DIR = get_mimetype_id("'httpd/unix-directory'") -IMAGE = get_mimetype_id("'image'") -VIDEO = get_mimetype_id("'video'") +DIR = get_mimetype_id("httpd/unix-directory") +IMAGE = get_mimetype_id("image") +VIDEO = get_mimetype_id("video") diff --git a/pyproject.toml b/pyproject.toml index a43f2b62..090f88e5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,8 +50,9 @@ strict_optional = true [tool.pytest.ini_options] minversion = "6.0" testpaths = [ - "tests", + "tests/nc_py_api", ] filterwarnings = [ "ignore::DeprecationWarning", ] +log_cli = true diff --git a/setup.cfg b/setup.cfg index 52840318..81033dd5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,13 @@ install_requires = pg8000 pymysql +[options.extras_require] +dev = + pytest + pre-commit + pylint + coverage + [flake8] max-line-length = 120 target-version = ["py39"] diff --git a/tests/nc_py_api/mimetype_test.py b/tests/nc_py_api/mimetype_test.py new file mode 100644 index 00000000..ec31b9da --- /dev/null +++ b/tests/nc_py_api/mimetype_test.py @@ -0,0 +1,13 @@ +import pytest + +from nc_py_api import get_mimetype_id + + +@pytest.mark.parametrize("mimetype", ["httpd", "httpd/unix-directory", "application", "text", "image", "video"]) +def test_get_mimetype_id(mimetype): + assert get_mimetype_id(mimetype) + + +@pytest.mark.parametrize("mimetype", ["", "invalid_mime", None, "'invalid_mime'"]) +def test_get_mimetype_id_invalid(mimetype): + assert not get_mimetype_id(mimetype) diff --git a/tests/nc_py_api/occ_test.py b/tests/nc_py_api/occ_test.py new file mode 100644 index 00000000..aac667fb --- /dev/null +++ b/tests/nc_py_api/occ_test.py @@ -0,0 +1,53 @@ +from unittest import mock + +import nc_py_api + + +def test_occ_call(): + assert nc_py_api.occ_call("--version").decode("utf-8").lower().find("nextcloud ") != -1 + + +def test_occ_call_invalid_command(): + assert nc_py_api.occ_call("invalid command") is None + + +def test_occ_call_with_param(): + assert nc_py_api.occ_call("config:system:get", "installed").decode("utf-8").lower() == "true\n" + + +def test_occ_call_decode(): + assert nc_py_api.occ_call_decode("--version").lower().find("nextcloud ") != -1 + + +def test_occ_call_decode_invalid_command(): + assert nc_py_api.occ_call_decode("invalid command") is None + + +def test_occ_call_decode_with_param(): + assert nc_py_api.occ_call_decode("config:system:get", "installed").lower() == "true" + + +def test_get_cloud_app_config_value(): + assert nc_py_api.get_cloud_app_config_value("core", "vendor") == "nextcloud" + + +def test_get_cloud_app_config_invalid_name(): + assert nc_py_api.get_cloud_app_config_value("core", "invalid_name") is None + + +def test_get_cloud_app_config_default_value(): + assert nc_py_api.get_cloud_app_config_value("core", "invalid_name", default=3) == 3 + + +@mock.patch("nc_py_api.occ._PHP_PATH", "no_php") +@mock.patch("nc_py_api.log.cpa_logger.exception") +def test_no_php_log_on(log_exception_mock): + assert nc_py_api.occ_call("--version") is None + log_exception_mock.assert_called_once_with("php_call exception:") + + +@mock.patch("nc_py_api.occ._PHP_PATH", "no_php") +@mock.patch("nc_py_api.log.cpa_logger.exception") +def test_no_php_log_off(log_exception_mock): + assert nc_py_api.occ_call("--version", log_error=False) is None + log_exception_mock.assert_not_called()