Complete testing system: pyproject.toml (pytest markers), test.sh orchestrator with auto app start/stop and colorful summary, pre-push hook, Gitea Actions workflow. New QA tests: API health (7 endpoints), responsive (3 viewports), log monitoring (ERROR/ORA-/Traceback detection), real GoMag sync, PL/SQL package validation, smoke prod (read-only). Converted test_app_basic.py and test_integration.py to pytest. Added pytestmark to all existing tests (unit/e2e/oracle). E2E conftest upgraded: console error collector, screenshot on failure, auto-detect live app on :5003. Usage: ./test.sh ci (30s) | ./test.sh full (2-3min) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
88 lines
2.1 KiB
Python
88 lines
2.1 KiB
Python
"""QA tests for API endpoint health and basic contract validation."""
|
|
import time
|
|
import urllib.request
|
|
import pytest
|
|
import httpx
|
|
|
|
pytestmark = pytest.mark.qa
|
|
|
|
ENDPOINTS = [
|
|
"/health",
|
|
"/api/dashboard/orders",
|
|
"/api/sync/status",
|
|
"/api/sync/history",
|
|
"/api/validate/missing-skus",
|
|
"/api/mappings",
|
|
"/api/settings",
|
|
]
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
def client(base_url):
|
|
"""Create httpx client; skip all if app is not reachable."""
|
|
try:
|
|
urllib.request.urlopen(f"{base_url}/health", timeout=3)
|
|
except Exception:
|
|
pytest.skip(f"App not reachable at {base_url}")
|
|
with httpx.Client(base_url=base_url, timeout=10.0) as c:
|
|
yield c
|
|
|
|
|
|
def test_health(client):
|
|
r = client.get("/health")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "oracle" in data
|
|
assert "sqlite" in data
|
|
|
|
|
|
def test_dashboard_orders(client):
|
|
r = client.get("/api/dashboard/orders")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "orders" in data
|
|
assert "counts" in data
|
|
|
|
|
|
def test_sync_status(client):
|
|
r = client.get("/api/sync/status")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "status" in data
|
|
|
|
|
|
def test_sync_history(client):
|
|
r = client.get("/api/sync/history")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "runs" in data
|
|
assert isinstance(data["runs"], list)
|
|
|
|
|
|
def test_missing_skus(client):
|
|
r = client.get("/api/validate/missing-skus")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "missing_skus" in data
|
|
|
|
|
|
def test_mappings(client):
|
|
r = client.get("/api/mappings")
|
|
assert r.status_code == 200
|
|
data = r.json()
|
|
assert "mappings" in data
|
|
|
|
|
|
def test_settings(client):
|
|
r = client.get("/api/settings")
|
|
assert r.status_code == 200
|
|
assert isinstance(r.json(), dict)
|
|
|
|
|
|
@pytest.mark.parametrize("endpoint", ENDPOINTS)
|
|
def test_response_time(client, endpoint):
|
|
start = time.monotonic()
|
|
client.get(endpoint)
|
|
elapsed = time.monotonic() - start
|
|
assert elapsed < 5.0, f"{endpoint} took {elapsed:.2f}s (limit: 5s)"
|