Skip to content

Commit 09e7c55

Browse files
committed
refactoring of the cypress.config.ts
1 parent f8966c7 commit 09e7c55

19 files changed

Lines changed: 579 additions & 324 deletions

config/containers/database.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { PullPolicy, GenericContainer, StartedTestContainer, Wait } from "testcontainers";
2+
import { DatabaseConfig } from "../interfaces";
3+
import { handleContainerError } from "../helpers";
4+
5+
export function startDatabaseContainer(
6+
config: DatabaseConfig,
7+
startedContainersMap: Map<string, StartedTestContainer>,
8+
): Promise<StartedTestContainer> {
9+
const containerBuilder = new GenericContainer(config.image)
10+
.withPullPolicy(PullPolicy.alwaysPull())
11+
.withName(config.name)
12+
.withNetworkAliases(config.name)
13+
.withNetworkMode(config.networkName)
14+
.withExposedPorts(config.port)
15+
.withEnvironment(config.environmentProperties)
16+
.withWaitStrategy(Wait.forLogMessage(new RegExp(config.waitLogMessage)));
17+
18+
return containerBuilder
19+
.start()
20+
.then((container) => {
21+
console.log(config.successMessage);
22+
startedContainersMap.set(config.containerMapKey, container);
23+
return container;
24+
})
25+
.catch((err: unknown) => {
26+
throw handleContainerError(err);
27+
});
28+
}

config/containers/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./database";
2+
export * from "./wildfly";

config/containers/wildfly.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import axios from "axios";
2+
import { GenericContainer, StartedTestContainer } from "testcontainers";
3+
import { findAPortNotInUse } from "portscanner";
4+
import {
5+
WILDFLY_MANAGEMENT_PORT,
6+
WILDFLY_PORT_RANGE,
7+
DEFAULT_WILDFLY_CONFIG,
8+
WILDFLY_READY_TIMEOUT_MS,
9+
WILDFLY_POLL_INTERVAL_MS,
10+
JBOSS_CLI_PATH,
11+
MANAGEMENT_INTERFACE_ADDRESS,
12+
} from "../../cypress.config";
13+
import { WildflyManagementResponse } from "../interfaces";
14+
import { buildLocalhostUrl } from "../helpers";
15+
16+
export function pollWildflyState(managementApi: string, container: StartedTestContainer): Promise<string> {
17+
const startTime = new Date().getTime();
18+
return new Promise<string>((resolve, reject) => {
19+
const interval = setInterval(() => {
20+
if (new Date().getTime() - startTime > WILDFLY_READY_TIMEOUT_MS) {
21+
clearInterval(interval);
22+
reject(new Error("Timeout waiting for WildFly to start"));
23+
}
24+
axios
25+
.post(managementApi, {
26+
operation: "read-attribute",
27+
name: "server-state",
28+
})
29+
.then((response: WildflyManagementResponse) => {
30+
if (response.data.result === "running") {
31+
clearInterval(interval);
32+
const wildflyServer = buildLocalhostUrl(container.getMappedPort(WILDFLY_MANAGEMENT_PORT));
33+
resolve(wildflyServer);
34+
}
35+
})
36+
.catch(() => {
37+
console.log("WildFly server is not ready yet");
38+
});
39+
}, WILDFLY_POLL_INTERVAL_MS);
40+
});
41+
}
42+
43+
export function executeJBossCLI(
44+
container: StartedTestContainer,
45+
managementPort: number,
46+
command: string,
47+
): Promise<string> {
48+
return container
49+
.exec([
50+
`/bin/sh`,
51+
`-c`,
52+
`${JBOSS_CLI_PATH} --connect --controller=localhost:${managementPort} --command="${command}"`,
53+
])
54+
.then((result) => result.output);
55+
}
56+
57+
export function configureWildflyNetworkMode(
58+
wildfly: GenericContainer,
59+
configuration: string,
60+
useHostMode: boolean,
61+
networkName?: string,
62+
): Promise<{ portOffset: number }> {
63+
if (useHostMode) {
64+
console.log("host mode");
65+
return findAPortNotInUse(WILDFLY_PORT_RANGE.min, WILDFLY_PORT_RANGE.max).then((freePort) => {
66+
const portOffset = freePort - WILDFLY_PORT_RANGE.min;
67+
wildfly
68+
.withNetworkMode("host")
69+
.withCommand([
70+
"-c",
71+
configuration || DEFAULT_WILDFLY_CONFIG,
72+
`-Djboss.socket.binding.port-offset=${portOffset.toString()}`,
73+
"-Djboss.node.name=localhost",
74+
] as string[]);
75+
return { portOffset };
76+
});
77+
} else {
78+
console.log(`default network mode, network name: ${networkName}`);
79+
wildfly
80+
.withNetworkMode(networkName!)
81+
.withNetworkAliases("wildfly")
82+
.withExposedPorts(WILDFLY_MANAGEMENT_PORT)
83+
.withCommand(["-c", configuration || DEFAULT_WILDFLY_CONFIG] as string[]);
84+
return Promise.resolve({ portOffset: 0 });
85+
}
86+
}
87+
88+
export function configureWildflyPostStart(
89+
container: StartedTestContainer,
90+
halPort: string,
91+
useHostMode: boolean,
92+
managementPort?: number,
93+
): Promise<string> {
94+
if (useHostMode) {
95+
const effectiveManagementPort = managementPort!;
96+
return executeJBossCLI(
97+
container,
98+
effectiveManagementPort,
99+
`/core-service=management/management-interface=http-interface:list-add(name=allowed-origins,value=${buildLocalhostUrl(Number(halPort))}`,
100+
)
101+
.then(() => executeJBossCLI(container, effectiveManagementPort, "reload"))
102+
.then(() => executeJBossCLI(container, effectiveManagementPort, "read-attribute server-state"))
103+
.then((output) => {
104+
if (output.includes("running")) {
105+
return buildLocalhostUrl(effectiveManagementPort);
106+
}
107+
throw new Error("WildFly did not reach running state");
108+
});
109+
} else {
110+
const managementApi = buildLocalhostUrl(container.getMappedPort(WILDFLY_MANAGEMENT_PORT), "/management");
111+
112+
return axios
113+
.post(managementApi, {
114+
operation: "list-add",
115+
address: MANAGEMENT_INTERFACE_ADDRESS,
116+
name: "allowed-origins",
117+
value: buildLocalhostUrl(Number(halPort)),
118+
})
119+
.then(() =>
120+
axios.post(managementApi, {
121+
operation: "reload",
122+
}),
123+
)
124+
.then(() => pollWildflyState(managementApi, container));
125+
}
126+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { hostname } from "os";
2+
import { WILDFLY_MANAGEMENT_PORT, LOCALHOST_IP } from "../../cypress.config";
3+
4+
export function calculateManagementPort(portOffset: number): number {
5+
return portOffset + WILDFLY_MANAGEMENT_PORT;
6+
}
7+
8+
export function getHostnameMapping(): Array<{ host: string; ipAddress: string }> {
9+
return [{ host: hostname(), ipAddress: LOCALHOST_IP }];
10+
}
11+
12+
export function buildKeycloakStartCommand(port: number): string[] {
13+
return ["start-dev", "--db=dev-mem", `--http-port=${port.toString()}`, "--import-realm"];
14+
}

config/helpers/error-handler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export function handleContainerError(err: unknown): Error {
2+
console.log(err);
3+
return err instanceof Error ? err : new Error(JSON.stringify(err));
4+
}

config/helpers/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "./error-handler";
2+
export * from "./url-builder";
3+
export * from "./container-helpers";

config/helpers/url-builder.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export function buildLocalhostUrl(port: number, path?: string): string {
2+
const baseUrl = `http://localhost:${port}`;
3+
return path ? `${baseUrl}${path}` : baseUrl;
4+
}

config/interfaces.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Environment } from "testcontainers/build/types";
2+
3+
export interface DatabaseConfig {
4+
name: string;
5+
image: string;
6+
port: number;
7+
waitLogMessage: string;
8+
environmentProperties: Environment;
9+
networkName: string;
10+
containerMapKey: string;
11+
successMessage: string;
12+
}
13+
14+
export interface WildflyManagementResponse {
15+
data: {
16+
result: string;
17+
};
18+
}
19+
20+
export interface AxiosErrorResponse {
21+
response: {
22+
data: string;
23+
};
24+
}
25+
26+
export interface StartWildflyContainerParams {
27+
name: string;
28+
configuration: string;
29+
useNetworkHostMode?: boolean;
30+
}
31+
32+
export interface StartKeycloakContainerParams {
33+
name: string;
34+
}
35+
36+
export interface StartDatabaseContainerParams {
37+
name: string;
38+
environmentProperties: Environment;
39+
}
40+
41+
export interface ExecuteInContainerParams {
42+
containerName: string;
43+
command: string;
44+
}
45+
46+
export interface ExecuteCliParams {
47+
managementApi: string;
48+
operation: string;
49+
address: string[];
50+
[key: string]: unknown;
51+
}

config/tasks/cli-tasks.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import axios from "axios";
2+
import { ExecuteCliParams, AxiosErrorResponse } from "../interfaces";
3+
4+
export function createExecuteCli() {
5+
return ({ managementApi, operation, address, ...args }: ExecuteCliParams) => {
6+
return axios
7+
.post(managementApi, {
8+
operation,
9+
address,
10+
...args,
11+
})
12+
.then((response) => response.data as unknown)
13+
.catch((err: AxiosErrorResponse) => {
14+
console.log(err);
15+
throw new Error(err.response.data);
16+
});
17+
};
18+
}

config/tasks/database-tasks.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { StartedTestContainer } from "testcontainers";
2+
import {
3+
DEFAULT_POSTGRES_IMAGE,
4+
DEFAULT_MYSQL_IMAGE,
5+
DEFAULT_MARIADB_IMAGE,
6+
DEFAULT_SQLSERVER_IMAGE,
7+
POSTGRES_PORT,
8+
MYSQL_PORT,
9+
MARIADB_PORT,
10+
SQLSERVER_PORT,
11+
POSTGRES_STARTED_MSG,
12+
MYSQL_STARTED_MSG,
13+
MARIADB_STARTED_MSG,
14+
SQLSERVER_STARTED_MSG,
15+
} from "../../cypress.config";
16+
import { StartDatabaseContainerParams } from "../interfaces";
17+
import { startDatabaseContainer } from "../containers";
18+
19+
export function createPostgresContainer(startedContainers: Map<string, StartedTestContainer>, networkName: string) {
20+
return ({ name, environmentProperties }: StartDatabaseContainerParams) => {
21+
return startDatabaseContainer(
22+
{
23+
name,
24+
image: process.env.POSTGRES_IMAGE || DEFAULT_POSTGRES_IMAGE,
25+
port: POSTGRES_PORT,
26+
waitLogMessage: POSTGRES_STARTED_MSG,
27+
environmentProperties,
28+
networkName,
29+
containerMapKey: "postgres",
30+
successMessage: "PostgreSQL started successfully",
31+
},
32+
startedContainers,
33+
);
34+
};
35+
}
36+
37+
export function createMysqlContainer(startedContainers: Map<string, StartedTestContainer>, networkName: string) {
38+
return ({ name, environmentProperties }: StartDatabaseContainerParams) => {
39+
return startDatabaseContainer(
40+
{
41+
name,
42+
image: process.env.MYSQL_IMAGE || DEFAULT_MYSQL_IMAGE,
43+
port: MYSQL_PORT,
44+
waitLogMessage: MYSQL_STARTED_MSG,
45+
environmentProperties,
46+
networkName,
47+
containerMapKey: "mysql",
48+
successMessage: "MySQL started successfully",
49+
},
50+
startedContainers,
51+
);
52+
};
53+
}
54+
55+
export function createMariadbContainer(startedContainers: Map<string, StartedTestContainer>, networkName: string) {
56+
return ({ name, environmentProperties }: StartDatabaseContainerParams) => {
57+
return startDatabaseContainer(
58+
{
59+
name,
60+
image: process.env.MARIADB_IMAGE || DEFAULT_MARIADB_IMAGE,
61+
port: MARIADB_PORT,
62+
waitLogMessage: MARIADB_STARTED_MSG,
63+
environmentProperties,
64+
networkName,
65+
containerMapKey: "mariadb",
66+
successMessage: "Mariadb started successfully",
67+
},
68+
startedContainers,
69+
);
70+
};
71+
}
72+
73+
export function createSqlserverContainer(startedContainers: Map<string, StartedTestContainer>, networkName: string) {
74+
return ({ name, environmentProperties }: StartDatabaseContainerParams) => {
75+
return startDatabaseContainer(
76+
{
77+
name,
78+
image: process.env.MSSQL_IMAGE || DEFAULT_SQLSERVER_IMAGE,
79+
port: SQLSERVER_PORT,
80+
waitLogMessage: SQLSERVER_STARTED_MSG,
81+
environmentProperties,
82+
networkName,
83+
containerMapKey: "sqlserver",
84+
successMessage: "SQL server started successfully",
85+
},
86+
startedContainers,
87+
);
88+
};
89+
}

0 commit comments

Comments
 (0)