Harden HTML5 SSE and local assets
CI / python (push) Has been cancelled
CI / rust (push) Has been cancelled

This commit is contained in:
2026-05-17 10:49:48 +03:00
parent 65c82c4fed
commit 22f59b7580
5 changed files with 535 additions and 34 deletions
+56 -5
View File
@@ -136,8 +136,9 @@ def test_html5_server_rendered_project_editor(tmp_path: Path):
assert 'hx-swap="outerHTML"' in editor.text
assert "hx-ext=\"sse\"" in editor.text
assert f"sse-connect=\"/html5/projects/{project_id}/events\"" in editor.text
assert "htmx.org" in editor.text
assert "htmx-ext-sse" in editor.text
assert '/html5/assets/htmx.min.js' in editor.text
assert '/html5/assets/htmx-ext-sse.js' in editor.text
assert "unpkg.com" not in editor.text
assert "client-js: htmx+sse only" in editor.text
assert ".object-actions .button[data-html5-object-action-active" in editor.text
assert ".inline-actions" in editor.text
@@ -147,7 +148,9 @@ def test_html5_server_rendered_project_editor(tmp_path: Path):
assert "__next" not in editor.text
with client.stream("GET", f"/html5/projects/{project_id}/events?once=1") as events:
first_chunk = next(events.iter_text())
first_chunk = "".join(events.iter_text())
assert ": project " in first_chunk
assert "retry: 5000" in first_chunk
assert "event: status" in first_chunk
assert "event: authoring-changes" in first_chunk
assert "event: project-report" in first_chunk
@@ -232,6 +235,15 @@ def test_html5_server_rendered_project_editor(tmp_path: Path):
assert "Изменений пока нет" in authoring.text
assert "<html" not in authoring.text
htmx_asset = client.get("/html5/assets/htmx.min.js")
assert htmx_asset.status_code == 200
assert "javascript" in htmx_asset.headers["content-type"]
assert "htmx" in htmx_asset.text
sse_asset = client.get("/html5/assets/htmx-ext-sse.js")
assert sse_asset.status_code == 200
assert "javascript" in sse_asset.headers["content-type"]
assert "sse" in sse_asset.text
def test_html5_project_index_creates_project_with_fragment():
client = TestClient(app)
@@ -625,7 +637,9 @@ def test_html5_project_setup_renders_server_fragments():
assert "<html" not in summary.text
with client.stream("GET", f"/html5/projects/{project_id}/setup/events?once=1") as events:
first_chunk = next(events.iter_text())
first_chunk = "".join(events.iter_text())
assert ": setup " in first_chunk
assert "retry: 5000" in first_chunk
assert "event: setup-summary" in first_chunk
assert "event: setup-import-job" in first_chunk
assert "data-html5-setup-summary" in first_chunk
@@ -651,6 +665,8 @@ def test_html5_operations_renders_job_monitor_fragments():
assert 'data-html5-page="operations"' in page.text
assert "data-html5-operations-body" in page.text
assert "data-html5-operations-summary" in page.text
assert "data-html5-operations-filter" in page.text
assert "data-html5-operation-detail" in page.text
assert 'hx-ext="sse"' in page.text
assert 'sse-connect="/html5/operations/events"' in page.text
assert 'sse-swap="operations-summary"' in page.text
@@ -660,7 +676,9 @@ def test_html5_operations_renders_job_monitor_fragments():
assert "__next" not in page.text
with client.stream("GET", "/html5/operations/events?once=1") as events:
first_chunk = next(events.iter_text())
first_chunk = "".join(events.iter_text())
assert ": operations heartbeat" in first_chunk
assert "retry: 5000" in first_chunk
assert "event: operations-summary" in first_chunk
assert "event: operations-jobs" in first_chunk
assert "data-html5-operations-summary" in first_chunk
@@ -671,9 +689,20 @@ def test_html5_operations_renders_job_monitor_fragments():
assert rows.status_code == 200
assert "text/html" in rows.headers["content-type"]
assert "data-html5-operation" in rows.text
assert 'hx-target="[data-html5-operation-detail]"' in rows.text
assert project_id in rows.text
assert "<html" not in rows.text
operation_job_id = client.get("/operations/jobs", params={"project_id": project_id, "kind": "SERVER_IMPORT"}).json()[0]["job_id"]
detail = client.get(f"/html5/operations/jobs/{operation_job_id}/detail")
assert detail.status_code == 200
assert "text/html" in detail.headers["content-type"]
assert "data-html5-operation-detail" in detail.text
assert operation_job_id in detail.text
assert "SERVER_IMPORT" in detail.text
assert project_id in detail.text
assert "<html" not in detail.text
summary = client.get("/html5/operations/summary")
assert summary.status_code == 200
assert "text/html" in summary.headers["content-type"]
@@ -681,6 +710,28 @@ def test_html5_operations_renders_job_monitor_fragments():
assert "Всего" in summary.text
assert "<html" not in summary.text
job_status = client.get("/operations/jobs", params={"project_id": project_id, "kind": "SERVER_IMPORT"}).json()[0]["status"]
filtered = client.get("/html5/operations", params={"project_id": project_id, "status": job_status, "kind": "SERVER_IMPORT"})
assert filtered.status_code == 200
assert f'sse-connect="/html5/operations/events?project_id={project_id}&status={job_status}&kind=SERVER_IMPORT"' in filtered.text
assert f'value="{project_id}"' in filtered.text
assert f'value="{job_status}"' in filtered.text
assert 'value="SERVER_IMPORT"' in filtered.text
assert project_id in filtered.text
filtered_rows = client.get("/html5/operations/jobs", params={"project_id": project_id, "status": job_status, "kind": "SERVER_IMPORT"})
assert filtered_rows.status_code == 200
assert project_id in filtered_rows.text
with client.stream(
"GET",
f"/html5/operations/events?once=1&project_id={project_id}&status={job_status}&kind=SERVER_IMPORT",
) as events:
filtered_chunk = "".join(events.iter_text())
assert "event: operations-summary" in filtered_chunk
assert "event: operations-jobs" in filtered_chunk
assert project_id in filtered_chunk
def test_project_setup_mock_import_indexes_project():
client = TestClient(app)