Switch to TestContainers for manging services in Playwright (#28860)
* Switch to TestContainers for manging services in Playwright Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Flip fixture dependency order Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove mas dep Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Update matrix-authentication-service in Playwright tests Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * delint Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix SMTP port Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Comments Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Strip ansi from playwright logs to make them more readable Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Actually do the update Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Remove access to homeserver.config.baseUrl field in favour of homeserver.baseUrl Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Use sane default_server_config and specify server.invalid in the specific tests which demand it Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Fix mas run Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * break cycle Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * typo Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Iterate Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * prettier Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> * Wire up basics of dendriteHomeserver Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --------- Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
committed by
GitHub
parent
66bbb84e56
commit
f75d1f5a5e
@@ -6,142 +6,32 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as path from "node:path";
|
||||
import * as os from "node:os";
|
||||
import * as fse from "fs-extra";
|
||||
import { Fixtures, PlaywrightTestArgs } from "@playwright/test";
|
||||
|
||||
import { getFreePort } from "../../utils/port";
|
||||
import { Homeserver, HomeserverConfig, HomeserverInstance, StartHomeserverOpts } from "../";
|
||||
import { randB64Bytes } from "../../utils/rand";
|
||||
import { Synapse } from "../synapse";
|
||||
import { Docker } from "../../docker";
|
||||
import { Fixtures as BaseFixtures } from "../../../element-web-test.ts";
|
||||
import { DendriteContainer, PineconeContainer } from "../../../testcontainers/dendrite.ts";
|
||||
import { Services } from "../../../services.ts";
|
||||
|
||||
const dockerConfigDir = "/etc/dendrite/";
|
||||
const dendriteConfigFile = "dendrite.yaml";
|
||||
type Fixture = PlaywrightTestArgs & Services & BaseFixtures;
|
||||
export const dendriteHomeserver: Fixtures<Fixture, {}, Fixture> = {
|
||||
_homeserver: async ({ request }, use) => {
|
||||
const container =
|
||||
process.env["PLAYWRIGHT_HOMESERVER"] === "dendrite"
|
||||
? new DendriteContainer(request)
|
||||
: new PineconeContainer(request);
|
||||
await use(container);
|
||||
},
|
||||
homeserver: async ({ logger, network, _homeserver: homeserver }, use) => {
|
||||
const container = await homeserver
|
||||
.withNetwork(network)
|
||||
.withNetworkAliases("homeserver")
|
||||
.withLogConsumer(logger.getConsumer("dendrite"))
|
||||
.start();
|
||||
|
||||
// Surprisingly, Dendrite implements the same register user Admin API Synapse, so we can just extend it
|
||||
export class Dendrite extends Synapse implements Homeserver, HomeserverInstance {
|
||||
protected image = "matrixdotorg/dendrite-monolith:main";
|
||||
protected entrypoint = "/usr/bin/dendrite";
|
||||
|
||||
/**
|
||||
* Start a dendrite instance: the template must be the name of one of the templates
|
||||
* in the playwright/plugins/dendritedocker/templates directory
|
||||
* @param opts
|
||||
*/
|
||||
public async start(opts: StartHomeserverOpts): Promise<HomeserverInstance> {
|
||||
const denCfg = await cfgDirFromTemplate(this.image, opts);
|
||||
|
||||
console.log(`Starting dendrite with config dir ${denCfg.configDir}...`);
|
||||
|
||||
const dendriteId = await this.docker.run({
|
||||
image: this.image,
|
||||
params: [
|
||||
"-v",
|
||||
`${denCfg.configDir}:` + dockerConfigDir,
|
||||
"-p",
|
||||
`${denCfg.port}:8008/tcp`,
|
||||
"--entrypoint",
|
||||
this.entrypoint,
|
||||
],
|
||||
containerName: `react-sdk-playwright-dendrite`,
|
||||
cmd: ["--config", dockerConfigDir + dendriteConfigFile, "--really-enable-open-registration", "true", "run"],
|
||||
});
|
||||
|
||||
console.log(`Started dendrite with id ${dendriteId} on port ${denCfg.port}.`);
|
||||
|
||||
// Await Dendrite healthcheck
|
||||
await this.docker.exec([
|
||||
"curl",
|
||||
"--connect-timeout",
|
||||
"30",
|
||||
"--retry",
|
||||
"30",
|
||||
"--retry-delay",
|
||||
"1",
|
||||
"--retry-all-errors",
|
||||
"--silent",
|
||||
"http://localhost:8008/_matrix/client/versions",
|
||||
]);
|
||||
|
||||
const dockerUrl = `http://${await this.docker.getContainerIp()}:8008`;
|
||||
this.config = {
|
||||
...denCfg,
|
||||
serverId: dendriteId,
|
||||
dockerUrl,
|
||||
};
|
||||
return this;
|
||||
}
|
||||
|
||||
public async stop(): Promise<string[]> {
|
||||
if (!this.config) throw new Error("Missing existing dendrite instance, did you call stop() before start()?");
|
||||
|
||||
const dendriteLogsPath = path.join("playwright", "dendritelogs", this.config.serverId);
|
||||
await fse.ensureDir(dendriteLogsPath);
|
||||
|
||||
await this.docker.persistLogsToFile({
|
||||
stdoutFile: path.join(dendriteLogsPath, "stdout.log"),
|
||||
stderrFile: path.join(dendriteLogsPath, "stderr.log"),
|
||||
});
|
||||
|
||||
await this.docker.stop();
|
||||
|
||||
await fse.remove(this.config.configDir);
|
||||
|
||||
console.log(`Stopped dendrite id ${this.config.serverId}.`);
|
||||
|
||||
return [path.join(dendriteLogsPath, "stdout.log"), path.join(dendriteLogsPath, "stderr.log")];
|
||||
}
|
||||
}
|
||||
|
||||
export class Pinecone extends Dendrite {
|
||||
protected image = "matrixdotorg/dendrite-demo-pinecone:main";
|
||||
protected entrypoint = "/usr/bin/dendrite-demo-pinecone";
|
||||
}
|
||||
|
||||
async function cfgDirFromTemplate(
|
||||
dendriteImage: string,
|
||||
opts: StartHomeserverOpts,
|
||||
): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
||||
const template = "default"; // XXX: for now we only have one template
|
||||
const templateDir = path.join(__dirname, "templates", template);
|
||||
|
||||
const stats = await fse.stat(templateDir);
|
||||
if (!stats?.isDirectory) {
|
||||
throw new Error(`No such template: ${template}`);
|
||||
}
|
||||
const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), "react-sdk-dendritedocker-"));
|
||||
|
||||
// copy the contents of the template dir, omitting homeserver.yaml as we'll template that
|
||||
console.log(`Copy ${templateDir} -> ${tempDir}`);
|
||||
await fse.copy(templateDir, tempDir, { filter: (f) => path.basename(f) !== dendriteConfigFile });
|
||||
|
||||
const registrationSecret = randB64Bytes(16);
|
||||
|
||||
const port = await getFreePort();
|
||||
const baseUrl = `http://localhost:${port}`;
|
||||
|
||||
// now copy homeserver.yaml, applying substitutions
|
||||
console.log(`Gen ${path.join(templateDir, dendriteConfigFile)}`);
|
||||
let hsYaml = await fse.readFile(path.join(templateDir, dendriteConfigFile), "utf8");
|
||||
hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret);
|
||||
await fse.writeFile(path.join(tempDir, dendriteConfigFile), hsYaml);
|
||||
|
||||
const docker = new Docker();
|
||||
await docker.run({
|
||||
image: dendriteImage,
|
||||
params: ["--entrypoint=", "-v", `${tempDir}:/mnt`],
|
||||
containerName: `react-sdk-playwright-dendrite-keygen`,
|
||||
cmd: ["/usr/bin/generate-keys", "-private-key", "/mnt/matrix_key.pem"],
|
||||
});
|
||||
|
||||
return {
|
||||
port,
|
||||
baseUrl,
|
||||
configDir: tempDir,
|
||||
registrationSecret,
|
||||
};
|
||||
}
|
||||
await use(container);
|
||||
await container.stop();
|
||||
},
|
||||
};
|
||||
|
||||
export function isDendrite(): boolean {
|
||||
return process.env["PLAYWRIGHT_HOMESERVER"] === "dendrite" || process.env["PLAYWRIGHT_HOMESERVER"] === "pinecone";
|
||||
|
||||
Reference in New Issue
Block a user