From 6514e24770b5c43ee73739209de142f9d9dfa6b5 Mon Sep 17 00:00:00 2001 From: nucleogenic Date: Sat, 15 Oct 2022 03:30:08 +0100 Subject: [PATCH] Docker container for Pytest (#901) * Dockerize Pytest * Fix auto-delete warning in delete file test * Allow tests to be executed with a non-default home dir * Use hostname for Docker container only if tests executed in Docker * Update container entrypoint to pytest * Re-format tests with black * Define if the execution environment is a Docker container in env fixture * Skip unsupported host bridge attachment test in Docker --- docker/docker-compose.yml | 14 ++++++++++++++ docker/pytest/Dockerfile | 5 +++++ python/web/requirements-dev.txt | 1 + python/web/tests/api/conftest.py | 3 --- python/web/tests/api/test_devices.py | 5 ++++- python/web/tests/api/test_files.py | 12 +++++++----- python/web/tests/api/test_misc.py | 6 +++--- python/web/tests/api/test_settings.py | 8 ++++---- python/web/tests/conftest.py | 26 +++++++++++++++++++++----- 9 files changed, 59 insertions(+), 21 deletions(-) create mode 100644 docker/pytest/Dockerfile diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 4f150688..df572f19 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -55,3 +55,17 @@ services: "--log-level=${WEB_LOG_LEVEL:-info}", "--dev-mode" ] + + pytest: + container_name: pytest + image: rascsi:pytest + pull_policy: never + profiles: + - webui-tests + build: + context: .. + dockerfile: docker/pytest/Dockerfile + volumes: + - ../python/web:/src:delegated + working_dir: /src + entrypoint: "pytest" diff --git a/docker/pytest/Dockerfile b/docker/pytest/Dockerfile new file mode 100644 index 00000000..712b1cda --- /dev/null +++ b/docker/pytest/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.7-bullseye +ENV DOCKER=1 + +COPY python/web/requirements-dev.txt /requirements-dev.txt +RUN pip install -r /requirements-dev.txt diff --git a/python/web/requirements-dev.txt b/python/web/requirements-dev.txt index 9d96eb59..06ff7a82 100644 --- a/python/web/requirements-dev.txt +++ b/python/web/requirements-dev.txt @@ -3,3 +3,4 @@ pytest-httpserver==1.0.6 black==22.8.0 flake8==5.0.4 watchdog==2.1.9 +requests==2.28.1 diff --git a/python/web/tests/api/conftest.py b/python/web/tests/api/conftest.py index 82175bd3..6c665655 100644 --- a/python/web/tests/api/conftest.py +++ b/python/web/tests/api/conftest.py @@ -2,9 +2,6 @@ import pytest import uuid import warnings -CFG_DIR = "/home/pi/.config/rascsi" -IMAGES_DIR = "/home/pi/images" -AFP_DIR = "/home/pi/afpshare" SCSI_ID = 6 FILE_SIZE_1_MIB = 1048576 STATUS_SUCCESS = "success" diff --git a/python/web/tests/api/test_devices.py b/python/web/tests/api/test_devices.py index 5fb6f33c..f13c6887 100644 --- a/python/web/tests/api/test_devices.py +++ b/python/web/tests/api/test_devices.py @@ -81,7 +81,10 @@ def test_attach_image(http_client, create_test_image, detach_devices): ("Printer", {"type": "SCLP", "param_timeout": 60, "param_cmd": "lp -fart %f"}), ], ) -def test_attach_device(http_client, detach_devices, device_name, device_config): +def test_attach_device(env, http_client, detach_devices, device_name, device_config): + if env["is_docker"] and device_name == "Host Bridge": + pytest.skip("Test not supported in Docker environment.") + device_config["scsi_id"] = SCSI_ID device_config["unit"] = 0 diff --git a/python/web/tests/api/test_files.py b/python/web/tests/api/test_files.py index 2b19ed72..f538076c 100644 --- a/python/web/tests/api/test_files.py +++ b/python/web/tests/api/test_files.py @@ -3,7 +3,6 @@ import uuid import os from conftest import ( - IMAGES_DIR, SCSI_ID, FILE_SIZE_1_MIB, STATUS_SUCCESS, @@ -86,7 +85,7 @@ def test_copy_file(http_client, create_test_image, list_files, delete_file): # route("/files/delete", methods=["POST"]) def test_delete_file(http_client, create_test_image, list_files): - file_name = create_test_image() + file_name = create_test_image(auto_delete=False) response = http_client.post("/files/delete", data={"file_name": file_name}) @@ -210,7 +209,7 @@ def test_download_file(http_client, create_test_image): # route("/files/download_url", methods=["POST"]) -def test_download_url_to_dir(httpserver, http_client, list_files, delete_file): +def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file): file_name = str(uuid.uuid4()) http_path = f"/images/{file_name}" url = httpserver.url_for(http_path) @@ -236,7 +235,9 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file): assert response.status_code == 200 assert response_data["status"] == STATUS_SUCCESS assert file_name in list_files() - assert response_data["messages"][0]["message"] == f"{file_name} downloaded to {IMAGES_DIR}" + assert ( + response_data["messages"][0]["message"] == f"{file_name} downloaded to {env['images_dir']}" + ) # Cleanup delete_file(file_name) @@ -244,6 +245,7 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file): # route("/files/download_to_iso", methods=["POST"]) def test_download_url_to_iso( + env, httpserver, http_client, list_files, @@ -283,7 +285,7 @@ def test_download_url_to_iso( m = response_data["messages"] assert m[0]["message"] == 'Created CD-ROM ISO image with arguments "-hfs"' - assert m[1]["message"] == f"Saved image as: {IMAGES_DIR}/{iso_file_name}" + assert m[1]["message"] == f"Saved image as: {env['images_dir']}/{iso_file_name}" assert m[2]["message"] == f"Attached to SCSI ID {SCSI_ID}" # Cleanup diff --git a/python/web/tests/api/test_misc.py b/python/web/tests/api/test_misc.py index 39d173fa..5b6272f5 100644 --- a/python/web/tests/api/test_misc.py +++ b/python/web/tests/api/test_misc.py @@ -1,7 +1,6 @@ import uuid from conftest import ( - CFG_DIR, FILE_SIZE_1_MIB, STATUS_SUCCESS, ) @@ -46,7 +45,7 @@ def test_show_named_drive_presets(http_client): # route("/drive/cdrom", methods=["POST"]) -def test_create_cdrom_properties_file(http_client): +def test_create_cdrom_properties_file(env, http_client): file_name = f"{uuid.uuid4()}.iso" response = http_client.post( @@ -62,7 +61,7 @@ def test_create_cdrom_properties_file(http_client): assert response.status_code == 200 assert response_data["status"] == STATUS_SUCCESS assert response_data["messages"][0]["message"] == ( - f"File created: {CFG_DIR}/{file_name}.properties" + f"File created: {env['cfg_dir']}/{file_name}.properties" ) @@ -89,6 +88,7 @@ def test_create_image_with_properties_file(http_client, delete_file): # Cleanup delete_file(file_name) + # route("/sys/manpage", methods=["POST"]) def test_show_manpage(http_client): response = http_client.get("/sys/manpage?app=rascsi") diff --git a/python/web/tests/api/test_settings.py b/python/web/tests/api/test_settings.py index df6cc42c..e50612d0 100644 --- a/python/web/tests/api/test_settings.py +++ b/python/web/tests/api/test_settings.py @@ -1,7 +1,7 @@ import pytest import uuid -from conftest import CFG_DIR, STATUS_SUCCESS +from conftest import STATUS_SUCCESS # route("/language", methods=["POST"]) @@ -74,7 +74,7 @@ def test_show_logs(http_client): # route("/config/save", methods=["POST"]) # route("/config/load", methods=["POST"]) -def test_save_load_and_delete_configs(http_client): +def test_save_load_and_delete_configs(env, http_client): config_name = str(uuid.uuid4()) config_json_file = f"{config_name}.json" reserved_scsi_id = 0 @@ -96,7 +96,7 @@ def test_save_load_and_delete_configs(http_client): assert response.status_code == 200 assert response_data["status"] == STATUS_SUCCESS assert response_data["messages"][0]["message"] == ( - f"File created: {CFG_DIR}/{config_json_file}" + f"File created: {env['cfg_dir']}/{config_json_file}" ) assert config_json_file in http_client.get("/").json()["data"]["config_files"] @@ -146,7 +146,7 @@ def test_save_load_and_delete_configs(http_client): assert response.status_code == 200 assert response_data["status"] == STATUS_SUCCESS assert response_data["messages"][0]["message"] == ( - f"File deleted: {CFG_DIR}/{config_json_file}" + f"File deleted: {env['cfg_dir']}/{config_json_file}" ) assert config_json_file not in http_client.get("/").json()["data"]["config_files"] diff --git a/python/web/tests/conftest.py b/python/web/tests/conftest.py index da10c3f7..7e70b696 100644 --- a/python/web/tests/conftest.py +++ b/python/web/tests/conftest.py @@ -1,15 +1,32 @@ import pytest import requests +import socket +import os def pytest_addoption(parser): - parser.addoption("--base_url", action="store", default="http://localhost:8080") - parser.addoption("--httpserver_host", action="store", default="host.docker.internal") - parser.addoption("--httpserver_listen_address", action="store", default="127.0.0.1") + default_base_url = "http://rascsi_web" if os.getenv("DOCKER") else "http://localhost:8080" + + parser.addoption("--home_dir", action="store", default="/home/pi") + parser.addoption("--base_url", action="store", default=default_base_url) + parser.addoption("--httpserver_host", action="store", default=socket.gethostname()) + parser.addoption("--httpserver_listen_address", action="store", default="0.0.0.0") parser.addoption("--rascsi_username", action="store", default="pi") parser.addoption("--rascsi_password", action="store", default="rascsi") +@pytest.fixture(scope="session") +def env(pytestconfig): + home_dir = pytestconfig.getoption("home_dir") + return { + "is_docker": bool(os.getenv("DOCKER")), + "home_dir": home_dir, + "cfg_dir": f"{home_dir}/.config/rascsi", + "images_dir": f"{home_dir}/images", + "afp_dir": f"{home_dir}/afpshare", + } + + @pytest.fixture(scope="session") def httpserver_listen_address(pytestconfig): return (pytestconfig.getoption("httpserver_listen_address"), 0) @@ -17,8 +34,7 @@ def httpserver_listen_address(pytestconfig): @pytest.fixture(scope="function", autouse=True) def set_httpserver_hostname(pytestconfig, httpserver): - # The HTTP requests are made by Python from within the container so we need - # httpserver.url_for to generate URLs which point to the Docker host + # We need httpserver.url_for() to generate URLs pointing to the correct host httpserver.host = pytestconfig.getoption("httpserver_host")