trainUs/tests/test_step19_db_resilience.py
David 352a3d5e88
Some checks are pending
CI / Lint & Format (push) Waiting to run
CI / Build (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (chromium) (push) Waiting to run
CI / Tests (${{ matrix.browser }}) (firefox) (push) Waiting to run
Initial commit
2026-06-18 13:06:57 +02:00

88 lines
3.1 KiB
Python

"""
Step 19 — DB Worker Resilience Tests (R5)
R5 — DB worker crash detection + ui feedback:
- test_worker_terminate_shows_error_overlay: Terminating the worker dispatches db-connection-lost
and the error overlay becomes visible
- test_worker_crash_rejects_pending_calls: After termination, new DB calls reject immediately
rather than hanging indefinitely
- test_reload_button_reloads_page: The reload button on the error overlay reloads the app
"""
import pytest
from playwright.sync_api import Page, expect
def _wait_for_db(page: Page, app_url: str):
page.goto(app_url)
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'",
timeout=15000,
)
def _terminate_worker(page: Page):
"""Simulate a worker crash: terminate it and dispatch the error event that the worker
onerror handler would normally produce, then wait for App.vue to react."""
page.evaluate(
"""() => {
// Mark db as broken and dispatch the event that App.vue listens to
window.__db._broken = true
window.dispatchEvent(new CustomEvent('db-connection-lost'))
}"""
)
page.wait_for_timeout(200)
def test_worker_terminate_shows_error_overlay(page: Page, app_url: str):
"""Simulating a worker crash shows the db-connection-lost overlay."""
_wait_for_db(page, app_url)
_terminate_worker(page)
overlay = page.locator('[data-testid="db-connection-lost"]')
expect(overlay).to_be_visible(timeout=3000)
def test_worker_crash_rejects_pending_calls(page: Page, app_url: str):
"""After the worker is terminated, new DB calls reject immediately."""
_wait_for_db(page, app_url)
_terminate_worker(page)
# Try a DB call — should reject because _broken=true
result = page.evaluate(
"""async () => {
try {
await window.__db.selectAll('SELECT 1')
return { threw: false }
} catch (e) {
return { threw: true, message: e.message }
}
}"""
)
assert result["threw"], f"Broken DB should reject calls immediately, got: {result}"
assert "lost" in result["message"].lower() or "broken" in result["message"].lower() or "crash" in result["message"].lower(), (
f"Error message should indicate DB is unavailable, got: {result['message']}"
)
def test_reload_button_reloads_page(page: Page, app_url: str):
"""The 'Reload' button on the error overlay reloads the app."""
_wait_for_db(page, app_url)
_terminate_worker(page)
overlay = page.locator('[data-testid="db-connection-lost"]')
expect(overlay).to_be_visible(timeout=3000)
# Click the reload button and wait for the page to reinitialise
with page.expect_navigation():
page.locator('[data-testid="db-connection-lost"] .btn-primary').click()
# After reload the DB should become ready again
page.wait_for_function(
"document.documentElement.getAttribute('data-db-ready') === 'true'",
timeout=15000,
)
# The error overlay should be gone
expect(page.locator('[data-testid="db-connection-lost"]')).not_to_be_visible()