Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/manual-test-matrix-workflow.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
on:
workflow_dispatch:
inputs:
verbose:
type: boolean
description: "Enable verbose Berg logging"
default: false
name: "Manual matrix test execution"
concurrency:
group: ${{ github.ref }}
Expand Down Expand Up @@ -30,6 +35,8 @@ jobs:
"mail",
"microprofile",
"metrics",
"micrometer",
"opentelemetry",
"runtime",
"security-manager",
"system-property",
Expand Down Expand Up @@ -71,6 +78,9 @@ jobs:
- name: "Run compile"
run: "npm run compile"
- name: "Run test(s)"
env:
BERG_VERBOSE: ${{ inputs.verbose }}
DEBUG: ${{ inputs.verbose && 'testcontainers*' || '' }}
run: "npm run test:compiled -- --browser=firefox --specs=packages/testsuite/cypress/e2e/${{ matrix.specs }}"
- name: "Archive test report(s) and video(s)"
if: ${{ !cancelled() }}
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/manual-test-worfklow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ on:
spec:
type: string
description: "Spec to run"
verbose:
type: boolean
description: "Enable verbose Berg logging"
default: false
name: "Manual job execution"
jobs:
call-reusable-workflow:
uses: "./.github/workflows/reusable-build-project-workflow.yaml"
with:
spec: ${{ inputs.spec }}
verbose: ${{ inputs.verbose }}
2 changes: 2 additions & 0 deletions .github/workflows/on-pull-request-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
packages/testsuite/cypress/e2e/**/*.cy.ts
- name: "Run tests of modified files"
if: steps.changed-files-specific.outputs.any_changed == 'true'
env:
TESTCONTAINERS_RYUK_DISABLED: true
run: |
SPECS="${{ steps.changed-files-specific.outputs.all_changed_files }}"
KEYCLOAK_IMAGE=quay.io/keycloak/keycloak:24.0 npm run test -- --browser=chrome --specs=$SPECS
13 changes: 13 additions & 0 deletions .github/workflows/reusable-build-project-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
type: string
required: false
description: "Comma delimited test spec file(s) to run."
verbose:
type: boolean
required: false
default: false
description: "Enable verbose Berg logging"
jobs:
build_project_job:
name: "Build Project"
Expand Down Expand Up @@ -36,7 +41,15 @@ jobs:
run: "npm run compile"
- name: "Run all tests"
if: "${{ github.event.inputs.spec == '' }}"
env:
TESTCONTAINERS_RYUK_DISABLED: true
BERG_VERBOSE: ${{ inputs.verbose }}
DEBUG: ${{ inputs.verbose && 'testcontainers*' || '' }}
run: "npm run test:compiled"
- name: "Run specific test"
if: "${{ github.event.inputs.spec != '' }}"
env:
TESTCONTAINERS_RYUK_DISABLED: true
BERG_VERBOSE: ${{ inputs.verbose }}
DEBUG: ${{ inputs.verbose && 'testcontainers*' || '' }}
run: "npm run test:compiled -- --specs=${{ inputs.spec }}"
2 changes: 2 additions & 0 deletions .github/workflows/scheduled-run-all-tests-workflow.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
"jsf",
"mail",
"metrics",
"micrometer",
"opentelemetry",
"runtime",
"security-manager",
"system-property",
Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,9 @@ Following tools are required to run the test suite
- recommended version is 22.
- [Node Version Manager (nvm)](https://github.com/nvm-sh/nvm) is recommended optional tool to install & manage multiple Node environments
- [npx](https://github.com/npm/npx) CLI tool used to exeute binaries from project's `node_modules` directly (instead of providing absolute/relative path to the commannds). It is used in multiple build steps.
- [Podman](https://podman.io) | [Docker](https://www.docker.com) as a container runtime used by TestContainers. Note that when using Podman as container runtime you may need to export following environment variables and start podman socket:
- [Podman](https://podman.io) | [Docker](https://www.docker.com) as a container runtime used by TestContainers. Podman is auto-detected: `DOCKER_HOST`, `TESTCONTAINERS_RYUK_DISABLED`, and `podman.socket` are configured automatically. To override, set the environment variables manually before running the test suite:
- `export TESTCONTAINERS_RYUK_DISABLED=true`
- `export DOCKER_HOST=unix:///run/user/$UID/podman/podman.sock`
- the path can be found by command `podman info --debug` and look for `path` in `remoteSocket` section.
- `systemctl --user start podman.socket`
- Java. Yes we'll need Java to write deployments/applications that will be deployed onto the running WildFly container.
- [Maven](https://maven.apache.org). Yes, we'll need Maven to ease up the development of the deployed applications & downloading needed JDBC drivers for datasource & drivers UI tests. Maven is mostly used embedded by [node-maven](https://github.com/headcr4sh/node-maven) JS wrapper to execute Maven & Java related tasks into the build automation.

Expand Down Expand Up @@ -96,6 +94,21 @@ Following is a table of supported environment properties that can be used when r
| `MYSQL_IMAGE` | `docker.io/library/mysql:latest` | MySQL image to be used for datasource tests |
| `MARIADB_IMAGE` | `docker.io/library/mariadb:latest` | MariaDB image to be used for datasource tests |
| `MSSQL_IMAGE` | `mcr.microsoft.com/mssql/server:2022-latest` | Microsoft SQL Server image to be used for datasource tests |
| `BERG_VERBOSE` | `false` | Enable verbose Berg logging (debug messages for container setup, CLI commands, etc.) |

## JDBC drivers

JDBC driver jars are used in datasource tests. They are downloaded automatically by Maven during `npm install` (via `npm run resources`) and placed into `packages/testsuite/cypress/fixtures/jdbc-drivers/`. These jars are `.gitignore`d and built fresh each time.

### Where versions are defined

Driver versions are managed in [`packages/resources/pom.xml`](packages/resources/pom.xml) under `<dependencyManagement>`. The download is configured in [`packages/resources/modules/jdbc-drivers/pom.xml`](packages/resources/modules/jdbc-drivers/pom.xml).

### How to update

1. Update the version in `packages/resources/pom.xml` under `<dependencyManagement>`
2. Run `npm install` or `npm run resources` to download the new jars
3. No test spec changes needed -- driver paths are resolved automatically at runtime by the `resolve:jdbc:driver` Cypress task using the artifact prefix (e.g., `postgresql`, `mysql-connector-j`)

## Custom method documentation

Expand Down
4 changes: 2 additions & 2 deletions config/containers/database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PullPolicy, GenericContainer, StartedTestContainer, Wait } from "testcontainers";
import { DatabaseConfig } from "../interfaces";
import { handleContainerError } from "../helpers";
import { handleContainerError, logger } from "../helpers";

export function startDatabaseContainer(
config: DatabaseConfig,
Expand All @@ -18,7 +18,7 @@ export function startDatabaseContainer(
return containerBuilder
.start()
.then((container) => {
console.log(config.successMessage);
logger.debug(config.successMessage);
startedContainersMap.set(config.containerMapKey, container);
return container;
})
Expand Down
8 changes: 4 additions & 4 deletions config/containers/wildfly.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
MANAGEMENT_INTERFACE_ADDRESS,
} from "../../cypress.config";
import { WildflyManagementResponse } from "../interfaces";
import { buildLocalhostUrl } from "../helpers";
import { buildLocalhostUrl, logger } from "../helpers";

export function pollWildflyState(managementApi: string, container: StartedTestContainer): Promise<string> {
const startTime = new Date().getTime();
Expand All @@ -34,7 +34,7 @@ export function pollWildflyState(managementApi: string, container: StartedTestCo
}
})
.catch(() => {
console.log("WildFly server is not ready yet");
logger.debug("WildFly server is not ready yet");
});
}, WILDFLY_POLL_INTERVAL_MS);
});
Expand All @@ -61,7 +61,7 @@ export function configureWildflyNetworkMode(
networkName?: string,
): Promise<{ portOffset: number }> {
if (useHostMode) {
console.log("host mode");
logger.debug("host mode");
return findAPortNotInUse(WILDFLY_PORT_RANGE.min, WILDFLY_PORT_RANGE.max).then((freePort) => {
const portOffset = freePort - WILDFLY_PORT_RANGE.min;
wildfly
Expand All @@ -75,7 +75,7 @@ export function configureWildflyNetworkMode(
return { portOffset };
});
} else {
console.log(`default network mode, network name: ${networkName}`);
logger.debug(`default network mode, network name: ${networkName}`);
wildfly
.withNetworkMode(networkName!)
.withNetworkAliases("wildfly")
Expand Down
4 changes: 3 additions & 1 deletion config/helpers/error-handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { logger } from "./logger";

export function handleContainerError(err: unknown): Error {
console.log(err);
logger.error(err);
return err instanceof Error ? err : new Error(JSON.stringify(err));
}
1 change: 1 addition & 0 deletions config/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./error-handler";
export * from "./url-builder";
export * from "./container-helpers";
export * from "./logger";
9 changes: 9 additions & 0 deletions config/helpers/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const isVerbose = process.env.BERG_VERBOSE === "true";

export const logger = {
error: (...args: unknown[]) => console.error("[berg]", ...args),
info: (...args: unknown[]) => console.log("[berg]", ...args),
debug: (...args: unknown[]) => {
if (isVerbose) console.log("[berg:debug]", ...args);
},
};
3 changes: 2 additions & 1 deletion config/tasks/cli-tasks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios from "axios";
import { ExecuteCliParams, AxiosErrorResponse } from "../interfaces";
import { logger } from "../helpers";

export function createExecuteCli() {
return ({ managementApi, operation, address, ...args }: ExecuteCliParams) => {
Expand All @@ -11,7 +12,7 @@ export function createExecuteCli() {
})
.then((response) => response.data as unknown)
.catch((err: AxiosErrorResponse) => {
console.log(err);
logger.error(err);
throw new Error(err.response.data);
});
};
Expand Down
4 changes: 2 additions & 2 deletions config/tasks/keycloak-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
FIXTURES_PATH,
} from "../../cypress.config";
import { StartKeycloakContainerParams } from "../interfaces";
import { buildLocalhostUrl, buildKeycloakStartCommand, handleContainerError } from "../helpers";
import { buildLocalhostUrl, buildKeycloakStartCommand, handleContainerError, logger } from "../helpers";

export function createKeycloakContainer(startedContainers: Map<string, StartedTestContainer>) {
return ({ name }: StartKeycloakContainerParams) => {
Expand All @@ -35,7 +35,7 @@ export function createKeycloakContainer(startedContainers: Map<string, StartedTe
.then((keycloakContainer) => {
startedContainers.set(name, keycloakContainer);
const keycloakServer = buildLocalhostUrl(freePort);
console.log(`Keycloak is ready: ${keycloakServer}`);
logger.info(`Keycloak is ready: ${keycloakServer}`);
return keycloakServer;
})
.catch((err: unknown) => {
Expand Down
19 changes: 12 additions & 7 deletions config/tasks/wildfly-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
WILDFLY_MANAGEMENT_PORT,
JBOSS_CLI_PATH,
} from "../../cypress.config";
import { StartWildflyContainerParams, ExecuteInContainerParams, AxiosErrorResponse } from "../interfaces";
import { getHostnameMapping, handleContainerError, calculateManagementPort } from "../helpers";
import { StartWildflyContainerParams, ExecuteInContainerParams } from "../interfaces";
import { getHostnameMapping, handleContainerError, calculateManagementPort, logger } from "../helpers";
import { configureWildflyNetworkMode, configureWildflyPostStart } from "../containers";

export function createWildflyContainer(
Expand Down Expand Up @@ -53,7 +53,7 @@ export function createWildflyContainer(
);
})
.then((wildflyServer) => {
console.log(`WildFly server is ready: ${wildflyServer}`);
logger.info(`WildFly server is ready: ${wildflyServer}`);
return wildflyServer;
})
.catch((err: unknown) => {
Expand All @@ -67,7 +67,7 @@ export function createExecuteInContainer(
startedContainersManagementPorts: Map<string, number>,
) {
return ({ containerName, command }: ExecuteInContainerParams) => {
console.log(`CLI commands: ${command}`);
logger.debug(`CLI commands: ${command}`);
const containerToExec = startedContainers.get(containerName);
let managementPort = startedContainersManagementPorts.get(containerName);
managementPort = managementPort ?? WILDFLY_MANAGEMENT_PORT;
Expand All @@ -86,12 +86,17 @@ export function createExecuteInContainer(
if (value.exitCode === 0) {
return value;
} else {
console.log(value);
logger.debug(value);
throw new Error(`Command failed with exit code ${value.exitCode}: ${value.output || ""}`);
}
})
.catch((err: AxiosErrorResponse) => {
throw new Error(err.response.data);
// Only container.exec() errors and plain Errors from the .then() block can reach here.
// AxiosErrorResponse is not possible — Axios is not used in this function.
.catch((err: unknown) => {
if (err instanceof Error) {
Comment thread
OndrejKotek marked this conversation as resolved.
throw err;
}
throw new Error(String(err));
});
};
}
23 changes: 19 additions & 4 deletions cypress.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { defineConfig } from "cypress";
import { StartedTestContainer, StoppedTestContainer } from "testcontainers";
import { existsSync, unlinkSync } from "fs";
import { existsSync, unlinkSync, readdirSync } from "fs";
import {
createWildflyContainer,
createExecuteInContainer,
Expand All @@ -11,6 +11,7 @@ import {
createSqlserverContainer,
createExecuteCli,
} from "./config/tasks";
import { logger } from "./config/helpers";

// WildFly configuration
export const DEFAULT_WILDFLY_CONFIG = "standalone-insecure.xml";
Expand Down Expand Up @@ -61,6 +62,7 @@ export const MANAGEMENT_INTERFACE_ADDRESS = ["core-service", "management", "mana
export const LOCALHOST_IP = "127.0.0.1";

export default defineConfig({
allowCypressEnv: false,
defaultCommandTimeout: 16000,
reporter: require.resolve("cypress-multi-reporters/index.js"),
reporterOptions: {
Expand Down Expand Up @@ -89,13 +91,26 @@ export default defineConfig({
"start:sqlserver:container": createSqlserverContainer(startedContainers, config.env.NETWORK_NAME as string),
"execute:in:container": createExecuteInContainer(startedContainers, startedContainersManagementPorts),
"execute:cli": createExecuteCli(),
"resolve:jdbc:driver": (prefix: string) => {
const jdbcDir = FIXTURES_PATH + "/jdbc-drivers";
const files = readdirSync(jdbcDir);
const match = files.find((f) => f.startsWith(prefix) && f.endsWith(".jar"));
if (!match) {
throw new Error(`No JDBC driver jar found matching prefix "${prefix}" in ${jdbcDir}`);
}
return `/home/fixtures/jdbc-drivers/${match}`;
},
"stop:containers": () => {
const promises: Promise<StoppedTestContainer>[] = [];
const promises: Promise<StoppedTestContainer | void>[] = [];
startedContainers.forEach((container, key) => {
console.log("Stopping container for test " + key);
logger.info("Stopping container for test " + key);
startedContainers.delete(key);
startedContainersManagementPorts.delete(key);
promises.push(container.stop());
promises.push(
container.stop().catch((err: unknown) => {
logger.error(`Failed to stop container ${key}: ${err instanceof Error ? err.message : String(err)}`);
}),
);
});
return Promise.all(promises);
},
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,17 @@
"license": "ISC",
"devDependencies": {
"@cypress/xvfb": "1.2.4",
"@eslint/js": "^10.0.1",
"@types/command-line-args": "5.2.3",
"@types/mochawesome": "6.2.4",
"@types/mochawesome": "6.2.5",
"@types/portscanner": "2.1.4",
"@typescript-eslint/eslint-plugin": "^8.46.1",
"@typescript-eslint/parser": "^8.46.1",
"command-line-args": "^6.0.1",
"cpy-cli": "^6.0.0",
"cpy-cli": "^7.0.0",
"cypress-multi-reporters": "^2.0.5",
"del-cli": "^7.0.0",
"eslint": "^9.37.0",
"eslint": "^10.0.2",
"eslint-config-prettier": "^10.1.8",
"mocha-junit-reporter": "2.2.1",
"mochawesome": "^7.1.4",
Expand All @@ -53,7 +54,7 @@
},
"dependencies": {
"axios": "^1.12.2",
"cypress": "15.4.0",
"cypress": "15.11.0",
"portscanner": "^2.2.0",
"testcontainers": "^11.7.1"
}
Expand Down
1 change: 1 addition & 0 deletions packages/berg/src/berg.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import "./container-runtime";
import { PullPolicy, GenericContainer, Network, StartedNetwork, StartedTestContainer } from "testcontainers";

export class Berg {
Expand Down
Loading
Loading