88 lines
3.1 KiB
Python
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()
|