Skip to content
Open
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
13 changes: 11 additions & 2 deletions .github/workflows/pr-verify.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,17 @@ jobs:


e2e:
name: e2e (${{ matrix.runtime.label }})
needs: formatting-and-quick-compile
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
runtime:
- label: Spring Boot
argument: spring-boot
- label: Docker Tomcat
argument: docker-tomcat
steps:
- uses: actions/checkout@v4
- name: Register JVM thread dump on cancel
Expand All @@ -160,9 +169,9 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 18
- name: Run end-to-end tests of RDF4J Server and Workbench
- name: Run end-to-end tests of RDF4J Server and Workbench (${{ matrix.runtime.label }})
working-directory: ./e2e
run: exec ../scripts/ci/run-with-thread-dump.sh ./run.sh
run: exec ../scripts/ci/run-with-thread-dump.sh ./run.sh ${{ matrix.runtime.argument }}

frontend-unit-tests:
needs: formatting-and-quick-compile
Expand Down
21 changes: 19 additions & 2 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# End-to-end tests

This directory contains end-to-end tests for the project. The suite now boots the RDF4J Server and Workbench using a Spring Boot wrapper with an embedded Tomcat instance, so Docker is no longer required.
This directory contains end-to-end tests for the project. The suite can run against either the Spring Boot wrapper with embedded Tomcat or the Docker image that deploys the regular WAR files to Tomcat.

The tests are written using Microsoft Playwright and interact with the server and workbench in a real browser.

Expand All @@ -11,7 +11,24 @@ Requirements:
- maven
- npm
- npx
- docker (for `docker-tomcat`)

The tests can be run using the `run.sh` script. The script builds the Spring Boot runner, launches it in the background, waits until the HTTP endpoints are reachable, and then executes the Playwright test suite.
The tests can be run using the `run.sh` script. The script builds the selected runtime, waits until the HTTP endpoints are reachable, and then executes the Playwright test suite.

Run against the Spring Boot implementation:

```bash
./run.sh spring-boot
```

Run against the Docker/Tomcat image:

```bash
./run.sh docker-tomcat
```

The default runtime is `spring-boot`, so `./run.sh` keeps the original local behavior.

If Playwright browsers are already installed locally, set `E2E_SKIP_PLAYWRIGHT_INSTALL=true` to skip the browser installer.

To run the tests interactively use `npx playwright test --ui`
174 changes: 137 additions & 37 deletions e2e/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,29 @@
# SPDX-License-Identifier: BSD-3-Clause
#

set -e
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
E2E_DIR="${ROOT_DIR}/e2e"
DOCKER_DIR="${ROOT_DIR}/docker"
SERVER_RUNTIME="${1:-${E2E_SERVER_RUNTIME:-spring-boot}}"
SERVER_PID=""
DOCKER_STARTED="false"
SPRING_BOOT_DATA_DIR=""

cleanup() {
stop_spring_boot() {
if [ -z "${SERVER_PID:-}" ]; then
return
fi

# If the process is already gone, nothing to do
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
return
fi

echo "Sending SIGINT to server-boot module (pid=$SERVER_PID)"
echo "Sending SIGINT to server-boot module (pid=${SERVER_PID})"
kill -s INT "$SERVER_PID" 2>/dev/null || true

# Wait for graceful shutdown after SIGINT
for i in 1 2 3 4 5 6 7 8 9 10; do
for _ in 1 2 3 4 5 6 7 8 9 10; do
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "server-boot module stopped gracefully after SIGINT"
wait "$SERVER_PID" 2>/dev/null || true
Expand All @@ -38,12 +42,10 @@ cleanup() {
sleep 0.5
done

# Still alive: send a more aggressive TERM
echo "Sending SIGTERM to server-boot module (pid=$SERVER_PID)"
echo "Sending SIGTERM to server-boot module (pid=${SERVER_PID})"
kill "$SERVER_PID" 2>/dev/null || true

# Wait for graceful shutdown after SIGTERM
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo "server-boot module stopped after SIGTERM"
wait "$SERVER_PID" 2>/dev/null || true
Expand All @@ -52,47 +54,145 @@ cleanup() {
sleep 0.5
done

# Still alive after: kill definitively
echo "Sending SIGKILL to server-boot module (pid=$SERVER_PID)"
echo "Sending SIGKILL to server-boot module (pid=${SERVER_PID})"
kill -9 "$SERVER_PID" 2>/dev/null || true
wait "$SERVER_PID" 2>/dev/null || true
}

trap cleanup EXIT
stop_docker_tomcat() {
if [ "${DOCKER_STARTED}" != "true" ]; then
return
fi

echo "Stopping Docker/Tomcat RDF4J stack"
(cd "$DOCKER_DIR" && APP_SERVER=tomcat docker compose down -v) || true
}

cleanup() {
local status="${1:-$?}"
trap - EXIT INT TERM
stop_spring_boot
stop_docker_tomcat
exit "$status"
}

usage() {
echo "Usage: $0 [spring-boot|docker-tomcat]" >&2
}

install_e2e_dependencies() {
cd "$E2E_DIR"

if [ ! -d "node_modules" ]; then
echo "Installing E2E npm dependencies"
npm ci
fi

if [ "${E2E_SKIP_PLAYWRIGHT_INSTALL:-false}" = "true" ]; then
echo "Skipping Playwright browser install"
else
echo "Installing Playwright browsers"
npx playwright install --with-deps
fi
}

npm install
wait_for_url() {
local label="$1"
local url="$2"

cd ..
printf 'Waiting for %s at %s' "$label" "$url"
for _ in $(seq 1 90); do
if curl --fail --location --silent --output /dev/null "$url"; then
echo ""
echo "${label} is ready"
return
fi
ensure_runtime_running
printf '.'
sleep 1
done

mvn -q install -Pquick
echo ""
echo "Timed out waiting for ${label} at ${url}" >&2
return 1
}

mvn -pl tools/server-boot spring-boot:run &
SERVER_PID=$!
# server-boot module will be stopped automatically on script exit (see cleanup trap above).
ensure_runtime_running() {
if [ -n "${SERVER_PID:-}" ] && ! kill -0 "$SERVER_PID" 2>/dev/null; then
echo ""
echo "server-boot module exited before RDF4J became ready" >&2
wait "$SERVER_PID" 2>/dev/null || true
SERVER_PID=""
return 1
fi

cd e2e
if [ "${DOCKER_STARTED}" = "true" ]; then
local container_id
container_id="$(cd "$DOCKER_DIR" && APP_SERVER=tomcat docker compose ps -q rdf4j 2>/dev/null || true)"
if [ -n "$container_id" ] && [ "$(docker inspect -f '{{.State.Running}}' "$container_id" 2>/dev/null || echo false)" != "true" ]; then
echo ""
echo "Docker/Tomcat container exited before RDF4J became ready" >&2
(cd "$DOCKER_DIR" && APP_SERVER=tomcat docker compose logs --tail=200 rdf4j) || true
return 1
fi
fi
}

sleep 10
wait_for_rdf4j() {
wait_for_url "RDF4J Server" "http://localhost:8080/rdf4j-server/"
wait_for_url "RDF4J Workbench" "http://localhost:8080/rdf4j-workbench/"
}

if [ ! -d 'node_modules' ]; then
echo "npm ci"
npm ci
fi
start_spring_boot() {
echo "Building RDF4J for Spring Boot E2E"
(cd "$ROOT_DIR" && mvn install -Pquick)

npx playwright install --with-deps # install browsers
npx playwright test
SPRING_BOOT_DATA_DIR="${E2E_DATA_DIR:-$(mktemp -d "${TMPDIR:-/tmp}/rdf4j-e2e.XXXXXX")}"
echo "Using RDF4J app data directory ${SPRING_BOOT_DATA_DIR}"

status_npx=$?
echo "Starting RDF4J Server and Workbench with Spring Boot"
(
cd "$ROOT_DIR"
mvn -pl tools/server-boot spring-boot:run \
-Dspring-boot.run.jvmArguments="-Dorg.eclipse.rdf4j.appdata.basedir=${SPRING_BOOT_DATA_DIR}"
) &
SERVER_PID=$!
}

cd ..
start_docker_tomcat() {
echo "Building Docker/Tomcat RDF4J image"
(cd "$DOCKER_DIR" && APP_SERVER=tomcat ./build.sh)

# test for error code
if [ $status_npx -ne 0 ]; then
echo "Error in E2E test"
exit $status_npx
fi
echo "Starting Docker/Tomcat RDF4J container"
(cd "$DOCKER_DIR" && APP_SERVER=tomcat docker compose up --force-recreate -d)
DOCKER_STARTED="true"
}

echo "E2E test OK"
run_playwright() {
cd "$E2E_DIR"
npx playwright test
}

# don't redo the whole build process just for making another docker image
export SKIP_BUILD="skip"
trap 'cleanup $?' EXIT
trap 'cleanup 130' INT
trap 'cleanup 143' TERM

case "$SERVER_RUNTIME" in
spring-boot)
install_e2e_dependencies
start_spring_boot
;;
docker | docker-tomcat | tomcat)
install_e2e_dependencies
start_docker_tomcat
;;
*)
usage
exit 2
;;
esac

wait_for_rdf4j
run_playwright

echo "E2E test OK (${SERVER_RUNTIME})"
Loading
Loading