diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml index e9f02d5..1845d98 100644 --- a/.github/workflows/publish-package.yml +++ b/.github/workflows/publish-package.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 626bf21..96c6849 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -2,9 +2,12 @@ name: Run tests # Run test at each push to main, if changes to package or tests files on: workflow_dispatch: + pull_request: + branches: [ main ] push: branches: [ main ] paths: + - '*.py' - 'requirements.txt' - 'app/**' - 'tests/**' @@ -12,11 +15,11 @@ on: jobs: - tests: + tests_python: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8, 3.9] + python-version: [3.7, 3.8, 3.9] steps: - uses: actions/checkout@v2 @@ -33,12 +36,23 @@ jobs: - name: Check code style with flake8 (lint) run: | - # warnings if there are Python syntax errors or undefined names + # warnings if there are Python syntax errors or undefined names # (remove --exit-zero to fail when syntax error) flake8 . --count --exit-zero --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest - run: pytest + run: pytest -v + + test_docker_build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Docker build + run: docker build -t libretranslate . + + - name: Docker build with some models + run: docker build -t libretranslate --build-arg models=en,es . diff --git a/app/app.py b/app/app.py index 428de95..a06b1f1 100644 --- a/app/app.py +++ b/app/app.py @@ -239,7 +239,7 @@ def create_app(args): return render_template("javascript-licenses.html") - @app.route("/languages", methods=["GET", "POST"]) + @app.get("/languages") @limiter.exempt def langs(): """ diff --git a/app/main.py b/app/main.py index 62d4d6e..1545a77 100644 --- a/app/main.py +++ b/app/main.py @@ -6,7 +6,7 @@ from app.app import create_app from app.default_values import DEFAULT_ARGUMENTS as DEFARGS -def main(): +def get_args(): parser = argparse.ArgumentParser( description="LibreTranslate - Free and Open Source Translation API" ) @@ -106,13 +106,18 @@ def main(): "--suggestions", default=DEFARGS['SUGGESTIONS'], action="store_true", help="Allow user suggestions" ) parser.add_argument( - "--disable-files-translation", default=DEFARGS['DISABLE_FILES_TRANSLATION'], action="store_true", help="Disable files translation" + "--disable-files-translation", default=DEFARGS['DISABLE_FILES_TRANSLATION'], action="store_true", + help="Disable files translation" ) parser.add_argument( "--disable-web-ui", default=DEFARGS['DISABLE_WEB_UI'], action="store_true", help="Disable web ui" ) - args = parser.parse_args() + return parser.parse_args() + + +def main(): + args = get_args() app = create_app(args) if sys.argv[0] == '--wsgi': diff --git a/setup.py b/setup.py index 970a006..38b0c66 100644 --- a/setup.py +++ b/setup.py @@ -22,16 +22,15 @@ setup( ], }, - python_requires='>=3.6.0', + python_requires='>=3.7.0', long_description=open('README.md').read(), long_description_content_type="text/markdown", install_requires=open("requirements.txt", "r").readlines(), - tests_require=['pytest==5.2.0'], + tests_require=['pytest==7.1.1'], setup_requires=['pytest-runner'], classifiers=[ "License :: OSI Approved :: GNU Affero General Public License v3 ", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9" diff --git a/tests/test_api/__init__.py b/tests/test_api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_api/conftest.py b/tests/test_api/conftest.py new file mode 100644 index 0000000..8ce03d1 --- /dev/null +++ b/tests/test_api/conftest.py @@ -0,0 +1,20 @@ +import sys +import pytest + +from app.app import create_app +from app.default_values import DEFAULT_ARGUMENTS +from app.main import get_args + + +@pytest.fixture() +def app(): + sys.argv = [''] + DEFAULT_ARGUMENTS['LOAD_ONLY'] = "en,es" + app = create_app(get_args()) + + yield app + + +@pytest.fixture() +def client(app): + return app.test_client() diff --git a/tests/test_api/test_api_detect_language.py b/tests/test_api/test_api_detect_language.py new file mode 100644 index 0000000..d85600c --- /dev/null +++ b/tests/test_api/test_api_detect_language.py @@ -0,0 +1,26 @@ +import json + + +def test_api_detect_language(client): + response = client.post("/detect", data={ + "q": "Hello" + }) + response_json = json.loads(response.data) + + assert "confidence" in response_json[0] and "language" in response_json[0] + assert len(response_json) >= 1 + assert response.status_code == 200 + + +def test_api_detect_language_must_fail_without_parameters(client): + response = client.post("/detect") + response_json = json.loads(response.data) + + assert "error" in response_json + assert response.status_code == 400 + + +def test_api_detect_language_must_fail_bad_request_type(client): + response = client.get("/detect") + + assert response.status_code == 405 diff --git a/tests/test_api/test_api_frontend_settings.py b/tests/test_api/test_api_frontend_settings.py new file mode 100644 index 0000000..8c3070a --- /dev/null +++ b/tests/test_api/test_api_frontend_settings.py @@ -0,0 +1,4 @@ +def test_api_get_frontend_settings(client): + response = client.get("/frontend/settings") + + assert response.status_code == 200 diff --git a/tests/test_api/test_api_get_languages.py b/tests/test_api/test_api_get_languages.py new file mode 100644 index 0000000..d625174 --- /dev/null +++ b/tests/test_api/test_api_get_languages.py @@ -0,0 +1,16 @@ +import json + + +def test_api_get_languages(client): + response = client.get("/languages") + response_json = json.loads(response.data) + + assert "code" in response_json[0] and "name" in response_json[0] + assert len(response_json) >= 1 + assert response.status_code == 200 + + +def test_api_get_languages_must_fail_bad_request_type(client): + response = client.post("/languages") + + assert response.status_code == 405 diff --git a/tests/test_api/test_api_spec.py b/tests/test_api/test_api_spec.py new file mode 100644 index 0000000..3922c44 --- /dev/null +++ b/tests/test_api/test_api_spec.py @@ -0,0 +1,10 @@ +def test_api_get_spec(client): + response = client.get("/spec") + + assert response.status_code == 200 + + +def test_api_get_spec_must_fail_bad_request_type(client): + response = client.post("/spec") + + assert response.status_code == 405 diff --git a/tests/test_api/test_api_translate.py b/tests/test_api/test_api_translate.py new file mode 100644 index 0000000..96f8632 --- /dev/null +++ b/tests/test_api/test_api_translate.py @@ -0,0 +1,61 @@ +import json + + +def test_api_translate(client): + response = client.post("/translate", data={ + "q": "Hello", + "source": "en", + "target": "es", + "format": "text" + }) + + response_json = json.loads(response.data) + + assert "translatedText" in response_json + assert response.status_code == 200 + + +def test_api_translate_batch(client): + + response = client.post("/translate", json={ + "q": ["Hello", "World"], + "source": "en", + "target": "es", + "format": "text" + }) + + response_json = json.loads(response.data) + + assert "translatedText" in response_json + assert isinstance(response_json["translatedText"], list) + assert len(response_json["translatedText"]) == 2 + assert response.status_code == 200 + + +def test_api_translate_unsupported_language(client): + response = client.post("/translate", data={ + "q": "Hello", + "source": "en", + "target": "zz", + "format": "text" + }) + + response_json = json.loads(response.data) + + assert "error" in response_json + assert "zz is not supported" == response_json["error"] + assert response.status_code == 400 + + +def test_api_translate_missing_parameter(client): + response = client.post("/translate", data={ + "source": "en", + "target": "es", + "format": "text" + }) + + response_json = json.loads(response.data) + + assert "error" in response_json + assert "Invalid request: missing q parameter" == response_json["error"] + assert response.status_code == 400 diff --git a/tests/test_init.py b/tests/test_init.py index c1dbfe1..dacffef 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -4,6 +4,6 @@ from argostranslate import package def test_boot_argos(): """Test Argos translate models initialization""" - boot() + boot(["en", "es"]) - assert len(package.get_installed_packages()) > 2 + assert len(package.get_installed_packages()) >= 2