Initial commit: CMM Report Analyzer
FastAPI app that parses CMM inspection reports (PDF/Excel/CSV), computes SPC metrics (Cp/Cpk/Pp/Ppk, control limits, Shapiro-Wilk), generates interactive Plotly charts, and provides AI-powered quality summaries via Azure OpenAI with graceful fallback. Includes 21 passing tests covering parsers, SPC calculations, and API endpoints.
This commit is contained in:
72
tests/test_api.py
Normal file
72
tests/test_api.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import io
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from httpx import ASGITransport, AsyncClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_excel() -> bytes:
|
||||
df = pd.DataFrame({
|
||||
"Feature Name": ["D1", "D1", "D1", "D2", "D2", "D2"],
|
||||
"Nominal": [10.0, 10.0, 10.0, 20.0, 20.0, 20.0],
|
||||
"Actual": [10.02, 9.99, 10.01, 20.05, 19.97, 20.02],
|
||||
"Tol+": [0.05, 0.05, 0.05, 0.10, 0.10, 0.10],
|
||||
"Tol-": [-0.05, -0.05, -0.05, -0.10, -0.10, -0.10],
|
||||
})
|
||||
buf = io.BytesIO()
|
||||
df.to_excel(buf, index=False)
|
||||
buf.seek(0)
|
||||
return buf.read()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upload_and_results(sample_excel):
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.post(
|
||||
"/api/upload",
|
||||
files=[("files", ("test.xlsx", sample_excel, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))],
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert "batch_id" in data
|
||||
|
||||
resp2 = await client.get(f"/api/results/{data['batch_id']}")
|
||||
assert resp2.status_code == 200
|
||||
result = resp2.json()
|
||||
assert result["status"] == "complete"
|
||||
assert len(result["files"]) == 1
|
||||
assert result["files"][0]["filename"] == "test.xlsx"
|
||||
assert len(result["files"][0]["spc"]) == 2 # D1 and D2
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upload_no_files():
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.post("/api/upload", files=[])
|
||||
assert resp.status_code in (400, 422)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_upload_unsupported_type():
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.post(
|
||||
"/api/upload",
|
||||
files=[("files", ("test.png", b"fake", "image/png"))],
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_results_not_found():
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as client:
|
||||
resp = await client.get("/api/results/nonexistent")
|
||||
assert resp.status_code == 404
|
||||
Reference in New Issue
Block a user