diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/Dockerfile b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/Dockerfile index d2fcd0ab..d4614a9f 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/Dockerfile +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/Dockerfile @@ -21,7 +21,10 @@ COPY profiles/ /fixtures/ # compose profile. See Docker/README.md §exception injection. COPY exception_injector.py /fixtures/ -EXPOSE 5020 +# 5020 = the shared port for the standard/dl205/mitsubishi/s7_1500/exception_injection +# profiles (only one binds it at a time); 5021 = the rtu_over_tcp profile, which co-runs +# with standard. Image-metadata honesty only — compose's ports: mapping doesn't need EXPOSE. +EXPOSE 5020 5021 # Default to the standard profile; docker-compose.yml overrides per service. # --http_port intentionally omitted; pymodbus 3.13's web UI binds on a diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md index 306fbc52..c9ec0938 100644 --- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md +++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/Docker/README.md @@ -2,15 +2,16 @@ The Modbus driver's integration tests talk to a [`pymodbus`](https://pymodbus.readthedocs.io/) simulator running as a -pinned Docker container. One image, per-profile service in compose, same -port binding (`5020`) regardless of which profile is live. Docker is the -only supported launch path — a fresh clone needs Docker Desktop and -nothing else. +pinned Docker container. One image, per-profile service in compose. Most +profiles bind `:5020`, so only one of *those* runs at a time; the +`rtu_over_tcp` profile binds `:5021` and is designed to co-run alongside +`standard`. Docker is the only supported launch path — a fresh clone needs +Docker Desktop and nothing else. | File | Purpose | |---|---| | [`Dockerfile`](Dockerfile) | `python:3.12-slim-bookworm` + `pymodbus[simulator]==3.13.0` + every profile JSON + `exception_injector.py` | -| [`docker-compose.yml`](docker-compose.yml) | One service per profile (`standard` / `dl205` / `mitsubishi` / `s7_1500` / `exception_injection`); all bind `:5020` so only one runs at a time | +| [`docker-compose.yml`](docker-compose.yml) | One service per profile (`standard` / `dl205` / `mitsubishi` / `s7_1500` / `exception_injection` bind `:5020`, so only one of those runs at a time; `rtu_over_tcp` binds `:5021` and can run alongside `standard`) | | [`profiles/*.json`](profiles/) | Same seed-register definitions the native launcher uses — canonical source | | [`exception_injector.py`](exception_injector.py) | Pure-stdlib Modbus/TCP server that emits arbitrary exception codes per rule — used by the `exception_injection` profile | @@ -43,10 +44,11 @@ docker compose -f tests\...\Docker\docker-compose.yml --profile dl205 up -d docker compose -f tests\...\Docker\docker-compose.yml --profile dl205 down ``` -Only one profile binds `:5020` at a time; switch by stopping the current -service + starting another. The integration tests discriminate by a -separate `MODBUS_SIM_PROFILE` env var so they skip correctly when the -wrong profile is live. +Only one of the `:5020` profiles binds at a time; switch by stopping the +current service + starting another. (`rtu_over_tcp` is the exception — it +binds `:5021` and co-runs with `standard`.) The integration tests +discriminate by a separate `MODBUS_SIM_PROFILE` env var so they skip +correctly when the wrong profile is live. > **⚠️ Profile-cycling gotcha (bit us in the 2026-07 sweep — see > `archreview/plans/INTEGRATION-SWEEP-STATUS.md`).** Each profile is a