Skip to content

Commit 8b86063

Browse files
fixed tests
1 parent 413d65d commit 8b86063

File tree

3 files changed

+114
-24
lines changed

3 files changed

+114
-24
lines changed

.github/workflows/test.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,20 @@ jobs:
263263
kubectl patch configmap backend-config -n model-platform --type merge -p "{\"data\":{\"IMAGE_TAG\":\"${IMAGE_TAG}\"}}"
264264
make k8s-modelplatform IMAGE_TAG=${IMAGE_TAG}
265265
266-
- name: Wait for infrastructure to settle (3m)
266+
- name: Wait for backend to be ready
267267
run: |
268-
echo "Waitin 3 minutes for infrastructure to settle"
269-
sleep 60
268+
echo "Waiting for backend healthcheck at http://model-platform.com/api/health"
269+
for i in $(seq 1 60); do
270+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://model-platform.com/api/health)
271+
if [ "$STATUS" = "200" ]; then
272+
echo "Backend is ready (HTTP 200)"
273+
exit 0
274+
fi
275+
echo "Attempt $i/60 — HTTP $STATUS, retrying in 10s..."
276+
sleep 10
277+
done
278+
echo "Backend did not become ready after 10 minutes"
279+
exit 1
270280
271281
- name: Run end-to-end tests
272282
run: |

tests/tests_end_to_end/test_from_project_creation_to_model_predict.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def setup_and_teardown():
6262
def test_health_endpoint_responds():
6363
"""Test that the platform health endpoint responds."""
6464
result = subprocess.run(
65-
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", f"http://{MP_HOSTNAME}/health"],
65+
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", f"http://{MP_HOSTNAME}/api/health"],
6666
capture_output=True,
6767
text=True,
6868
)
@@ -78,25 +78,22 @@ def test_project_creation():
7878

7979
def test_mlflow_registry_responds():
8080
"""Test that MLflow registry responds after project creation."""
81-
# Wait for MLflow to be ready
82-
time.sleep(60)
81+
registry_url = f"http://{MP_HOSTNAME}/registry/{PROJECT_NAME}/"
82+
timeout = time.time() + 300 # 5-minute timeout
83+
while time.time() < timeout:
84+
result = subprocess.run(
85+
["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", registry_url],
86+
capture_output=True,
87+
text=True,
88+
)
89+
if result.stdout == "200":
90+
global _mlflow_ready
91+
_mlflow_ready = True
92+
return
93+
print(f"[DEBUG] MLflow registry HTTP {result.stdout}, retrying in 10s...")
94+
time.sleep(10)
8395

84-
result = subprocess.run(
85-
[
86-
"curl",
87-
"-s",
88-
"-o",
89-
"/dev/null",
90-
"-w",
91-
"%{http_code}",
92-
f"http://{MP_HOSTNAME}/registry/{PROJECT_NAME}/#/models",
93-
],
94-
capture_output=True,
95-
text=True,
96-
)
97-
assert result.stdout == "200", f"MLflow registry did not respond with 200: {result.stdout}"
98-
global _mlflow_ready
99-
_mlflow_ready = True
96+
assert False, f"MLflow registry did not respond with 200 within 5 minutes"
10097

10198

10299
# Flag to track if MLflow is ready
@@ -228,7 +225,6 @@ def _dump_registry_status():
228225

229226
def test_train_and_push_model_to_mlflow():
230227
"""Test model training and push to MLflow."""
231-
time.sleep(120)
232228
_skip_if_mlflow_not_ready()
233229
# Verify MLflow API is operational before pushing
234230
api_url = f"http://{MP_HOSTNAME}/registry/{PROJECT_NAME}/api/2.0/mlflow/experiments/search"
@@ -278,7 +274,6 @@ def test_deploy_model():
278274
def test_deployed_model_health_check():
279275
"""Test that deployed model responds to health check."""
280276
_skip_if_mlflow_not_ready()
281-
time.sleep(180)
282277
deployment_name = sanitize_ressource_name(f"{PROJECT_NAME}-{MODEL_NAME}-{MODEL_VERSION}-deployment")
283278

284279
# Check pod status first for debugging
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# author: Octo Technology MLOps tribe
2+
import pytest
3+
from playwright.sync_api import Page, expect
4+
5+
from tests.conftest import DEFAULT_TEST_USER, MP_HOSTNAME, is_minikube_running
6+
7+
BASE_URL = f"http://{MP_HOSTNAME}"
8+
9+
10+
@pytest.fixture(scope="session", autouse=True)
11+
def require_minikube():
12+
if not is_minikube_running():
13+
pytest.skip("Minikube not running — skipping frontend e2e tests")
14+
15+
16+
@pytest.fixture
17+
def logged_in_page(page: Page) -> Page:
18+
"""Fixture : ouvre la SPA et se connecte avec les credentials de test."""
19+
page.goto(BASE_URL)
20+
page.fill("#signin-email", DEFAULT_TEST_USER["username"])
21+
page.fill("#signin-password", DEFAULT_TEST_USER["password"])
22+
page.click("#signin-btn")
23+
page.wait_for_selector("#projects-grid, #projects-empty", timeout=10_000)
24+
return page
25+
26+
27+
@pytest.mark.e2e
28+
class TestFrontendIsUp:
29+
"""La SPA est servie et redirige correctement."""
30+
31+
def test_frontend_serves_spa(self, page: Page):
32+
response = page.goto(BASE_URL)
33+
assert response.status == 200
34+
expect(page.locator("#app")).to_be_attached()
35+
36+
def test_unauthenticated_redirected_to_login(self, page: Page):
37+
page.goto(f"{BASE_URL}/#projects")
38+
expect(page.locator("#signin-email")).to_be_visible(timeout=5_000)
39+
40+
41+
@pytest.mark.e2e
42+
class TestAuthentication:
43+
"""Login / logout fonctionnent."""
44+
45+
def test_login_form_renders(self, page: Page):
46+
page.goto(BASE_URL)
47+
expect(page.locator("#signin-email")).to_be_visible()
48+
expect(page.locator("#signin-password")).to_be_visible()
49+
expect(page.locator("#signin-btn")).to_be_visible()
50+
51+
def test_login_with_valid_credentials(self, page: Page):
52+
page.goto(BASE_URL)
53+
page.fill("#signin-email", DEFAULT_TEST_USER["username"])
54+
page.fill("#signin-password", DEFAULT_TEST_USER["password"])
55+
page.click("#signin-btn")
56+
expect(page.locator("#projects-grid, #projects-empty")).to_be_visible(timeout=10_000)
57+
58+
def test_login_with_invalid_credentials_shows_error(self, page: Page):
59+
page.goto(BASE_URL)
60+
page.fill("#signin-email", "invalid@example.com")
61+
page.fill("#signin-password", "wrong_password")
62+
page.click("#signin-btn")
63+
expect(page.locator("#signin-error")).to_be_visible(timeout=5_000)
64+
65+
def test_logout_redirects_to_login(self, logged_in_page: Page):
66+
logged_in_page.click("#logout-btn")
67+
expect(logged_in_page.locator("#signin-email")).to_be_visible(timeout=5_000)
68+
69+
70+
@pytest.mark.e2e
71+
class TestNavigation:
72+
"""Navigation de base dans la SPA."""
73+
74+
def test_projects_page_shows_content(self, logged_in_page: Page):
75+
expect(logged_in_page.locator("#projects-grid, #projects-empty")).to_be_visible(timeout=10_000)
76+
77+
def test_governance_page_loads(self, logged_in_page: Page):
78+
logged_in_page.click("[data-route='governance']")
79+
expect(logged_in_page.locator("#gov-project-select")).to_be_visible(timeout=5_000)
80+
81+
def test_create_project_modal_opens(self, logged_in_page: Page):
82+
logged_in_page.click("[data-route='projects']")
83+
logged_in_page.wait_for_selector("#new-project-btn", timeout=5_000)
84+
logged_in_page.click("#new-project-btn")
85+
expect(logged_in_page.locator("#new-proj-name")).to_be_visible(timeout=5_000)

0 commit comments

Comments
 (0)