From 355d74164837d0d22f71f1c8b9fdf69858f5d0af Mon Sep 17 00:00:00 2001 From: Mads Bisgaard <126242332+bisgaard-itis@users.noreply.github.com> Date: Fri, 5 Apr 2024 14:41:39 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20Restrict=20client=20version=20fo?= =?UTF-8?q?r=20logstreaming=20e2e=20test=20and=20don't=20fail=20when=20cle?= =?UTF-8?q?aning=20up=20jobs=20in=20e2e=20tests=20(#146)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update workflow before publishing python package * fix dependency issue and bump version * point to website in project description * fix broken dependency * improve doc * add github token to download artifacts * ensure only read-access @wvangeit * yet another attempt at downloading artifacts * make sure to use repo that ran the trigger wf * another attempt at fixing * change owner * allow publishing to testpypi also when pr * minor change * revert minor (but breaking) change * minor fix * add debug messages * another debug message * hopefully the final version * final fix * minor fix * move master and tag to individual jobs * add debug messages * dev->post * add python script for determining semantic version * minor changes * minor changes * improve error handling and add version file to artifacts * check if release * minor fix * ensure to enter venv * also when tagging * source venv in publishin workflow * ensure only master * add script for testing 'pure' semver * adapt workflows to new python script * minor change * attempt to evaluate expressions correctly * several fixes to fix tests * ensure repo is checked out in publish workflow * several small fixes * cleanup * debug * minor cleanup * mionr changes * add debug message * minor change * minor change * yet another try * minor change * minor change * minor change * mionr change * minor changes * correct workflow run id * cosmetic change * avoid using gh * change to a single job for publishing * minor cleanup * swap loops in clean up jobs * correction * update server compatibility to new url * minor change to trigger ci * introduce decorator to filter tests based on client version * test if it works * remove file which was wrongly committed * improve doc string * small cleanup * dont fail postprocessing step if deleting a job fails * @pcrespov change name --- clients/python/test/e2e/_utils.py | 24 ++++++++- .../python/test/e2e/ci/e2e/e2e/postprocess.py | 52 ++++++++++++------- clients/python/test/e2e/test_files_api.py | 6 +-- clients/python/test/e2e/test_solvers_api.py | 12 ++--- 4 files changed, 63 insertions(+), 31 deletions(-) diff --git a/clients/python/test/e2e/_utils.py b/clients/python/test/e2e/_utils.py index de197d57..bf702d52 100644 --- a/clients/python/test/e2e/_utils.py +++ b/clients/python/test/e2e/_utils.py @@ -1,6 +1,7 @@ import os import subprocess from pathlib import Path +from typing import Optional import osparc import pytest @@ -23,7 +24,7 @@ def repo_version() -> Version: return Version(version_file.read_text()) -def requires_dev_features(test): +def skip_if_no_dev_features(test): if ( Version(osparc.__version__) < repo_version() or not osparc_dev_features_enabled() @@ -31,7 +32,26 @@ def requires_dev_features(test): return pytest.mark.skip( ( f"{osparc.__version__=}<{str(repo_version)} " - "or {osparc_dev_features_enabled()=}" + f"or {osparc_dev_features_enabled()=}" ) )(test) return test + + +def skip_if_osparc_version( + *, + at_least: Optional[Version] = None, + at_most: Optional[Version] = None, + exactly: Optional[Version] = None, +): + def _wrapper(test): + osparc_version = Version(osparc.__version__) + if at_least and osparc_version < at_least: + return pytest.mark.skip((f"{osparc_version=}<{at_least}"))(test) + if at_most and osparc_version > at_most: + return pytest.mark.skip((f"{osparc_version=}>{at_most}"))(test) + if exactly and osparc_version != exactly: + return pytest.mark.skip((f"{osparc_version=}!={exactly}"))(test) + return test + + return _wrapper diff --git a/clients/python/test/e2e/ci/e2e/e2e/postprocess.py b/clients/python/test/e2e/ci/e2e/e2e/postprocess.py index 3b40e337..94852ca8 100644 --- a/clients/python/test/e2e/ci/e2e/e2e/postprocess.py +++ b/clients/python/test/e2e/ci/e2e/e2e/postprocess.py @@ -11,11 +11,13 @@ import typer from pydantic import PositiveInt from tenacity import ( + RetryError, Retrying, retry_if_exception_type, stop_after_attempt, stop_after_delay, ) +from urllib3.exceptions import HTTPError as Urllib3HttpError from ._models import Artifacts, ClientSettings, PytestIniFile, ServerSettings from ._utils import E2eExitCodes, E2eScriptFailure, handle_validation_error @@ -219,22 +221,34 @@ def clean_up_jobs(artifacts_dir: Path, retry_minutes: Optional[PositiveInt] = No msg = "Cleaning up jobs for user: " msg += f"\n{server_config.model_dump_json(indent=1)}" typer.echo(msg) - for attempt in Retrying( - retry=retry_if_exception_type(osparc.ApiException), - stop=stop_after_delay(timedelta(minutes=retry_minutes)) - if retry_minutes - else stop_after_attempt(1), - ): - with attempt: - with osparc.ApiClient(config) as api_client: - solvers_api = osparc.SolversApi(api_client) - assert isinstance( - solvers := solvers_api.list_solvers_releases(), list - ) - for solver in solvers: - assert isinstance(solver, osparc.Solver) - assert (id_ := solver.id) is not None - assert (version := solver.version) is not None - for job in solvers_api.jobs(id_, version): - assert isinstance(job, osparc.Job) - solvers_api.delete_job(id_, version, job.id) + try: + for attempt in Retrying( + retry=retry_if_exception_type((osparc.ApiException, Urllib3HttpError)), + stop=stop_after_delay(timedelta(minutes=retry_minutes)) + if retry_minutes + else stop_after_attempt(1), + ): + with attempt: + with osparc.ApiClient(config) as api_client: + solvers_api = osparc.SolversApi(api_client) + assert isinstance( + solvers := solvers_api.list_solvers_releases(), list + ) + for solver in solvers: + assert isinstance(solver, osparc.Solver) + assert (id_ := solver.id) is not None + assert (version := solver.version) is not None + for job in solvers_api.jobs(id_, version): + assert isinstance(job, osparc.Job) + solvers_api.delete_job(id_, version, job.id) + except RetryError as exc: + typer.echo( + typer.style( + ( + "Failed when cleaning jobs when encountering " + f"the following exception:\n{exc.last_attempt.exception()}" + ), + fg=typer.colors.RED, + bold=True, + ) + ) diff --git a/clients/python/test/e2e/test_files_api.py b/clients/python/test/e2e/test_files_api.py index 4bfe3699..c7519203 100644 --- a/clients/python/test/e2e/test_files_api.py +++ b/clients/python/test/e2e/test_files_api.py @@ -3,7 +3,7 @@ import osparc import pytest -from _utils import requires_dev_features +from _utils import skip_if_no_dev_features from conftest import _KB @@ -19,7 +19,7 @@ def _hash_file(file: Path) -> str: return sha256.hexdigest() -@requires_dev_features +@skip_if_no_dev_features def test_upload_file(tmp_file: Path, api_client: osparc.ApiClient) -> None: """Test that we can upload a file via the multipart upload""" tmp_path: Path = tmp_file.parent @@ -37,7 +37,7 @@ def test_upload_file(tmp_file: Path, api_client: osparc.ApiClient) -> None: files_api.delete_file(uploaded_file1.id) -@requires_dev_features +@skip_if_no_dev_features @pytest.mark.parametrize("use_checksum", [True, False]) @pytest.mark.parametrize("use_id", [True, False]) def test_search_files( diff --git a/clients/python/test/e2e/test_solvers_api.py b/clients/python/test/e2e/test_solvers_api.py index 719a6561..55422f55 100644 --- a/clients/python/test/e2e/test_solvers_api.py +++ b/clients/python/test/e2e/test_solvers_api.py @@ -1,13 +1,14 @@ import json import osparc -from _utils import requires_dev_features +from _utils import skip_if_no_dev_features, skip_if_osparc_version from httpx import AsyncClient +from packaging.version import Version DEFAULT_TIMEOUT_SECONDS = 10 * 60 # 10 min -@requires_dev_features +@skip_if_no_dev_features def test_jobs(api_client: osparc.ApiClient, sleeper: osparc.Solver): """Test the jobs method @@ -48,14 +49,11 @@ def test_jobs(api_client: osparc.ApiClient, sleeper: osparc.Solver): solvers_api.delete_job(sleeper.id, sleeper.version, elm) +@skip_if_osparc_version(at_least=Version("0.6.5")) async def test_logstreaming( api_client: osparc.ApiClient, sleeper: osparc.Solver, async_client: AsyncClient ): - """Test the log streaming - - Args: - configuration (osparc.Configuration): The Configuration - """ + """Test log streaming""" solvers_api: osparc.SolversApi = osparc.SolversApi(api_client) job: osparc.Job = solvers_api.create_job( sleeper.id, sleeper.version, osparc.JobInputs({"input1": 1.0})