From ed8f99ab363505e336fecda137f1ab655280283b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jiri=20Dan=C4=9Bk?= Date: Wed, 29 Jan 2025 11:31:36 +0100 Subject: [PATCH] RHOAIENG-11156: chore(tests/containers/jupyterlab): check that the JupyterLab index.html contains the spinner code --- poetry.lock | 2 +- pyproject.toml | 1 + .../workbenches/jupyterlab/__init__.py | 0 .../workbenches/jupyterlab/jupyterlab_test.py | 63 +++++++++++++++++++ .../workbenches/workbench_image_test.py | 7 ++- 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 tests/containers/workbenches/jupyterlab/__init__.py create mode 100644 tests/containers/workbenches/jupyterlab/jupyterlab_test.py diff --git a/poetry.lock b/poetry.lock index a4924bf16..ab1515a40 100644 --- a/poetry.lock +++ b/poetry.lock @@ -694,4 +694,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = "~3.12" -content-hash = "619289439f43248a01c78a331b7d475e9c53bef4a2044f287350bd9c13d25a0e" +content-hash = "f525d20baef6a062a045c13772f17d8e30b7fa14609069a13872ce570191240f" diff --git a/pyproject.toml b/pyproject.toml index 91980d9be..2e494b0bc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ pyfakefs = "^5.7.4" testcontainers = "^4.9.1" docker = "^7.1.0" pydantic = "^2.10.6" +requests = "^2.32.3" [build-system] requires = ["poetry-core"] diff --git a/tests/containers/workbenches/jupyterlab/__init__.py b/tests/containers/workbenches/jupyterlab/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/containers/workbenches/jupyterlab/jupyterlab_test.py b/tests/containers/workbenches/jupyterlab/jupyterlab_test.py new file mode 100644 index 000000000..43e1b299f --- /dev/null +++ b/tests/containers/workbenches/jupyterlab/jupyterlab_test.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import requests + +import allure +import pytest + +from tests.containers import docker_utils +from tests.containers.workbenches.workbench_image_test import WorkbenchContainer, skip_if_not_workbench_image + +if TYPE_CHECKING: + import docker.models.images + + +class TestJupyterLabImage: + """Tests for JupyterLab Workbench images in this repository.""" + + APP_ROOT_HOME = "/opt/app-root/src" + + @allure.issue("RHOAIENG-11156") + @allure.description("Check that the HTML for the spinner is contained in the initial page.") + def test_spinner_html_loaded(self, image: str) -> None: + skip_if_not_jupyterlab_image(image) + + container = WorkbenchContainer(image=image, user=4321, group_add=[0]) + # if no env is specified, the image will run + # > 4321 3334 3319 0 10:36 pts/0 00:00:01 /mnt/rosetta /opt/app-root/bin/python3.11 /opt/app-root/bin/jupyter-lab + # > --ServerApp.root_dir=/opt/app-root/src --ServerApp.ip= --ServerApp.allow_origin=* --ServerApp.open_browser=False + # which does not let us open a notebook and get a spinner, we need to disable auth at a minimum + + # These NOTEBOOK_ARGS are what ODH Dashboard uses, + # and we also have them in the Kustomize test files for Makefile tests + container.with_env("NOTEBOOK_ARGS", "\n".join([ + "--ServerApp.port=8888", + "--ServerApp.token=''", + "--ServerApp.password=''", + "--ServerApp.base_url=/notebook/opendatahub/jovyan", + "--ServerApp.quit_button=False", + """--ServerApp.tornado_settings={"user":"jovyan","hub_host":"https://opendatahub.io","hub_prefix":"/notebookController/jovyan"}"""])) + try: + # we changed base_url, and wait_for_readiness=True would attempt connections to / + container.start(wait_for_readiness=False) + container._connect(base_url="/notebook/opendatahub/jovyan") + + host_ip = container.get_container_host_ip() + host_port = container.get_exposed_port(container.port) + response = requests.get(f"http://{host_ip}:{host_port}/notebook/opendatahub/jovyan") + assert response.status_code == 200 + assert "text/html" in response.headers["content-type"] + assert 'class="pf-v6-c-spinner"' in response.text + finally: + docker_utils.NotebookContainer(container).stop(timeout=0) + + +def skip_if_not_jupyterlab_image(image: str) -> docker.models.images.Image: + image_metadata = skip_if_not_workbench_image(image) + if "-jupyter-" not in image_metadata.labels['name']: + pytest.skip( + f"Image {image} does not have '-jupyter-' in {image_metadata.labels['name']=}'") + + return image_metadata diff --git a/tests/containers/workbenches/workbench_image_test.py b/tests/containers/workbenches/workbench_image_test.py index 44731bb23..dd9c412f6 100644 --- a/tests/containers/workbenches/workbench_image_test.py +++ b/tests/containers/workbenches/workbench_image_test.py @@ -137,9 +137,12 @@ def __init__( self.with_exposed_ports(self.port) @testcontainers.core.waiting_utils.wait_container_is_ready(urllib.error.URLError) - def _connect(self, container_host: str | None = None, container_port: int | None = None) -> None: + def _connect(self, container_host: str | None = None, container_port: int | None = None, + base_url: str = "") -> None: """ :param container_host: overrides the container host IP in connection check to use direct access + :param container_port: overrides the container port + :param base_url: needs to be with a leading / """ # are we still alive? self.get_wrapped_container().reload() @@ -156,7 +159,7 @@ def _connect(self, container_host: str | None = None, container_port: int | None # host may be an ipv6 address, need to be careful with formatting this if ":" in host: host = f"[{host}]" - result = opener.open(urllib.request.Request(f"http://{host}:{port}"), timeout=1) + result = opener.open(urllib.request.Request(f"http://{host}:{port}{base_url}"), timeout=1) except urllib.error.URLError as e: raise e