diff --git a/.github/workflows/deploy-docker-images.yml b/.github/workflows/deploy-docker-images.yml index a94d66f9..8c562be2 100644 --- a/.github/workflows/deploy-docker-images.yml +++ b/.github/workflows/deploy-docker-images.yml @@ -53,7 +53,7 @@ jobs: run: > DOCKER_PREFIX=${{ env.registry }} DOCKER_TAG=${{ matrix.capella_version }}-${{ steps.tag.outputs.branch }} - pytest -m "not t4c" tests + pytest -m "not (t4c_server or t4c)" tests - name: Push run: | for image in ${{ env.images }} diff --git a/.gitignore b/.gitignore index fbf6d6d6..96e932c2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,13 @@ capella/versions/*/capella.zip capella/versions/*/dropins/* !capella/versions/*/dropins/.gitkeep t4c/updateSite/*/* +t4c/server !t4c/updateSite/*/.gitkeep pure-variants/updateSite/* !pure-variants/updateSite/.gitkeep pure-variants/dependencies/* !pure-variants/dependencies/.gitkeep +tests/t4c-server-test-data libs ease/debug/libs/* ease/extensions/*/* diff --git a/Makefile b/Makefile index 322abc71..ecca7409 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,21 @@ LOG_LEVEL ?= DEBUG GIT_SHA = $(shell git rev-parse --short HEAD) +# If this option is set to 1, all tests that require a running t4c server +# will be executed. To run these tests, you need a Makefile in +# t4c/server with a target t4c/server/server that builds the t4c server +# docker images and provides them in the following format: +# t4c/server/server:x.x.x-latest. You also need test data in +# tests/t4c-server-test-data/data/x.x.x, which consists of a +# test repository (name test-repo) with a test project (name test-project). +# x.x.x here refers to the capella version +RUN_TESTS_WITH_T4C_SERVER ?= 0 + +# If this option is set to 1, all tests that require a t4c client will +# be executed. To run these tests, you must place the t4c files in the +# correct locations (as described in the README) +RUN_TESTS_WITH_T4C_CLIENT ?= 0 + all: \ base \ capella/base \ @@ -108,7 +123,6 @@ all: \ base: SHELL=./capella_loop.sh base: docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$(CAPELLA_DOCKERIMAGES_REVISION) base - echo test $(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) DOCKER_TAG=$(CAPELLA_DOCKERIMAGES_REVISION) IMAGENAME=$@ .push capella/base: SHELL=./capella_loop.sh @@ -128,7 +142,7 @@ capella/remote: capella/base t4c/client/base: SHELL=./capella_loop.sh t4c/client/base: capella/base - docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$$DOCKER_TAG --build-arg BASE_IMAGE=$(DOCKER_PREFIX)$<:$$DOCKER_TAG --build-arg CAPELLA_VERSION=$(CAPELLA_VERSION) t4c + docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$$DOCKER_TAG --build-arg BASE_IMAGE=$(DOCKER_PREFIX)$<:$$DOCKER_TAG --build-arg CAPELLA_VERSION=$$CAPELLA_VERSION t4c $(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) IMAGENAME=$@ .push t4c/client/cli: SHELL=./capella_loop.sh @@ -173,7 +187,7 @@ capella/readonly: capella/ease/remote t4c/client/backup: SHELL=./capella_loop.sh t4c/client/backup: t4c/client/base - docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$$DOCKER_TAG --build-arg BASE_IMAGE=$(DOCKER_PREFIX)$<:$$DOCKER_TAG backups + docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$$DOCKER_TAG --build-arg BASE_IMAGE=$(DOCKER_PREFIX)$<:$$DOCKER_TAG --build-arg CAPELLA_VERSION=$$CAPELLA_VERSION backups $(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) IMAGENAME=$@ .push run-capella/readonly: capella/readonly @@ -270,11 +284,41 @@ debug-t4c/client/backup: LOG_LEVEL=DEBUG debug-t4c/client/backup: DOCKER_RUN_FLAGS=-it --entrypoint="bash" -v $$(pwd)/backups/backup.py:/opt/capella/backup.py debug-t4c/client/backup: run-t4c/client/backup +t4c/server/server: SHELL=./capella_loop.sh +t4c/server/server: + $(MAKE) -C t4c/server PUSH_IMAGES=$(PUSH_IMAGES) CAPELLA_VERSION=$$CAPELLA_VERSION $@ + +local-git-server: SHELL=./capella_loop.sh +local-git-server: + docker build $(DOCKER_BUILD_FLAGS) -t $(DOCKER_PREFIX)$@:$$DOCKER_TAG --build-arg CAPELLA_VERSION=$$CAPELLA_VERSION tests/local-git-server + $(MAKE) PUSH_IMAGES=$(PUSH_IMAGES) IMAGENAME=$@ .push; \ + +ifeq ($(RUN_TESTS_WITH_T4C_SERVER), 1) +test: t4c/client/backup local-git-server t4c/server/server +endif + +ifeq ($(RUN_TESTS_WITH_T4C_CLIENT), 1) +test: t4c/client/remote +endif + test: SHELL=./capella_loop.sh -test: capella/readonly t4c/client/remote +test: capella/readonly + export CAPELLA_VERSION=$$CAPELLA_VERSION source .venv/bin/activate cd tests - pytest -o log_cli=true -s + + export PYTEST_MARKERS="not (t4c or t4c_server)" + if [ "$(RUN_TESTS_WITH_T4C_SERVER)" == "1" ] + then + export PYTEST_MARKERS="$$PYTEST_MARKERS or t4c_server" + fi + + if [ "$(RUN_TESTS_WITH_T4C_CLIENT)" == "1" ] + then + export PYTEST_MARKERS="$$PYTEST_MARKERS or t4c" + fi + + pytest -o log_cli=true -s -m "$$PYTEST_MARKERS" .push: @if [ "$(PUSH_IMAGES)" == "1" ]; \ @@ -283,5 +327,5 @@ test: capella/readonly t4c/client/remote docker push "$(DOCKER_REGISTRY)/$(DOCKER_PREFIX)$(IMAGENAME):$$DOCKER_TAG";\ fi -.PHONY: * +.PHONY: tests/* t4c/* * .ONESHELL: test diff --git a/backups/Dockerfile b/backups/Dockerfile index edf0cb56..ba7ece8d 100644 --- a/backups/Dockerfile +++ b/backups/Dockerfile @@ -4,6 +4,9 @@ ARG BASE_IMAGE=t4c/client/base FROM $BASE_IMAGE +ARG CAPELLA_VERSION +ENV CAPELLA_VERSION=${CAPELLA_VERSION} + USER root # Otherwise, xvfb runs with PID 1 and signal USR1 is not handled properly diff --git a/backups/backup.py b/backups/backup.py index 9f7aa46a..a72a1763 100644 --- a/backups/backup.py +++ b/backups/backup.py @@ -1,10 +1,10 @@ # SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors # SPDX-License-Identifier: Apache-2.0 -import itertools import logging import os import pathlib +import re import shutil import subprocess import urllib.parse @@ -15,43 +15,76 @@ def run_importer_script() -> None: log.debug("Import model from TeamForCapella server...") - subprocess.run( - [ - "/opt/capella/capella", - "--launcher.suppressErrors", - "-consoleLog", - "-data", - "importer-workspace", - "-application", - "com.thalesgroup.mde.melody.collab.importer", - "-closeserveronfailure", - "false", - "-hostname", - os.environ["T4C_REPO_HOST"], - "-port", - os.getenv("T4C_REPO_PORT", "2036"), - "-reponame", - os.environ["T4C_REPO_NAME"], - "-projectName", - urllib.parse.quote(os.environ["T4C_PROJECT_NAME"]), - "-importerLogin", - os.environ["T4C_USERNAME"], - "-importerPassword", - os.environ["T4C_PASSWORD"], - "-outputFolder", - "/tmp/model", - "-archiveProject", - "false", - "-importCommitHistoryAsJson", - "true", - "-includeCommitHistoryChanges", - os.getenv("INCLUDE_COMMIT_HISTORY", "true"), + + connection_type: str = get_connection_type() + + command: list[str] = ["/opt/capella/capella"] + + if is_capella_5_x_x(): + command.append("--launcher.suppressErrors") + + command += [ + "-consoleLog", + "-data", + "importer-workspace", + "-application", + "com.thalesgroup.mde.melody.collab.importer", + "-closeserverOnFailure", + "false", + "-hostname", + os.environ["T4C_REPO_HOST"], + "-port", + os.getenv("T4C_REPO_PORT", "2036"), + "-repoName", + os.environ["T4C_REPO_NAME"], + "-projectName", + urllib.parse.quote(os.environ["T4C_PROJECT_NAME"]), + "-importerLogin" if is_capella_5_x_x() else "-repositoryLogin", + os.environ["T4C_USERNAME"], + "-importerPassword" if is_capella_5_x_x() else "-repositoryPassword", + os.environ["T4C_PASSWORD"], + "-outputFolder", + "/tmp/model", + "-archiveProject", + "false", + "-importCommitHistoryAsJson", + "true", + "-includeCommitHistoryChanges", + os.getenv("INCLUDE_COMMIT_HISTORY", "true"), + ] + + if connection_type == "telnet": + command += [ "-consoleport", os.getenv("T4C_CDO_PORT", "12036"), - ], - check=True, - cwd="/opt/capella", - ) + ] + else: + http_login, http_password, http_port = get_http_envs() + command.extend( + [ + "-httpLogin", + http_login, + "-httpPassword", + http_password, + "-httpPort", + http_port, + ] + ) + + with subprocess.Popen( + command, cwd="/opt/capella", stdout=subprocess.PIPE, text=True + ) as popen: + if popen.stdout: + for line in popen.stdout: + print(line, end="") + if "Team for Capella server unreachable" in line: + raise RuntimeError( + "Import of model from TeamForCapella server failed - Team for Capella server unreachable" + ) + + if (return_code := popen.returncode) != 0: + raise subprocess.CalledProcessError(return_code, command) + log.info("Import of model from TeamForCapella server finished") @@ -162,6 +195,40 @@ def git_commit_and_push(git_dir: pathlib.Path) -> None: log.warning("No changes, will not commit.") +def get_connection_type() -> str: + default_connection_type: str = "telnet" if is_capella_5_x_x() else "http" + connection_type: str = os.getenv( + "CONNECTION_TYPE", default_connection_type + ) + + if connection_type not in ("telnet", "http"): + raise ValueError( + 'CONNECTION_TYPE is only allowed to be: "telnet", "http"' + ) + + if connection_type == "http" and is_capella_5_x_x(): + raise ValueError('CONNECTION_TYPE must be "telnet" for capella 5.x.x') + + return connection_type + + +def is_capella_5_x_x() -> bool: + return bool(re.match(r"5.[0-9]+.[0-9]+", os.getenv("CAPELLA_VERSION", ""))) + + +def get_http_envs() -> tuple[str, str, str]: + http_login = os.getenv("HTTP_LOGIN") + http_password = os.getenv("HTTP_PASSWORD") + http_port = os.getenv("HTTP_PORT") + + if not (http_login and http_password and http_port): + raise ValueError( + "HTTP_LOGIN, HTTP_PASSWORD, HTTP_PORT must be specified" + ) + + return (http_login, http_password, http_port) + + if __name__ == "__main__": model_dir = pathlib.Path("/tmp/model") model_dir.mkdir(exist_ok=True) @@ -170,3 +237,4 @@ def git_commit_and_push(git_dir: pathlib.Path) -> None: git_dir = checkout_git_repository() copy_exported_files_into_git_repo(model_dir) git_commit_and_push(git_dir) + print("Backup finished") diff --git a/backups/importer.sh b/backups/importer.sh deleted file mode 100644 index 5a2609b9..00000000 --- a/backups/importer.sh +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors -# SPDX-License-Identifier: Apache-2.0 - -./capella \ - --launcher.suppressErrors \ - -nosplash -console -consoleLog \ - -data importer-workspace \ - -application com.thalesgroup.mde.melody.collab.importer \ - -checksize -1 -closeserveronfailure false \ - "$@" \ - -vmargs -Xms1000m -Xmx3000m -Xss4m -Dorg.eclipse.net4j.util.om.trace.disable=true \ diff --git a/pyproject.toml b/pyproject.toml index 0996c5ef..9662a603 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,24 +7,14 @@ dynamic = ["version"] name = "capella-dockerimages-tests" requires-python = ">=3.9, <3.12" license = { text = "Apache-2.0" } -authors = [ - { name = "DB Netz AG" }, -] -dependencies = [ - "docker", - "pytest", -] +authors = [{ name = "DB Netz AG" }] +dependencies = ["docker", "pytest"] [project.urls] Homepage = "https://github.com/DSD-DBS/capella-dockerimages" [project.optional-dependencies] -dev = [ - "black", - "isort", - "mypy", - "pylint", -] +dev = ["black", "isort", "mypy", "pylint"] [tool.black] line-length = 79 @@ -52,17 +42,17 @@ ignore_missing_imports = true [tool.pydocstyle] convention = "numpy" add-select = [ - "D212", # Multi-line docstring summary should start at the first line - "D402", # First line should not be the function’s “signature” - "D417", # Missing argument descriptions in the docstring + "D212", # Multi-line docstring summary should start at the first line + "D402", # First line should not be the function’s “signature” + "D417", # Missing argument descriptions in the docstring ] add-ignore = [ - "D201", # No blank lines allowed before function docstring # auto-formatting - "D202", # No blank lines allowed after function docstring # auto-formatting - "D203", # 1 blank line required before class docstring # auto-formatting - "D204", # 1 blank line required after class docstring # auto-formatting - "D211", # No blank lines allowed before class docstring # auto-formatting - "D213", # Multi-line docstring summary should start at the second line + "D201", # No blank lines allowed before function docstring # auto-formatting + "D202", # No blank lines allowed after function docstring # auto-formatting + "D203", # 1 blank line required before class docstring # auto-formatting + "D204", # 1 blank line required after class docstring # auto-formatting + "D211", # No blank lines allowed before class docstring # auto-formatting + "D213", # Multi-line docstring summary should start at the second line ] [tool.pylint.messages_control] @@ -139,7 +129,8 @@ addopts = """ """ markers = [ - "t4c: mark a test that requires a TeamForCapella docker image", + "t4c: mark a test that requires a TeamForCapella docker image", + "t4c_server: mark a test that requires a TeamForCapella server docker image", ] testpaths = ["tests"] diff --git a/t4c/Dockerfile b/t4c/Dockerfile index 6c0ffed7..5396836b 100644 --- a/t4c/Dockerfile +++ b/t4c/Dockerfile @@ -6,10 +6,12 @@ FROM ${BASE_IMAGE} ARG CAPELLA_VERSION="" -USER techuser # Install T4C COPY ./updateSite/$CAPELLA_VERSION /opt/updateSite WORKDIR /opt/updateSite +RUN chmod +r $(ls /opt/updateSite/*.zip) + +USER techuser ## Install T4C Plugins via the P2 API from Eclipse RUN T4C_ZIP=$(find . -type f -iname "*.zip" | head -n 1 | cut -c 3-); \ /opt/capella/capella \ diff --git a/tests/conftest.py b/tests/conftest.py index c7e5bfac..a9df5b24 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,9 +8,10 @@ import pathlib import time from collections.abc import Iterator +from contextlib import contextmanager import docker -import docker.models.containers +from docker.models import containers log = logging.getLogger(__file__) log.setLevel("DEBUG") @@ -19,33 +20,38 @@ timeout = 120 # Timeout in seconds +@contextmanager def get_container( image: str, - environment: dict[str, str], - tmp_path: pathlib.Path | None = None, + ports: dict[str, int | None] | None = None, + environment: dict[str, str] | None = None, + path: pathlib.Path | None = None, mount_path: str | None = None, -) -> Iterator[docker.models.containers.Container]: + network: str | None = None, +) -> Iterator[containers.Container]: volumes = ( { - str(tmp_path): { + str(path): { "bind": mount_path, "model": "rw", } } - if tmp_path + if path else {} ) - container = None docker_prefix = os.getenv("DOCKER_PREFIX", "") docker_tag = os.getenv("DOCKER_TAG", "latest") + container = client.containers.run( + image=f"{docker_prefix}{image}:{docker_tag}", + detach=True, + ports=ports, + environment=environment, + volumes=volumes, + network=network, + ) + try: - container = client.containers.run( - image=f"{docker_prefix}{image}:{docker_tag}", - detach=True, - environment=environment | {"RMT_PASSWORD": "password"}, - volumes=volumes, - ) yield container finally: if container: @@ -54,7 +60,7 @@ def get_container( def wait_for_container( - container: docker.models.containers.Container, wait_for_message: str + container: containers.Container, wait_for_message: str ) -> None: log_line = 0 for _ in range(int(timeout / 2)): diff --git a/tests/local-git-server/Dockerfile b/tests/local-git-server/Dockerfile new file mode 100644 index 00000000..805a9627 --- /dev/null +++ b/tests/local-git-server/Dockerfile @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +FROM alpine/git + +RUN apk add --update git-daemon && \ + apk add --update lighttpd && \ + rm -rf /var/cache/apk/* + +COPY lighttpd.conf /etc/lighttpd/lighttpd.conf + +RUN mkdir /var/www/git; \ + mkdir /var/www/git/git-test-repo.git; + +WORKDIR /var/www/git/git-test-repo.git +RUN git init -b main --bare +RUN git config --local http.receivepack true + +WORKDIR / + +ENTRYPOINT [ "lighttpd" ] +CMD ["-D", "-f", "/etc/lighttpd/lighttpd.conf"] diff --git a/tests/local-git-server/lighttpd.conf b/tests/local-git-server/lighttpd.conf new file mode 100644 index 00000000..b73abe9c --- /dev/null +++ b/tests/local-git-server/lighttpd.conf @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +server.document-root = "/var/www/git" +server.modules += ( "mod_cgi", "mod_alias", "mod_setenv") +$HTTP["host"] =~ "" { + alias.url += ( "/git" => "/usr/libexec/git-core/git-http-backend" ) + $HTTP["url"] =~ "^/git" { + cgi.assign = ("" => "") + setenv.add-environment = ( + "GIT_PROJECT_ROOT" => "/var/www/git", + "GIT_HTTP_EXPORT_ALL" => "1" + ) + } +} diff --git a/tests/test_backups.py b/tests/test_backups.py new file mode 100644 index 00000000..50d0e575 --- /dev/null +++ b/tests/test_backups.py @@ -0,0 +1,188 @@ +# SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors +# SPDX-License-Identifier: Apache-2.0 + +from __future__ import annotations + +import logging +import os +import pathlib +import re +import subprocess + +import conftest +import pytest +from docker.models import containers + +log = logging.getLogger(__file__) +log.setLevel("DEBUG") + +CAPELLA_VERSION = os.getenv("CAPELLA_VERSION", "") + + +def is_capella_5_x_x() -> bool: + return bool(re.match(r"5.[0-9]+.[0-9]+", CAPELLA_VERSION)) + + +def is_capella_6_x_x() -> bool: + return bool(re.match(r"6.[0-9]+.[0-9]+", CAPELLA_VERSION)) + + +@pytest.fixture(name="local_git_container") +def fixture_local_git_container() -> containers.Container: + ports: dict[str, int | None] = {"80/tcp": None} + + with conftest.get_container( + image="local-git-server", ports=ports + ) as container: + conftest.wait_for_container(container, "server started") + yield container + + +@pytest.fixture(name="t4c_backup_container") +def fixture_t4c_backup_container( + local_git_container: containers.Container, + t4c_server_container: containers.Container, + request, +) -> containers.Container: + t4c_server_container.reload() + + t4c_repo_port = t4c_server_container.ports["2036/tcp"][0]["HostPort"] + t4c_cdo_port = t4c_server_container.ports["12036/tcp"][0]["HostPort"] + + if "HTTP_PORT" in request.param: + request.param["HTTP_PORT"] = t4c_server_container.ports["8080/tcp"][0][ + "HostPort" + ] + + local_git_container.reload() + git_server_port = local_git_container.ports["80/tcp"][0]["HostPort"] + + env: dict[str, str] = { + "GIT_REPO_URL": f"http://localhost:{git_server_port}/git/git-test-repo.git", + "GIT_REPO_BRANCH": "backup-test", + "GIT_USERNAME": "any", + "GIT_PASSWORD": "any", + "T4C_REPO_HOST": "127.0.0.1", + "T4C_REPO_PORT": t4c_repo_port, + "T4C_CDO_PORT": t4c_cdo_port, + "T4C_REPO_NAME": "test-repo", + "T4C_PROJECT_NAME": "test-project", + "T4C_USERNAME": "admin", + "T4C_PASSWORD": "password", + "LOG_LEVEL": "INFO", + "INCLUDE_COMMIT_HISTORY": "false", + } | request.param + + with conftest.get_container( + image="t4c/client/backup", environment=env, network="host" + ) as container: + yield container + + +@pytest.fixture(name="t4c_server_container") +def fixture_t4c_server_container(request) -> containers.Container: + env: dict[str, str] = {"REST_API_PASSWORD": "password"} | request.param + + ports: dict[str, int | None] = { + "2036/tcp": None, + "8080/tcp": None, + "12036/tcp": None, + } + + with conftest.get_container( + image="t4c/server/server", + ports=ports, + environment=env, + path=pathlib.Path(__file__).parents[0] + / "t4c-server-test-data" + / "data" + / CAPELLA_VERSION, + mount_path="/data", + ) as container: + conftest.wait_for_container( + container, "!MESSAGE Warmup done for repository test-repo." + ) + yield container + + +@pytest.mark.t4c_server +@pytest.mark.parametrize( + "t4c_server_container,t4c_backup_container", + [ + pytest.param({"ENABLE_CDO": "true"}, {"CONNECTION_TYPE": "telnet"}), + pytest.param( + {"ENABLE_CDO": "false"}, + {"CONNECTION_TYPE": "telnet"}, + marks=pytest.mark.skipif( + condition=is_capella_6_x_x(), + reason="CDO disabled by default for capella >= 6.0.0", + ), + ), + pytest.param( + {"ENABLE_CDO": "false"}, + { + "CONNECTION_TYPE": "http", + "HTTP_LOGIN": "admin", + "HTTP_PASSWORD": "password", + "HTTP_PORT": None, + }, + marks=pytest.mark.skipif( + condition=is_capella_5_x_x(), + reason="conncetion type http is not supported for capella < 6.0.0", + ), + ), + ], + indirect=True, +) +def test_model_backup_happy( + t4c_backup_container: containers.Container, + local_git_container: containers.Container, + tmp_path: pathlib.Path, +): + conftest.wait_for_container(t4c_backup_container, "Backup finished") + + git_server_port = local_git_container.ports["80/tcp"][0]["HostPort"] + subprocess.run( + [ + "git", + "clone", + f"http://localhost:{git_server_port}/git/git-test-repo.git", + tmp_path, + ], + check=True, + ) + try: + subprocess.run( + ["git", "switch", "backup-test"], check=True, cwd=tmp_path + ) + except subprocess.CalledProcessError: + log.debug("backup failed - backup-test branch does not exists") + raise + + assert os.path.exists(tmp_path / ".project") + assert os.path.exists(tmp_path / "test-project.afm") + assert os.path.exists(tmp_path / "test-project.aird") + assert os.path.exists(tmp_path / "test-project.capella") + + +@pytest.mark.t4c_server +@pytest.mark.parametrize( + "t4c_server_container,t4c_backup_container", + [ + # CONNECTION_TYPE "unknown" is not allowed + ({"ENABLE_CDO": "true"}, {"CONNECTION_TYPE": "unknown"}), + pytest.param( + {"ENABLE_CDO": "false"}, + {"CONNECTION_TYPE": "telnet"}, + marks=pytest.mark.skipif( + condition=is_capella_5_x_x(), + reason="CDO enabled by default for capella < 6.0.0", + ), + ), + ({"ENABLE_CDO": "false"}, {"CONNECTION_TYPE": "http"}), + ], + indirect=True, +) +def test_model_backup_unhappy(t4c_backup_container): + with pytest.raises(RuntimeError): + conftest.wait_for_container(t4c_backup_container, "Backup finished") diff --git a/tests/test_read_only.py b/tests/test_read_only.py index 05cbe940..d4e357bd 100644 --- a/tests/test_read_only.py +++ b/tests/test_read_only.py @@ -4,17 +4,13 @@ import io import json import logging -from collections.abc import Iterator -import docker +import conftest import pytest from docker.models import containers -from tests import conftest - log = logging.getLogger(__file__) log.setLevel("DEBUG") -client = docker.from_env() @pytest.fixture(name="mode_success", params=["json", "json2", "legacy"]) @@ -25,9 +21,7 @@ def fixture_mode_success(request) -> str: @pytest.fixture( name="container_success", ) -def fixture_container_success( - mode_success: str, -) -> Iterator[containers.Container]: +def fixture_container_success(mode_success: str) -> containers.Container: env: dict[str, str] = { # type: ignore "json": { "GIT_REPOS_JSON": json.dumps( @@ -66,8 +60,11 @@ def fixture_container_success( "GIT_REVISION": "main", "GIT_DEPTH": 0, }, - }[mode_success] - yield conftest.get_container(image="capella/readonly", environment=env) + }[mode_success] | {"RMT_PASSWORD": "password"} + with conftest.get_container( + image="capella/readonly", environment=env + ) as container: + yield container @pytest.fixture(name="mode_failure", params=["json", "legacy"]) @@ -76,9 +73,7 @@ def fixture_mode_failure(request) -> str: @pytest.fixture(name="container_failure") -def fixture_container_failure( - mode_failure: str, -) -> Iterator[containers.Container]: +def fixture_container_failure(mode_failure: str) -> containers.Container: env: dict[str, str] = { # type: ignore "json": { "GIT_REPOS_JSON": json.dumps( @@ -99,7 +94,10 @@ def fixture_container_failure( "GIT_DEPTH": 0, }, }[mode_failure] - yield conftest.get_container(image="capella/readonly", environment=env) + with conftest.get_container( + image="capella/readonly", environment=env + ) as container: + yield container def lines(bytes): @@ -108,12 +106,11 @@ def lines(bytes): @pytest.fixture(name="workspace_result") def fixture_workspace_result( - container_success: Iterator[containers.Container], + container_success: containers.Container, ) -> containers.ExecResult: - container = next(container_success) - conftest.wait_for_container(container, "---START_SESSION---") + conftest.wait_for_container(container_success, "---START_SESSION---") - return container.exec_run("ls -A1 /workspace") + return container_success.exec_run("ls -A1 /workspace") @pytest.mark.parametrize("mode_success", ["legacy"]) @@ -133,9 +130,6 @@ def test_model_loading_with_json2_env(workspace_result: containers.ExecResult): assert len(lines(workspace_result.output)) == 2 -def test_invalid_url_fails( - container_failure: Iterator[containers.Container], -): - container = next(container_failure) +def test_invalid_url_fails(container_failure: containers.Container): with pytest.raises(RuntimeError): - conftest.wait_for_container(container, "---START_SESSION---") + conftest.wait_for_container(container_failure, "---START_SESSION---") diff --git a/tests/test_t4c_repository_injection.py b/tests/test_t4c_repository_injection.py index 9cfae280..4cf620ab 100644 --- a/tests/test_t4c_repository_injection.py +++ b/tests/test_t4c_repository_injection.py @@ -5,19 +5,14 @@ import logging import pathlib import re -from collections.abc import Iterator -import docker +import conftest import pytest from docker.models import containers -from tests import conftest - log = logging.getLogger(__file__) log.setLevel("DEBUG") -client = docker.from_env() -timeout = 120 # Timeout in seconds default_env = { "T4C_LICENCE_SECRET": "", "RMT_PASSWORD": "my_long_password", @@ -37,7 +32,7 @@ def fixture_success_mode(request) -> str: def fixture_container_success( success_mode: str, tmp_path: pathlib.Path, -) -> Iterator[containers.Container]: +) -> containers.Container: env = { "json": { **default_env, @@ -70,12 +65,13 @@ def fixture_container_success( "T4C_REPOSITORIES": "repo1,repo2", }, }[success_mode] - yield conftest.get_container( + with conftest.get_container( image="t4c/client/remote", environment=env, - tmp_path=tmp_path, + path=tmp_path, mount_path="/opt/capella/configuration", - ) + ) as container: + yield container @pytest.fixture(name="failure_mode", params=["json"]) @@ -86,7 +82,7 @@ def fixture_failure_mode(request) -> str: @pytest.fixture(name="container_failure") def fixture_container_failure( failure_mode: str, -) -> Iterator[containers.Container]: +) -> containers.Container: env = { "json": { **default_env, @@ -111,20 +107,22 @@ def fixture_container_failure( ), }, }[failure_mode] - yield conftest.get_container(image="t4c/client/remote", environment=env) + with conftest.get_container( + image="t4c/client/remote", environment=env + ) as container: + yield container @pytest.mark.t4c def test_repositories_seeding( - container_success: Iterator[containers.Container], + container_success: containers.Container, success_mode: str, tmp_path: pathlib.Path, ): tmp_path.chmod(0o777) - container = next(container_success) conftest.wait_for_container( - container, "INFO success: xrdp-sesman entered RUNNING state" + container_success, "INFO success: xrdp-sesman entered RUNNING state" ) path = tmp_path / "fr.obeo.dsl.viewpoint.collab" / "repository.properties" @@ -151,11 +149,9 @@ def test_repositories_seeding( @pytest.mark.t4c -def test_invalid_env_variable( - container_failure: Iterator[containers.Container], -): - container = next(container_failure) +def test_invalid_env_variable(container_failure: containers.Container): with pytest.raises(RuntimeError): conftest.wait_for_container( - container, "INFO success: xrdp-sesman entered RUNNING state" + container_failure, + "INFO success: xrdp-sesman entered RUNNING state", )