Skip to content
Draft
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
117 changes: 117 additions & 0 deletions .github/workflows/e2e-client.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: "E2E — Client (persistence)"

on:
push:
paths:
- "network-protocol/e2e/**"
- "network-protocol/schemas/**"
pull_request:
paths:
- "network-protocol/e2e/**"
- "network-protocol/schemas/**"
workflow_dispatch:
inputs:
persistence_image:
description: "Persistence Docker image (e.g. harbor.example.com/dyingstar/persistence:latest)"
required: true
default: "harbor.example.com/dyingstar/persistence:latest"
scenario_filter:
description: "Optional scenario path filter (e.g. persistence/get-all-items)"
required: false
default: "persistence"

jobs:
e2e-client:
name: Client scenarios
runs-on: ubuntu-latest

env:
PERSISTENCE_IMAGE: ${{ github.event.inputs.persistence_image || 'harbor.example.com/dyingstar/persistence:latest' }}
SCENARIO_FILTER: ${{ github.event.inputs.scenario_filter || 'persistence' }}
NETWORK_PROTOCOL_SCHEMAS_DIR: ${{ github.workspace }}/network-protocol/schemas
# Used by client scenarios (${PERSISTENCE_WS_URL} in scenario.json)
PERSISTENCE_WS_URL: ws://localhost:9100/ws

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: network-protocol/e2e/package.json

- name: Install e2e dependencies
working-directory: network-protocol/e2e
run: npm ci

- name: Log in to Harbor
uses: docker/login-action@v3
with:
registry: harbor.example.com
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}

- name: Pull service images
run: docker pull "$PERSISTENCE_IMAGE"

# ScyllaDB — required by the persistence service.
# We run it on host network so the persistence container can reach it at 127.0.0.1.
- name: Start ScyllaDB
run: |
docker run --rm --detach \
--network host \
--name scylladb-e2e \
scylladb/scylla:6.2 \
--smp 1 --memory 512M --developer-mode 1

- name: Wait for ScyllaDB to be ready
run: |
echo "Waiting for ScyllaDB..."
for i in $(seq 1 30); do
if docker exec scylladb-e2e nodetool status 2>/dev/null | grep -q "^UN"; then
echo "ScyllaDB is ready"
break
fi
echo " attempt $i/30 — not ready yet"
sleep 5
done
docker exec scylladb-e2e nodetool status

- name: Start persistence service
run: |
docker run --rm --detach \
--network host \
--name persistence-e2e \
-e SCYLLA_NODES=127.0.0.1 \
-e SCYLLA_KEYSPACE=dyingstar \
-e WS_PORT=9100 \
"$PERSISTENCE_IMAGE"

- name: Wait for persistence to be ready
run: |
echo "Waiting for persistence WebSocket..."
for i in $(seq 1 20); do
if curl -sf http://localhost:9101/health > /dev/null 2>&1; then
echo "Persistence is ready"
break
fi
echo " attempt $i/20 — not ready yet"
sleep 3
done

- name: Run client scenarios
working-directory: network-protocol/e2e
run: SCENARIO_FILTER="$SCENARIO_FILTER" npm test

- name: Show persistence logs on failure
if: failure()
run: docker logs persistence-e2e || true

- name: Stop containers (always)
if: always()
run: |
docker stop persistence-e2e || true
docker stop scylladb-e2e || true
76 changes: 76 additions & 0 deletions .github/workflows/e2e-mock.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
name: "E2E — Mock server (Horizon)"

# Run when anything in the e2e framework or schemas changes, or on manual trigger.
on:
push:
paths:
- "network-protocol/e2e/**"
- "network-protocol/schemas/**"
pull_request:
paths:
- "network-protocol/e2e/**"
- "network-protocol/schemas/**"
workflow_dispatch:
inputs:
horizon_image:
description: "Horizon Docker image to test (e.g. harbor.example.com/dyingstar/horizon:latest)"
required: true
default: "harbor.example.com/dyingstar/horizon:latest"
scenario_filter:
description: "Optional scenario path filter (e.g. horizon/startup-persistence)"
required: false
default: "horizon"

jobs:
e2e-mock:
name: Mock-server scenarios
runs-on: ubuntu-latest

env:
HORIZON_IMAGE: ${{ github.event.inputs.horizon_image || 'harbor.example.com/dyingstar/horizon:latest' }}
SCENARIO_FILTER: ${{ github.event.inputs.scenario_filter || 'horizon' }}
NETWORK_PROTOCOL_SCHEMAS_DIR: ${{ github.workspace }}/network-protocol/schemas

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: network-protocol/e2e/package.json

- name: Install e2e dependencies
working-directory: network-protocol/e2e
run: npm ci

- name: Log in to Harbor
uses: docker/login-action@v3
with:
registry: harbor.example.com
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_PASSWORD }}

- name: Pull Horizon image
run: docker pull "$HORIZON_IMAGE"

# Start the TypeScript mock servers in the background, then launch Horizon.
# Both processes share the host network so Horizon connects to localhost:<port>.
- name: Run mock-server scenarios
working-directory: network-protocol/e2e
run: |
# Start Horizon with the CI plugins.toml (redirects all service URLs to localhost)
docker run --rm --detach \
--network host \
--name horizon-e2e \
-v "${{ github.workspace }}/network-protocol/e2e/ci/plugins.toml:/app/plugins.toml:ro" \
"$HORIZON_IMAGE"

# Run the Mocha tests (mock servers start inside the test process)
SCENARIO_FILTER="$SCENARIO_FILTER" npm test

- name: Stop Horizon container (always)
if: always()
run: docker stop horizon-e2e || true
57 changes: 57 additions & 0 deletions e2e/ci/plugins.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ============================================================================
# CI override of plugins.toml for the e2e mock-server test job.
#
# All service URLs point to localhost so that the Horizon Docker container
# (run with --network host) connects to the TypeScript mock WebSocket servers
# started by the test runner on the same host.
# ============================================================================

[ds_game_server]
log_level = "info"
game_servers = ["ws://127.0.0.1:8980"]
servers_mode = "single"
split_rule = "fps:20"

[ds_genericprops]
log_level = "info"

[ds_player_authentication]
log_level = "info"

[ds_props]
log_level = "info"

[ds_services]
log_level = "info"
resources_dynamic_address = "127.0.0.1:9200"

[ds_audio]
log_level = "info"

[ds_bridge]
log_level = "debug"

[[ds_bridge.services]]
name = "persistence"
url = "ws://127.0.0.1:9100/ws"
subscribe = [
"plugin:genericprops:create_object",
"plugin:genericprops:create_object_from_gameserver",
"plugin:genericprops:update_object",
"plugin:genericprops:update_object_from_external",
"plugin:bridge_persistence:get_all_items",
"plugin:bridge_persistence:player_spawn",
]

[[ds_bridge.services]]
name = "monitoring"
url = "ws://127.0.0.1:9300/ws"
subscribe = [
"core:player_connected",
"core:player_disconnected",
"plugin:genericprops:create_object",
"plugin:genericprops:create_object_from_gameserver",
"plugin:ds_player_authentication:player_authenticated",
"plugin:ds_game_server:server_registered",
"plugin:ds_game_server:server_unregistered",
]
Loading
Loading