Files
ss-tools/backend/tests/test_app_spa.py
busya 488a8f349b test(backend): raise coverage to 95%+ statements and branches (97.8%/95.0%)
- ~60 new/extended test files across api, core, plugins, services, schemas:
  routes, superset clients, task_manager, lineage, git, translate,
  dashboard-testing, load-testing, migration, llm_analysis, scheduler, ssl
- .coveragerc: enable branch coverage; exclude src/__tests__ (test files)
  and src/scripts (CLI/ops tools) from the denominator
- bug fixes found while testing:
  * settings: PUT /settings/reports registered under duplicated prefix
  * schemas/lineage: FleetReportDTO missing run_status (route always 500)
  * dashboard_testing/baseline_inheritance: visual entry read wrong field
  * superset_client/_databases: logger extra name shadowed LogRecord attr
  * routes/datasets: _yaml_string_paths recursion without yield from
  * translate/sql_generator: restore explicit-type timestamp contract
  * baseline_catalog: remove unreachable dashboard_id fallback
- conftest fixes: pytest_plugins to rootdir conftest (pytest 9), test
  filename collision, TMPDIR-safe integration fixtures
2026-08-19 17:14:32 +03:00

179 lines
7.5 KiB
Python

# #region Test.AppModule.Spa [C:3] [TYPE Module] [SEMANTICS test,app,spa,serving,static]
# @BRIEF Tests for app.py — SPA serving, read_root, catch-all route, TestClient integration.
# @RELATION BINDS_TO -> [App.AppModule]
# @TEST_EDGE: no_frontend_build -> read_root returns API status JSON
from pathlib import Path
import sys
import os
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
import pytest
class TestSpaServing:
"""serve_spa and read_root — SPA fallback and API status."""
# #region Test.AppModule.TestReadRootResponse [C:2] [TYPE Function]
def test_read_root_response(self):
from fastapi.testclient import TestClient
from src.app import app, frontend_path
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
if frontend_path.exists():
assert "text/html" in response.headers.get("content-type", "")
else:
data = response.json()
assert "message" in data
assert "API is running" in data["message"]
# #endregion Test.AppModule.TestReadRootResponse
# #region Test.AppModule.TestSpaApiPathRejected [C:2] [TYPE Function]
def test_spa_api_path_rejected(self):
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
response = client.get("/api/unknown-route-test-xyz")
assert response.status_code == 404
assert response.headers.get("content-type", "").startswith("application/json")
# #endregion Test.AppModule.TestSpaApiPathRejected
class TestAppWithTestClient:
"""Integration tests using TestClient against the real app."""
# #region Test.AppModule.TestUnknownApiReturns404 [C:2] [TYPE Function]
def test_unknown_api_returns_404(self):
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
response = client.get("/api/nonexistent-endpoint-xyz")
assert response.status_code == 404
assert response.headers.get("content-type", "").startswith("application/json")
# #endregion Test.AppModule.TestUnknownApiReturns404
class TestServeSpaEdgeCases:
"""serve_spa — static file serving edge cases."""
# #region Test.AppModule.TestServeSpaKnownFile [C:2] [TYPE Function]
def test_serve_spa_known_file(self):
from pathlib import Path as P
root = P(__file__).resolve().parent.parent.parent
fp = root / "frontend" / "build"
if not fp.exists():
pytest.skip("Frontend build not found")
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
app_dir = fp / "_app"
if app_dir.exists():
assets = list(app_dir.rglob("*"))
if assets:
rel = assets[0].relative_to(fp)
resp = client.get(f"/{rel}")
assert resp.status_code == 200
# #endregion Test.AppModule.TestServeSpaKnownFile
# #region Test.AppModule.TestSpaUnknownFileReturnsIndex [C:2] [TYPE Function]
def test_spa_unknown_file_returns_index(self):
from pathlib import Path as P
root = P(__file__).resolve().parent.parent.parent
fp = root / "frontend" / "build"
if not fp.exists():
pytest.skip("Frontend build not found")
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
resp = client.get("/some-random-non-api-path")
assert resp.status_code == 200
assert "text/html" in resp.headers.get("content-type", "")
# #endregion Test.AppModule.TestSpaUnknownFileReturnsIndex
# #region Test.AppModule.TestSpaApiPath404 [C:2] [TYPE Function]
def test_spa_api_path_404(self):
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
resp = client.get("/api/does-not-exist-98765")
assert resp.status_code == 404
assert "application/json" in resp.headers.get("content-type", "")
# #endregion Test.AppModule.TestSpaApiPath404
# #region Test.AppModule.TestSpaApiPathNoLeadingSlash [C:2] [TYPE Function]
def test_spa_api_path_no_leading_slash(self):
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
resp = client.get("/api/another-missing-endpoint")
assert resp.status_code == 404
assert "application/json" in resp.headers.get("content-type", "")
# #endregion Test.AppModule.TestSpaApiPathNoLeadingSlash
# #region Test.AppModule.TestSpaExistingFileServed [C:2] [TYPE Function]
def test_spa_existing_file_served(self):
"""SPA catch-all serves an existing static file (covers line 910)."""
from pathlib import Path as P
root = P(__file__).resolve().parent.parent.parent
fp = root / "frontend" / "build"
if not fp.exists():
pytest.skip("Frontend build not found — SPA catch-all not registered")
from fastapi.testclient import TestClient
from src.app import app
client = TestClient(app)
# favicon.png exists at frontend/build/favicon.png
resp = client.get("/favicon.png")
assert resp.status_code == 200
assert resp.headers.get("content-type", "").startswith("image/")
# #endregion Test.AppModule.TestSpaExistingFileServed
class TestReadRootWhenFrontendMissing:
"""read_root — registered only when frontend/build is absent (lines 1455-1463)."""
def test_read_root_registered_when_frontend_missing(self):
"""Subprocess import with frontend/build masked -> read_root serves API status JSON."""
import subprocess
import sys
import textwrap
backend = str(Path(__file__).resolve().parent.parent)
script = textwrap.dedent(f"""
import os, sys
sys.path.insert(0, {backend!r})
import pathlib
_orig_exists = pathlib.Path.exists
def _fake_exists(self):
s = str(self)
if s.endswith(os.sep + "frontend" + os.sep + "build"):
return False
return _orig_exists(self)
pathlib.Path.exists = _fake_exists
_orig_mkdir = pathlib.Path.mkdir
def _safe_mkdir(self, mode=0o777, parents=False, exist_ok=False):
if str(self).startswith("/app"):
return
return _orig_mkdir(self, mode, parents=parents, exist_ok=exist_ok)
pathlib.Path.mkdir = _safe_mkdir
_orig_makedirs = os.makedirs
def _safe_makedirs(path, mode=0o777, exist_ok=False):
if str(path).startswith("/app"):
return
return _orig_makedirs(path, mode, exist_ok=exist_ok)
os.makedirs = _safe_makedirs
import asyncio
from src.app import app, read_root
body = asyncio.run(read_root())
print("MSG=" + body.get("message", ""))
print("ROOT=" + str(any(getattr(r, "path", None) == "/" for r in app.routes)))
""")
proc = subprocess.run(
[sys.executable, "-c", script],
capture_output=True, text=True, cwd=backend, env=os.environ.copy(), timeout=180,
)
assert proc.returncode == 0, proc.stderr
assert "MSG=Superset Tools API is running (Frontend build not found)" in proc.stdout
assert "ROOT=True" in proc.stdout
# #endregion Test.AppModule.Spa