From 6760d322637a3d875242a66e9c1a784866d7df1d Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Wed, 15 Jun 2022 08:05:11 +0300 Subject: Setup test fixtures and initial tests for web-UI --- guix.scm | 3 +-- manifest.scm | 3 ++- qc_app/entry.py | 4 +-- tests/conftest.py | 30 +++++++++++++++++++++ tests/qc_app/test_entry.py | 55 +++++++++++++++++++++++++++++++++++++++ tests/qc_app/test_parse.py | 12 +++++++++ tests/test_instance_dir/config.py | 11 ++++++++ 7 files changed, 113 insertions(+), 5 deletions(-) create mode 100644 tests/qc_app/test_entry.py create mode 100644 tests/qc_app/test_parse.py create mode 100644 tests/test_instance_dir/config.py diff --git a/guix.scm b/guix.scm index 35911ae..0c78fc1 100644 --- a/guix.scm +++ b/guix.scm @@ -36,8 +36,7 @@ python-pytest python-hypothesis)) (propagated-inputs - (list python-rq - python-magic)) + (list redis)) (synopsis "GeneNetwork Quality Control Application") (description "GeneNetwork qc is a quality control application for the data files that diff --git a/manifest.scm b/manifest.scm index 42d95bb..407834a 100644 --- a/manifest.scm +++ b/manifest.scm @@ -1,5 +1,6 @@ (specifications->manifest - (list "python" + (list "redis" + "python" "python-mypy" "python-redis" "python-flask" diff --git a/qc_app/entry.py b/qc_app/entry.py index d876bb7..7f67a33 100644 --- a/qc_app/entry.py +++ b/qc_app/entry.py @@ -24,7 +24,7 @@ def errors(request) -> Tuple[str, ...]: def __filetype_error__(): return ( ("Invalid file type provided.",) - if request.form["filetype"] not in ("average", "standard-error") + if request.form.get("filetype") not in ("average", "standard-error") else tuple()) def __file_missing_error__(): @@ -76,10 +76,10 @@ def zip_file_errors(filepath, upload_dir) -> Tuple[str, ...]: @entrybp.route("/", methods=["GET", "POST"]) def upload_file(): """Enables uploading the files""" - upload_dir = app.config["UPLOAD_FOLDER"] if request.method == "GET": return render_template("index.html") + upload_dir = app.config["UPLOAD_FOLDER"] request_errors = errors(request) if request_errors: for error in request_errors: diff --git a/tests/conftest.py b/tests/conftest.py index 6ef5374..70bbd37 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,40 @@ """Set up fixtures for tests""" +import os +import socket +import subprocess +from contextlib import closing import pytest +from qc_app import create_app from quality_control.parsing import strain_names @pytest.fixture(scope="session") def strains(): """Parse the strains once every test session""" return strain_names("etc/strains.csv") + +def is_port_in_use(port: int) -> bool: + "Check whether `port` is in use" + with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as sckt: + return sckt.connect_ex(("localhost", port)) == 0 + +@pytest.fixture(scope="session") +def redis_server(): + "Fixture to launch a new redis instance and return appropriate URI" + port = next(# pylint: disable=[stop-iteration-return] + port for port in range(6379,65535) if not is_port_in_use(port)) + redis_path = f"{os.getenv('GUIX_ENVIRONMENT')}/bin/redis-server" + command = [redis_path, "--port", str(port)] + process = subprocess.Popen(command) # pylint: disable=[consider-using-with] + yield f"redis://localhost:{port}" + process.kill() + +@pytest.fixture(scope="module") +def client(redis_server): # pylint: disable=[redefined-outer-name] + "Fixture for test client" + app = create_app(f"{os.path.abspath('.')}/tests/test_instance_dir") + app.config.update({ + "REDIS_URL": redis_server + }) + yield app.test_client() diff --git a/tests/qc_app/test_entry.py b/tests/qc_app/test_entry.py new file mode 100644 index 0000000..d2908ee --- /dev/null +++ b/tests/qc_app/test_entry.py @@ -0,0 +1,55 @@ +"""Test the entry module in the web-ui""" +import io + +def test_basic_elements_present_in_index_page(client): + """ + GIVEN: A flask application testing client + WHEN: the index page is requested with the "POST" method and no datat + THEN: verify that the response contains error notifications + """ + response = client.get("/") + assert response.status_code == 200 + ## form present + assert b'
' in response.data + ## filetype elements + assert b'select file' in response.data + assert b'Invalid file type provided.' + in response.data) + assert ( + b'No file was uploaded.' + in response.data) + +def test_post_with_correct_data(client): + """ + GIVEN: A flask application testing client + WHEN: the index page is requested with the "POST" method and with the + appropriate data provided + THEN: .... + """ + with open("tests/test_data/no_data_errors.tsv", "br") as test_file: + response = client.post( + "/", data={ + "filetype": "average", + "qc_text_file": (io.BytesIO(test_file.read()), "no_data_errors.tsv") + }) + + assert response.status_code == 302 + assert b'Redirecting...' in response.data + assert ( + b'/parse/parse?filename=no_data_errors.tsv&filetype=average' + in response.data) diff --git a/tests/qc_app/test_parse.py b/tests/qc_app/test_parse.py new file mode 100644 index 0000000..41d2c26 --- /dev/null +++ b/tests/qc_app/test_parse.py @@ -0,0 +1,12 @@ +import pytest + +def test_parse_with_existing_file(client, monkeypatch): + monkeypatch.setattr( + "qc_app.jobs.uuid4", lambda : "934c55d8-396e-4959-90e1-2698e9205758") + resp = client.get( + "/parse/parse?filename=no_data_errors.tsv&filetype=average") + print(resp.status) + print(resp.data) + assert resp.status_code == 302 + assert b'Redirecting...' in resp.data + assert b'/parse/status/934c55d8-396e-4959-90e1-2698e9205758' in resp.data diff --git a/tests/test_instance_dir/config.py b/tests/test_instance_dir/config.py new file mode 100644 index 0000000..2ee569b --- /dev/null +++ b/tests/test_instance_dir/config.py @@ -0,0 +1,11 @@ +""" +The tests configuration file. +""" + +import os + +LOG_LEVEL = os.getenv("LOG_LEVEL", "WARNING") +SECRET_KEY = b"" +UPLOAD_FOLDER = "/tmp/qc_app_files" +REDIS_URL = "redis://" +JOBS_TTL_SECONDS = 600 # 10 minutes -- cgit v1.2.3