ntegration_tests/test_integration_reconn.py::test_mqtt_reconnection FAILED
==================================== ERRORS ====================================
_____________________ ERROR at setup of test_ha_discovery ______________________
request = <SubRequest 'setup' for <Function test_ha_discovery>>
@pytest.fixture(scope="module", autouse=True)
def setup(request):
"""
Fixture to setup and teardown the MQTT broker
"""
> broker.start()
integration_tests/test_integration_ha_discovery.py:21:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
integration_tests/mosquitto_container.py:36: in start
super().start()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/testcontainers/mqtt/__init__.py:131: in start
self._wait()
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/testcontainers/mqtt/__init__.py:143: in _wait
wait_for_logs(self, r"mosquitto version \d+.\d+.\d+ running", timeout=30)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
container = <integration_tests.mosquitto_container.MosquittoContainerEnhanced object at 0x7fe4f58a1d30>
predicate = 'mosquitto version \\d+.\\d+.\\d+ running', timeout = 30
interval = 1, predicate_streams_and = False, raise_on_exit = False
def wait_for_logs(
container: WaitStrategyTarget,
predicate: Union[Callable[[str], bool], str, WaitStrategy],
timeout: float = testcontainers_config.timeout,
interval: float = 1,
predicate_streams_and: bool = False,
raise_on_exit: bool = False,
#
) -> float:
"""
Enhanced version of wait_for_logs that supports both old and new interfaces.
This function waits for container logs to satisfy a predicate. It supports
multiple input types for the predicate and maintains backwards compatibility
with existing code while adding support for the new WaitStrategy system.
This is a convenience function that can be used for simple log-based waits.
For more complex scenarios, consider using structured wait strategies directly.
Args:
container: The DockerContainer to monitor
predicate: The predicate to check against logs. Can be:
- A callable function that takes log text and returns bool
- A string that will be compiled to a regex pattern
- A WaitStrategy object
timeout: Maximum time to wait in seconds (default: config.timeout)
interval: How frequently to check in seconds (default: 1)
predicate_streams_and: If True, predicate must match both stdout and stderr (default: False)
raise_on_exit: If True, raise RuntimeError if container exits before predicate matches (default: False)
Returns:
The time in seconds that was spent waiting
Raises:
TimeoutError: If the predicate is not satisfied within the timeout period
RuntimeError: If raise_on_exit is True and container exits before predicate matches
Example:
# Wait for a simple string
wait_for_logs(container, "ready for start")
# Wait with custom predicate
wait_for_logs(container, lambda logs: "database" in logs and "ready" in logs)
# Wait with WaitStrategy
strategy = LogMessageWaitStrategy("ready")
wait_for_logs(container, strategy)
# For more complex scenarios, use structured wait strategies directly:
container.waiting_for(LogMessageWaitStrategy("ready"))
"""
if isinstance(predicate, WaitStrategy):
start = time.time()
predicate.with_startup_timeout(int(timeout)).with_poll_interval(interval)
predicate.wait_until_ready(container)
return time.time() - start
else:
# Only warn for legacy usage (string or callable predicates, not WaitStrategy objects)
warnings.warn(
"The wait_for_logs function with string or callable predicates is deprecated and will be removed in a future version. "
"Use structured wait strategies instead: "
"container.waiting_for(LogMessageWaitStrategy('ready')) or "
"container.waiting_for(LogMessageWaitStrategy(re.compile(r'pattern')))",
DeprecationWarning,
stacklevel=2,
)
# Original implementation for backwards compatibility
re_predicate: Optional[Callable[[str], Any]] = None
if timeout is None:
timeout = testcontainers_config.timeout
if isinstance(predicate, str):
re_predicate = re.compile(predicate, re.MULTILINE).search
elif callable(predicate):
# some modules like mysql sends the search directly to the predicate
re_predicate = predicate
else:
raise TypeError("Predicate must be a string or callable")
wrapped = container.get_wrapped_container()
start = time.time()
while True:
duration = time.time() - start
stdout_b, stderr_b = container.get_logs()
stdout = stdout_b.decode()
stderr = stderr_b.decode()
predicate_result = (
re_predicate(stdout) or re_predicate(stderr)
if predicate_streams_and is False
else re_predicate(stdout) and re_predicate(stderr)
#
)
if predicate_result:
return duration
if duration > timeout:
# Get current logs and status for debugging
stdout_str, stderr_str = _get_container_logs_for_debugging(container)
status_info = _get_container_status_info(container)
> raise TimeoutError(
f"Container did not emit logs satisfying predicate in {timeout:.3f} seconds. "
f"Container status: {status_info['status']}, health: {status_info['health_status']}. "
f"Recent stdout: {stdout_str}. "
f"Recent stderr: {stderr_str}. "
f"Hint: Check if the container is starting correctly and the expected log pattern is being generated. "
f"Verify the predicate function or pattern matches the actual log output."
)
E TimeoutError: Container did not emit logs satisfying predicate in 30.000 seconds. Container status: exited, health: no health check. Recent stdout: ...1 01:51:32: Saving in-memory database to /data//mosquitto.db.
E 2026-03-01 01:51:32: Error saving in-memory database, unable to open /data//mosquitto.db.new for writing, error No such file or directory
E . Recent stderr: ... option without a listener will be disabled in the future.
E 2026-03-01 01:51:32: Info: running mosquitto as user: root.
E 2026-03-01 01:51:32: Warning: Mosquitto should not be run as root/administrator.
E . Hint: Check if the container is starting correctly and the expected log pattern is being generated. Verify the predicate function or pattern matches the actual log output.
which seems to indicate that there are some issues writing to the database. Maybe the new containers default to immutable filesystem and we just need to mount the DB on a Docker volume.
Need to investigate more.
If we upgrade the mosquitto container in
integration_tests/mosquitto_container.pyto 2.1.0 or higher all integration tests fail withwhich seems to indicate that there are some issues writing to the database. Maybe the new containers default to immutable filesystem and we just need to mount the DB on a Docker volume.
Need to investigate more.