From a0ff6831f4e637a9d25b012f4202ed0dddc8aa60 Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Thu, 6 Nov 2025 11:24:03 +0100 Subject: [PATCH 1/5] Fix ioc hostname --- server/docker/test-multi-recc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/docker/test-multi-recc.yml b/server/docker/test-multi-recc.yml index f66fa173..cbe24010 100644 --- a/server/docker/test-multi-recc.yml +++ b/server/docker/test-multi-recc.yml @@ -36,7 +36,7 @@ services: restart: true environment: - IOCSH_NAME=IOC1-2 - hostname: ioc1-1 + hostname: ioc1-2 recc2: extends: recc1 From 3380a06e12d37e9a9643d10394aa92627965bf60 Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Thu, 6 Nov 2025 12:46:57 +0100 Subject: [PATCH 2/5] Split up tests based on compose file --- ...t-remove-infotag.yml => test-bash-ioc.yml} | 2 +- server/docker/test-single-ioc.yml | 33 +++++++++++++++++++ server/tests/docker.py | 31 ++++++++++------- ...st_remove_property.py => test_bash_ioc.py} | 16 ++------- ...test_e2e.py => test_multiple_recceiver.py} | 10 ++++-- .../{test_restart.py => test_single_ioc.py} | 14 ++++++-- 6 files changed, 73 insertions(+), 33 deletions(-) rename server/docker/{test-remove-infotag.yml => test-bash-ioc.yml} (95%) create mode 100644 server/docker/test-single-ioc.yml rename server/tests/{test_remove_property.py => test_bash_ioc.py} (85%) rename server/tests/{test_e2e.py => test_multiple_recceiver.py} (86%) rename server/tests/{test_restart.py => test_single_ioc.py} (91%) diff --git a/server/docker/test-remove-infotag.yml b/server/docker/test-bash-ioc.yml similarity index 95% rename from server/docker/test-remove-infotag.yml rename to server/docker/test-bash-ioc.yml index ae4a43aa..17b1d5a0 100644 --- a/server/docker/test-remove-infotag.yml +++ b/server/docker/test-bash-ioc.yml @@ -1,4 +1,4 @@ -name: test-removeinfotag +name: test-bash-ioc include: - cf-compose.yml services: diff --git a/server/docker/test-single-ioc.yml b/server/docker/test-single-ioc.yml new file mode 100644 index 00000000..6e4908df --- /dev/null +++ b/server/docker/test-single-ioc.yml @@ -0,0 +1,33 @@ +name: test-single-ioc +include: + - cf-compose.yml +services: + recc1: + extends: + file: ../compose.yml + service: recc + depends_on: + cf: + condition: service_healthy + restart: true + hostname: recc1 + networks: + - net-1-recc-1 + - net-2-cf + ioc1-1: + extends: + file: ../../client/ioc-compose.yml + service: ioc1 + depends_on: + recc1: + condition: service_healthy + restart: true + environment: + - IOCSH_NAME=IOC1-1 + hostname: ioc1-1 + networks: + - net-1-recc-1 + +networks: + net-1-recc-1: + driver: bridge diff --git a/server/tests/docker.py b/server/tests/docker.py index e479edcf..b2d19071 100644 --- a/server/tests/docker.py +++ b/server/tests/docker.py @@ -10,7 +10,7 @@ LOG: logging.Logger = logging.getLogger(__name__) -def test_compose(compose_file=Path("docker") / Path("test-multi-recc.yml")) -> DockerCompose: +def test_compose(compose_file: Path) -> DockerCompose: current_path = Path(__file__).parent.resolve() return DockerCompose( @@ -30,17 +30,24 @@ def fetch_containers_and_log_logs(compose: DockerCompose) -> None: LOG.debug(log.decode("utf-8")) -@pytest.fixture(scope="class") -def setup_compose(): - LOG.info("Setup test environment") - compose = test_compose() - compose.start() - yield compose - LOG.info("Teardown test environment") - LOG.info("Stopping docker compose") - if LOG.level <= logging.DEBUG: - fetch_containers_and_log_logs(compose) - compose.stop() +class ComposeFixtureFactory: + def __init__(self, compose_file: Path) -> None: + self.compose_file = compose_file + + def return_fixture(self): + @pytest.fixture(scope="class") + def setup_compose() -> DockerCompose: + LOG.info("Setup test environment") + compose = test_compose(self.compose_file) + compose.start() + yield compose + LOG.info("Teardown test environment") + LOG.info("Stopping docker compose") + if LOG.level <= logging.DEBUG: + fetch_containers_and_log_logs(compose) + compose.stop() + + return setup_compose def restart_container(compose: DockerCompose, host_name: str) -> str: diff --git a/server/tests/test_remove_property.py b/server/tests/test_bash_ioc.py similarity index 85% rename from server/tests/test_remove_property.py rename to server/tests/test_bash_ioc.py index 7b5950d3..85a087bb 100644 --- a/server/tests/test_remove_property.py +++ b/server/tests/test_bash_ioc.py @@ -2,14 +2,13 @@ import threading from pathlib import Path -import pytest from testcontainers.compose import DockerCompose from docker import DockerClient from docker.models.containers import Container from .client_checks import INACTIVE_PROPERTY, check_channel_property, create_client_and_wait, wait_for_sync -from .docker import fetch_containers_and_log_logs, test_compose # noqa: F401 +from .docker import ComposeFixtureFactory LOG: logging.Logger = logging.getLogger(__name__) @@ -19,18 +18,7 @@ encoding="utf-8", ) - -@pytest.fixture(scope="class") -def setup_compose(): - LOG.info("Setup remove test environment") - compose = test_compose(Path("docker") / Path("test-remove-infotag.yml")) - compose.start() - yield compose - LOG.info("Teardown test environment") - LOG.info("Stopping docker compose") - if LOG.level <= logging.DEBUG: - fetch_containers_and_log_logs(compose) - compose.stop() +setup_compose = ComposeFixtureFactory(Path("docker") / Path("test-bash-ioc.yml")).return_fixture() def docker_exec_new_command(container: Container, command: str): diff --git a/server/tests/test_e2e.py b/server/tests/test_multiple_recceiver.py similarity index 86% rename from server/tests/test_e2e.py rename to server/tests/test_multiple_recceiver.py index 3ba10435..0fc864a5 100644 --- a/server/tests/test_e2e.py +++ b/server/tests/test_multiple_recceiver.py @@ -1,10 +1,12 @@ import logging +from pathlib import Path import pytest from channelfinder import ChannelFinderClient +from testcontainers.compose import DockerCompose from .client_checks import create_client_and_wait -from .docker import setup_compose # noqa: F401 +from .docker import ComposeFixtureFactory LOG: logging.Logger = logging.getLogger(__name__) @@ -13,13 +15,15 @@ # 4 iocs, 6 channels per ioc (2 reccaster.db, 2 somerecords.db, 2 aliases in somerecords.db) EXPECTED_DEFAULT_CHANNEL_COUNT = 32 +setup_compose = ComposeFixtureFactory(Path("docker") / Path("test-multi-recc.yml")).return_fixture() + @pytest.fixture(scope="class") -def cf_client(setup_compose): # noqa: F811 +def cf_client(setup_compose: DockerCompose): # noqa: F811 return create_client_and_wait(setup_compose, EXPECTED_DEFAULT_CHANNEL_COUNT) -class TestE2E: +class TestMultipleRecceiver: def test_number_of_channels_and_channel_name(self, cf_client: ChannelFinderClient) -> None: channels = cf_client.find(name="*") assert len(channels) == EXPECTED_DEFAULT_CHANNEL_COUNT diff --git a/server/tests/test_restart.py b/server/tests/test_single_ioc.py similarity index 91% rename from server/tests/test_restart.py rename to server/tests/test_single_ioc.py index fcf08e72..3b80cacf 100644 --- a/server/tests/test_restart.py +++ b/server/tests/test_single_ioc.py @@ -1,5 +1,6 @@ import logging import time +from pathlib import Path import pytest from channelfinder import ChannelFinderClient @@ -12,17 +13,24 @@ create_client_and_wait, wait_for_sync, ) -from .docker import restart_container, setup_compose, shutdown_container, start_container # noqa: F401 +from .docker import ( + ComposeFixtureFactory, + restart_container, + shutdown_container, + start_container, +) PROPERTIES_TO_MATCH = ["pvStatus", "recordType", "recordDesc", "alias", "hostName", "iocName", "recceiverID"] LOG: logging.Logger = logging.getLogger(__name__) -EXPECTED_DEFAULT_CHANNEL_COUNT = 32 +EXPECTED_DEFAULT_CHANNEL_COUNT = 8 + +setup_compose = ComposeFixtureFactory(Path("docker") / Path("test-single-ioc.yml")).return_fixture() @pytest.fixture(scope="class") -def cf_client(setup_compose): # noqa: F811 +def cf_client(setup_compose: DockerCompose) -> ChannelFinderClient: # noqa: F811 return create_client_and_wait(setup_compose, expected_channel_count=EXPECTED_DEFAULT_CHANNEL_COUNT) From c80e3c6ddcf95577c48e7b4ad3cb46538eaaa610 Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Thu, 6 Nov 2025 16:18:07 +0100 Subject: [PATCH 3/5] Enable tests for just pushes --- .github/workflows/server.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index 0e8c266d..aea43206 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -3,7 +3,7 @@ name: recceiver on: push: - branches: [ "master" ] + branches: [ "*" ] paths: - server/** pull_request: From 940a4bdc0a760d61417066f21f3fb72e78e7ac4e Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Thu, 6 Nov 2025 16:18:42 +0100 Subject: [PATCH 4/5] Test move ioc --- server/tests/docker.py | 25 +++++++++++++++++++++++++ server/tests/test_single_ioc.py | 14 ++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/server/tests/docker.py b/server/tests/docker.py index b2d19071..fc60d35e 100644 --- a/server/tests/docker.py +++ b/server/tests/docker.py @@ -1,4 +1,5 @@ import logging +import time from pathlib import Path from typing import Optional @@ -72,3 +73,27 @@ def start_container( if container_id: docker_client = DockerClient() docker_client.containers.get(container_id).start() + + +def clone_container( + compose: DockerCompose, + new_host_name: str, + host_name: Optional[str] = None, + container_id: Optional[str] = None, + sleep_time=10, +) -> str: + container_id = container_id or compose.get_container(host_name).ID + if container_id: + docker_client = DockerClient() + container = docker_client.containers.get(container_id) + image = container.image + networks = container.attrs["NetworkSettings"]["Networks"].keys() + container.stop() + time.sleep(sleep_time) + container.remove() + docker_client.containers.run( + image, detach=True, environment={"IOC_NAME": host_name}, hostname=new_host_name, network=list(networks)[0] + ) + + return container_id + raise Exception("Container not found") diff --git a/server/tests/test_single_ioc.py b/server/tests/test_single_ioc.py index 3b80cacf..c767639b 100644 --- a/server/tests/test_single_ioc.py +++ b/server/tests/test_single_ioc.py @@ -15,6 +15,7 @@ ) from .docker import ( ComposeFixtureFactory, + clone_container, restart_container, shutdown_container, start_container, @@ -112,3 +113,16 @@ def test_status_property_works_between_cf_down( ) channels_inactive = cf_client.find(property=[("iocName", "IOC1-1")]) assert all(INACTIVE_PROPERTY in ch["properties"] for ch in channels_inactive) + + +class TestMoveIocHost: + def test_move_ioc_host( + self, + setup_compose: DockerCompose, # noqa: F811 + cf_client: ChannelFinderClient, + ) -> None: + channels_begin = cf_client.find(name="*") + clone_container(setup_compose, "ioc1-1-new", host_name="ioc1-1") + wait_for_sync(cf_client, lambda cf_client: check_channel_property(cf_client, "IOC1-1:Msg-I")) + channels_end = cf_client.find(name="*") + assert len(channels_begin) == len(channels_end) From 1820ad68fb31e07253b8dec05bfdd895f1127658 Mon Sep 17 00:00:00 2001 From: Sky Brewer Date: Tue, 11 Nov 2025 15:45:59 +0100 Subject: [PATCH 5/5] cleaner raise early --- server/tests/docker.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/server/tests/docker.py b/server/tests/docker.py index fc60d35e..eacb7097 100644 --- a/server/tests/docker.py +++ b/server/tests/docker.py @@ -83,17 +83,17 @@ def clone_container( sleep_time=10, ) -> str: container_id = container_id or compose.get_container(host_name).ID - if container_id: - docker_client = DockerClient() - container = docker_client.containers.get(container_id) - image = container.image - networks = container.attrs["NetworkSettings"]["Networks"].keys() - container.stop() - time.sleep(sleep_time) - container.remove() - docker_client.containers.run( - image, detach=True, environment={"IOC_NAME": host_name}, hostname=new_host_name, network=list(networks)[0] - ) - - return container_id - raise Exception("Container not found") + if not container_id: + raise Exception("Container not found") + + docker_client = DockerClient() + container = docker_client.containers.get(container_id) + image = container.image + networks = container.attrs["NetworkSettings"]["Networks"].keys() + container.stop() + time.sleep(sleep_time) + container.remove() + docker_client.containers.run( + image, detach=True, environment={"IOC_NAME": host_name}, hostname=new_host_name, network=list(networks)[0] + ) + return container_id