fix: Wait for MockServer HTTP status without following logs (#40846)

Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com>
This commit is contained in:
n8n-cat-bot[bot]
2026-10-11 22:09:03 +00:00
committed by GitHub
co-authored by n8n-cat-bot[bot]
parent 3301b2cc0f
commit 45938389b8
2 changed files with 58 additions and 38 deletions
@@ -11,20 +11,17 @@ import type {
import { join } from 'path';
import { GenericContainer, Wait } from 'testcontainers';
import { createSilentLogConsumer } from '../helpers/utils';
import { TEST_CONTAINER_IMAGES } from '../test-containers';
import type { HelperContext, Service, ServiceResult } from './types';
const HOSTNAME = 'proxyserver';
const PORT = 1080;
const MOCKSERVER_WAIT_STRATEGY = Wait.forAll([
Wait.forLogMessage(`INFO ${PORT} started on port: ${PORT}`),
Wait.forHttp('/mockserver/status', PORT)
.withMethod('PUT')
.forStatusCode(200)
.withStartupTimeout(60_000)
.withReadTimeout(250),
]);
// The status check proves readiness without following MockServer logs.
const MOCKSERVER_WAIT_STRATEGY = Wait.forHttp('/mockserver/status', PORT)
.withMethod('PUT')
.forStatusCode(200)
.withStartupTimeout(60_000)
.withReadTimeout(250);
export interface ProxyMeta {
host: string;
@@ -49,36 +46,29 @@ export const proxy: Service<ProxyResult> = {
},
async start(network, projectName): Promise<ProxyResult> {
const { consumer, throwWithLogs } = createSilentLogConsumer();
const container = await new GenericContainer(TEST_CONTAINER_IMAGES.mockserver)
.withNetwork(network)
.withNetworkAliases(HOSTNAME)
.withExposedPorts(PORT)
// Keep MockServer heap predictable under parallel container load.
.withEnvironment({ JVM_OPTIONS: '-Xmx512m -Xms512m' })
.withWaitStrategy(MOCKSERVER_WAIT_STRATEGY)
.withLabels({
'com.docker.compose.project': projectName,
'com.docker.compose.service': HOSTNAME,
})
.withName(`${projectName}-${HOSTNAME}`)
.withReuse()
.start();
try {
const container = await new GenericContainer(TEST_CONTAINER_IMAGES.mockserver)
.withNetwork(network)
.withNetworkAliases(HOSTNAME)
.withExposedPorts(PORT)
// Keep MockServer heap predictable under parallel container load.
.withEnvironment({ JVM_OPTIONS: '-Xmx512m -Xms512m' })
.withWaitStrategy(MOCKSERVER_WAIT_STRATEGY)
.withLabels({
'com.docker.compose.project': projectName,
'com.docker.compose.service': HOSTNAME,
})
.withName(`${projectName}-${HOSTNAME}`)
.withReuse()
.withLogConsumer(consumer)
.start();
return {
container,
meta: {
host: HOSTNAME,
port: PORT,
internalUrl: `http://${HOSTNAME}:${PORT}`,
},
};
} catch (error) {
return throwWithLogs(error);
}
return {
container,
meta: {
host: HOSTNAME,
port: PORT,
internalUrl: `http://${HOSTNAME}:${PORT}`,
},
};
},
env(result: ProxyResult, external?: boolean): Record<string, string> {
@@ -0,0 +1,30 @@
import {
GenericContainer,
Wait,
type StartedNetwork,
type StartedTestContainer,
} from 'testcontainers';
import { afterEach, expect, test, vi } from 'vitest';
afterEach(() => {
vi.restoreAllMocks();
});
test('waits for MockServer status without a followed log stream', async () => {
const httpWait = vi.spyOn(Wait, 'forHttp');
const logWait = vi.spyOn(Wait, 'forLogMessage');
const withWaitStrategy = vi.spyOn(GenericContainer.prototype, 'withWaitStrategy');
const withLogConsumer = vi.spyOn(GenericContainer.prototype, 'withLogConsumer');
vi.spyOn(GenericContainer.prototype, 'start').mockResolvedValue({} as StartedTestContainer);
const { proxy } = await import('../services/proxy.js');
const network = { getName: () => 'test-network' } as StartedNetwork;
const result = await proxy.start(network, 'test-project');
expect(result.meta.internalUrl).toBe('http://proxyserver:1080');
expect(httpWait).toHaveBeenCalledWith('/mockserver/status', 1080);
// DEVP-1269: Log waits and consumers leave followed Docker streams attached to the worker.
expect(logWait).not.toHaveBeenCalled();
expect(withWaitStrategy).toHaveBeenCalledWith(httpWait.mock.results[0].value);
expect(withLogConsumer).not.toHaveBeenCalled();
});