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.
73 lines
2.3 KiB
Python
73 lines
2.3 KiB
Python
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
|