fix tests with file server host (#38138)

This commit is contained in:
sabril
2026-08-27 04:48:45 +08:00
committed by GitHub
parent 85b0227d1d
commit c4a2e085ec
8 changed files with 33 additions and 27 deletions
@@ -27,6 +27,9 @@ describe('Upload Files', () => {
// # Login as sysadmin
cy.apiAdminLogin();
// # Ensure standard message display (download aria-label is omitted in compact)
cy.apiSaveMessageDisplayPreference('clean');
// # Init setup
cy.apiInitSetup().then((out) => {
channelUrl = out.channelUrl;
@@ -47,15 +47,15 @@ function structuralEnv(): Record<string, string> {
MM_EMAILSETTINGS_SMTPSERVER: INBUCKET_ALIAS,
MM_EMAILSETTINGS_SMTPPORT: String(INBUCKET_SMTP_PORT),
...(process.env.MM_LICENSE ? {MM_LICENSE: process.env.MM_LICENSE} : {}),
// Overrides (not merges) SERVER_ENV_BASELINE's own value for this same key appends the
// network's gateway IP so the SSRF guard also allows fetching from file_server.ts's mock
// file server, reachable at that address (see test_config.ts). Only known once the
// network is up (testConfig.testcontainersNetworkGatewayIp is set by stack.ts's
// startStack() before this container ever starts), so falls back to the baseline's own
// value verbatim on the off chance this ever runs without it.
MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS: testConfig.testcontainersNetworkGatewayIp
? `${SERVER_ENV_BASELINE.MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS} ${testConfig.testcontainersNetworkGatewayIp}`
: SERVER_ENV_BASELINE.MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS,
// Replaces the baseline for this key: appends mock file-server hosts (host.docker.internal
// and the bridge gateway IP, set by startStack() once the network is up).
MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS: [
SERVER_ENV_BASELINE.MM_SERVICESETTINGS_ALLOWEDUNTRUSTEDINTERNALCONNECTIONS,
'host.docker.internal',
testConfig.testcontainersNetworkGatewayIp,
]
.filter(Boolean)
.join(' '),
};
}
@@ -85,6 +85,8 @@ export async function startMattermostContainer(
.withNetworkMode(networkName)
.withNetworkAliases(MATTERMOST_ALIAS)
.withLabels(TESTCONTAINERS_LABELS)
// Ensures host.docker.internal resolves to the Docker host (via host-gateway).
.withExtraHosts([{host: 'host.docker.internal', ipAddress: 'host-gateway'}])
.withExposedPorts(MATTERMOST_PORT)
.withEnvironment(env)
.withStartupTimeout(5 * 60_000)
+17 -6
View File
@@ -8,6 +8,9 @@ import {test} from './test_fixture';
import {testConfig} from '@/test_config';
/** Hostname containers use to reach the Docker host. */
export const DOCKER_HOST_INTERNAL = 'host.docker.internal';
/**
* Starts a server that serves files from ./asset. When run from the monorepo, this will serve files from
* e2e-tests/playwright/asset.
@@ -63,10 +66,18 @@ export function setupFileServer(): Promise<string> {
});
}
// localhost isn't reachable from inside the Mattermost container in `testcontainers` mode (it resolves to
// the container's own loopback, not the host's). testcontainersNetworkGatewayIp is reachable
// from both the container and the host-side browser, so it works as a single URL for both — this
// server binds to 0.0.0.0, not just loopback, to accept the former.
function fileServerHost(): string {
return testConfig.useTestContainers ? testConfig.testcontainersNetworkGatewayIp : 'localhost';
/**
* Host used in file-server URLs embedded in posts. Must be reachable from both the host-side
* browser (Markdown images load directly) and the Mattermost container (link previews / metadata).
* On macOS/Windows the bridge gateway does not reach host listeners, so use host.docker.internal;
* on Linux use the bridge gateway IP (works both sides; host.docker.internal is often absent from host DNS).
*/
export function fileServerHost(): string {
if (!testConfig.useTestContainers) {
return 'localhost';
}
if (process.platform === 'darwin' || process.platform === 'win32') {
return DOCKER_HOST_INTERNAL;
}
return testConfig.testcontainersNetworkGatewayIp || DOCKER_HOST_INTERNAL;
}
+2 -1
View File
@@ -14,7 +14,8 @@ const {createServer} = require('http');
const fs = require('fs');
const path = require('path');
const PORT = Number(process.env.PORT) || 3011;
// PORT=0 must request an ephemeral port — don't treat 0 as falsy and fall back to 3011.
const PORT = process.env.PORT !== undefined && process.env.PORT !== '' ? Number(process.env.PORT) : 3011;
const ASSET_DIR = process.env.ASSET_DIR || path.join(__dirname, 'asset');
if (process.argv[2]) {
@@ -176,7 +176,6 @@ test.describe('Post list initial scroll in read channel', () => {
async function waitForScrollToSettle(watcher: PostListScrollWatcher) {
await channelsPage.centerView.toBeVisible();
await page.waitForLoadState('networkidle');
// # Wait until the post list hasn't scrolled for 500ms before returning results
return watcher.waitForObservations(500);
@@ -194,8 +194,6 @@ test.describe('Post list initial scroll in unread channel', () => {
await channelsPage.goto(team.name, 'town-square');
await channelsPage.centerView.getLastPost();
await page.waitForLoadState('networkidle');
// * Verify that the channel starts as unread
await channelsPage.sidebarLeft.assertItemUnread(channel.name);
@@ -220,7 +218,6 @@ test.describe('Post list initial scroll in unread channel', () => {
async function waitForScrollToSettle(watcher: PostListScrollWatcher) {
await channelsPage.centerView.toBeVisible();
await page.waitForLoadState('networkidle');
// # Wait until the post list hasn't scrolled for 500ms before returning results
return watcher.waitForObservations(500);
@@ -281,7 +281,6 @@ test.describe('Post list scroll to permalink', () => {
async function waitForScrollToSettle(watcher: PostListScrollWatcher) {
await channelsPage.centerView.toBeVisible();
await page.waitForLoadState('networkidle');
// # Wait until the post list hasn't scrolled for 500ms before returning results
return watcher.waitForObservations(500);
@@ -38,9 +38,6 @@ test.describe('Post height', () => {
});
// # Enable SVG rendering and let the server fetch metadata from the mock file server.
// AllowedUntrustedInternalConnections only takes effect in `external` mode here — in
// `testcontainers` mode it's fixed at boot via an env var, and a PatchConfig on an env-controlled
// field is accepted but has no real effect.
await adminClient.patchConfig({
ServiceSettings: {
EnableSVGs: true,
@@ -465,9 +462,6 @@ test.describe('Post height', () => {
await testCase.additionalCheck({postComponent});
}
// # Wait for all network requests to finish
await page.waitForLoadState('networkidle');
// * Verify no height changes were detected
expect(await sizeWatcher.getObservations()).toHaveLength(1);
},