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
96 changes: 96 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: E2E Tests

on:
pull_request:
branches: [main]
push:
branches: [main]

jobs:
e2e:
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- uses: actions/checkout@v4

# ── Build Docker images ──────────────────────────────────────────────────

- name: Build router image
run: docker build -t starfish-router router/

- name: Build controller image
run: docker build -t starfish-controller controller/

# ── Start the E2E stack ──────────────────────────────────────────────────

- name: Start E2E stack
working-directory: workbench
run: docker compose -f docker-compose.e2e.yml up -d

# ── Wait for services to be healthy ─────────────────────────────────────

- name: Wait for router to be ready
run: |
for i in $(seq 1 30); do
if curl -sf http://localhost:8000/starfish/api/v1/sites/ \
-u admin:1234 > /dev/null 2>&1; then
echo "Router is ready"
exit 0
fi
echo "Waiting for router ($i/30)..."
sleep 5
done
echo "Router did not become ready in time"
docker compose -f workbench/docker-compose.e2e.yml logs router
exit 1

- name: Wait for all three controllers to be ready
run: |
for port in 8001 8002 8003; do
for i in $(seq 1 20); do
if curl -sf http://localhost:${port}/controller/ > /dev/null 2>&1; then
echo "Controller on port $port is ready"
break
fi
echo "Waiting for controller:$port ($i/20)..."
sleep 5
if [ $i -eq 20 ]; then
echo "Controller on port $port did not become ready"
docker compose -f workbench/docker-compose.e2e.yml logs
exit 1
fi
done
done

# ── Install test dependencies ────────────────────────────────────────────

- uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install E2E test dependencies
working-directory: e2e
run: |
pip install -r requirements.txt
playwright install chromium --with-deps

# ── Run E2E tests ────────────────────────────────────────────────────────

- name: Run E2E tests
working-directory: e2e
run: pytest test_fl_workflow.py -v --timeout=300

# ── Collect logs on failure ──────────────────────────────────────────────

- name: Collect service logs on failure
if: failure()
working-directory: workbench
run: docker compose -f docker-compose.e2e.yml logs --tail=200

# ── Tear down ────────────────────────────────────────────────────────────

- name: Tear down E2E stack
if: always()
working-directory: workbench
run: docker compose -f docker-compose.e2e.yml down -v
45 changes: 45 additions & 0 deletions e2e/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""
pytest configuration for Starfish-FL E2E tests.

Requires three Controller instances and a Router to be running:
site-a (coordinator) → http://localhost:8001
site-b (participant 1) → http://localhost:8002
site-c (participant 2) → http://localhost:8003

Start the stack with:
cd workbench
docker-compose -f docker-compose.e2e.yml up -d
"""
from pathlib import Path
import pytest

# Base URLs for each site's Controller web portal
BASE_A = "http://localhost:8001"
BASE_B = "http://localhost:8002"
BASE_C = "http://localhost:8003"

FIXTURES_DIR = Path(__file__).parent / "fixtures"


@pytest.fixture(scope="session")
def base_a():
return BASE_A


@pytest.fixture(scope="session")
def base_b():
return BASE_B


@pytest.fixture(scope="session")
def base_c():
return BASE_C


@pytest.fixture(scope="session")
def fixtures_dir():
return FIXTURES_DIR


# pytest-playwright provides the `browser` fixture (session-scoped, Chromium by default).
# The test creates its own browser contexts so each site runs in an isolated session.
50 changes: 50 additions & 0 deletions e2e/fixtures/site_a.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-0.1441,-0.1729,0
-0.1113,0.702,1
-0.1276,-1.4974,0
0.3323,-0.2673,1
-0.217,0.1159,0
0.2323,1.1636,1
0.6566,0.1105,1
-0.7383,-1.0147,0
0.2463,1.3111,1
0.0417,-0.1063,0
0.5318,-1.4535,0
-0.3123,0.4904,1
0.8734,-0.2406,1
0.3766,0.2482,1
0.7823,-1.1132,0
0.5683,-1.5145,0
-2.6199,-0.6069,0
-0.9158,0.876,0
0.6643,-1.2191,0
0.8474,-1.0022,0
-0.0862,-0.2939,0
0.1144,0.8186,1
0.6384,0.3499,1
0.6499,0.4785,1
-0.627,-0.7174,0
-0.47,0.4993,1
-0.2501,2.3358,1
-0.8193,-1.0989,0
0.7685,1.4218,1
0.5057,0.8358,1
1.4263,-0.094,1
-1.423,-0.5321,0
0.9529,-1.4437,0
0.0335,0.2532,1
-0.3156,0.7236,1
0.5808,2.3214,1
0.62,-0.6094,1
-0.5618,-0.8316,0
0.9523,-0.5668,1
-0.0703,0.7493,1
-0.7235,-0.2937,0
-1.8413,-1.0825,0
-0.5677,0.4158,0
1.1935,-0.0185,1
0.2614,0.168,1
1.0847,0.8934,1
0.2737,-1.0109,0
0.9034,0.381,1
1.2269,-0.0299,1
1.9531,-0.3589,1
50 changes: 50 additions & 0 deletions e2e/fixtures/site_b.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
0.923,0.4512,1
0.7233,-0.0443,1
-1.0781,1.5081,1
0.9727,-0.7082,1
1.8577,-0.4548,1
-0.5343,-0.0862,0
-1.0844,0.5339,0
0.227,0.003,1
0.5453,-0.0892,1
0.4942,-0.3827,1
0.532,-1.9094,0
1.7455,-0.4307,1
-0.0587,0.1269,1
-0.3009,0.2198,0
-0.746,0.9339,1
0.4356,1.2762,1
1.1825,-0.4125,1
-1.4852,-0.4864,0
1.7045,0.4217,1
0.981,0.7018,1
0.0284,-0.0743,0
-0.3779,-1.9053,0
0.0429,2.1667,1
0.5019,-0.1877,1
-0.0707,-1.8881,0
0.2117,1.7268,1
-0.2287,1.45,1
-0.5615,0.103,0
0.6509,-1.4532,0
-0.4305,-2.0542,0
-1.6488,1.376,0
-2.3285,-0.2571,0
0.2718,0.3693,1
-0.2839,-0.4596,0
0.1923,-1.442,0
-1.6745,-1.0611,0
-0.6087,1.8806,1
0.6749,-0.0752,1
-0.2297,-0.6161,0
-1.6465,-0.692,0
-1.3635,1.0704,0
-0.6035,0.5375,0
0.974,-0.0736,1
-0.5238,-1.5097,0
1.6814,0.4615,1
0.421,-2.1342,0
0.7027,0.4824,1
1.6134,-0.5055,1
1.59,0.1336,1
-0.0616,0.1335,1
50 changes: 50 additions & 0 deletions e2e/fixtures/site_c.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-0.5502,0.3791,0
0.3269,0.6814,1
0.0472,-0.7591,0
-1.1268,1.018,0
-2.2916,-0.5674,0
-1.0441,0.0705,0
-0.5294,-0.4563,0
0.7901,-0.559,1
-1.1554,0.9792,0
0.847,1.2015,1
0.5345,-0.736,0
1.1589,-0.402,1
0.1723,-1.1119,0
-0.6486,0.4551,0
0.6743,0.0323,1
1.2337,-0.081,1
-2.0865,0.5066,0
-2.1614,-1.6876,0
-0.4361,0.5441,1
0.5203,-0.2828,1
0.3496,-1.2991,0
2.3865,-0.2576,1
-0.1468,-0.2323,0
1.121,0.1653,1
0.1744,-0.3727,0
-0.3784,0.1916,0
-0.0391,0.0992,1
-0.5845,1.068,1
0.5353,1.6758,1
0.6479,0.2351,1
-0.0484,1.0163,1
0.0809,-0.931,0
-0.7655,-2.1263,0
1.3587,1.4639,1
-1.3438,-0.972,0
-0.7974,-0.6432,0
0.4577,0.3173,1
0.287,0.5557,1
0.4164,0.5403,1
-1.5915,-0.6413,0
0.5743,-1.8475,0
1.3772,1.2486,1
0.1518,0.3567,1
-0.3116,1.2904,1
0.6671,0.2061,1
-1.4978,1.1012,0
-0.8342,0.6108,0
-0.3039,0.3196,1
1.6467,-1.5548,1
-0.1786,-0.9925,0
4 changes: 4 additions & 0 deletions e2e/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pytest==8.3.4
pytest-playwright==0.5.2
pytest-timeout==2.3.1
playwright==1.49.1
Loading