import pytest from httpx import AsyncClient from tests.conftest import get_auth_headers async def _login( client: AsyncClient, email: str = "super@test.com", pw: str = "pass1234" ): return await get_auth_headers(client, email, pw) @pytest.mark.asyncio async def test_full_lifecycle_flow(client: AsyncClient, seeded_db): h = await _login(client) tid = "test-co" r = await client.post( f"/api/{tid}/machines", json={ "name": "E2E 통합 설비", "equipment_code": "E2E-001", "model": "Test Model X", }, headers=h, ) assert r.status_code == 200 machine = r.json() machine_id = machine["id"] assert machine["name"] == "E2E 통합 설비" r = await client.post( f"/api/{tid}/machines/{machine_id}/parts", json={ "name": "E2E 부품", "part_number": "E2E-P-001", "category": "소모품", "lifecycle_type": "count", "lifecycle_limit": 1000, "alarm_threshold": 80, "counter_source": "manual", }, headers=h, ) assert r.status_code == 200 part = r.json() part_id = part["id"] assert part["counter"]["current_value"] == 0 r = await client.put( f"/api/{tid}/parts/{part_id}/counter", json={"value": 500}, headers=h ) assert r.status_code == 200 assert r.json()["counter"]["current_value"] == 500 assert r.json()["counter"]["lifecycle_pct"] == 50.0 r = await client.get(f"/api/{tid}/alarms?is_acknowledged=false", headers=h) assert r.status_code == 200 assert len(r.json()["alarms"]) == 0 r = await client.put( f"/api/{tid}/parts/{part_id}/counter", json={"value": 850}, headers=h ) assert r.status_code == 200 assert r.json()["counter"]["lifecycle_pct"] == 85.0 r = await client.get(f"/api/{tid}/alarms?is_acknowledged=false", headers=h) assert r.status_code == 200 alarms = r.json()["alarms"] assert len(alarms) == 1 warning_alarm = alarms[0] assert warning_alarm["severity"] == "warning" assert warning_alarm["alarm_type"] == "threshold" warning_alarm_id = warning_alarm["id"] r = await client.put( f"/api/{tid}/parts/{part_id}/counter", json={"value": 1050}, headers=h ) assert r.status_code == 200 assert r.json()["counter"]["lifecycle_pct"] == 105.0 r = await client.get(f"/api/{tid}/alarms?is_acknowledged=false", headers=h) assert r.status_code == 200 active = r.json()["alarms"] assert len(active) == 2 severities = {a["severity"] for a in active} assert severities == {"warning", "critical"} r = await client.post( f"/api/{tid}/alarms/{warning_alarm_id}/acknowledge", headers=h ) assert r.status_code == 200 assert r.json()["is_acknowledged"] is True r = await client.get(f"/api/{tid}/alarms/summary", headers=h) assert r.status_code == 200 summary = r.json() assert summary["active_count"] == 1 assert summary["critical_count"] == 1 assert summary["warning_count"] == 0 r = await client.post( f"/api/{tid}/parts/{part_id}/replace", json={ "reason": "수명 초과", "notes": "E2E 테스트 교체", }, headers=h, ) assert r.status_code == 200 replaced = r.json() assert replaced["status"] == "success" assert replaced["part"]["counter"]["current_value"] == 0 assert replaced["part"]["counter"]["lifecycle_pct"] == 0.0 r = await client.get(f"/api/{tid}/parts/{part_id}/replacements", headers=h) assert r.status_code == 200 logs = r.json()["replacements"] assert len(logs) == 1 assert logs[0]["counter_at_replacement"] == 1050 assert logs[0]["reason"] == "수명 초과" r = await client.post( f"/api/{tid}/templates", json={ "name": "E2E 설비 점검", "subject_type": "equipment", "machine_id": machine_id, "schedule_type": "daily", "inspection_mode": "measurement", "items": [ { "name": "온도", "data_type": "numeric", "unit": "°C", "spec_min": 10, "spec_max": 50, "is_required": True, }, {"name": "가동 상태", "data_type": "boolean", "is_required": True}, { "name": "상태", "data_type": "select", "select_options": ["정상", "주의", "이상"], "is_required": True, }, ], }, headers=h, ) assert r.status_code == 200 template = r.json() template_id = template["id"] assert len(template["items"]) == 3 r = await client.post( f"/api/{tid}/inspections", json={ "template_id": template_id, }, headers=h, ) assert r.status_code == 200 session = r.json() session_id = session["id"] assert session["status"] == "in_progress" assert len(session["records"]) == 3 detail_r = await client.get(f"/api/{tid}/inspections/{session_id}", headers=h) assert detail_r.status_code == 200 items_detail = detail_r.json()["items_detail"] record_data = [] for item_d in items_detail: item = item_d["item"] record = item_d["record"] if item["name"] == "온도": record_data.append( {"template_item_id": record["template_item_id"], "value_numeric": 25.5} ) elif item["name"] == "가동 상태": record_data.append( {"template_item_id": record["template_item_id"], "value_boolean": True} ) elif item["name"] == "상태": record_data.append( {"template_item_id": record["template_item_id"], "value_select": "정상"} ) r = await client.put( f"/api/{tid}/inspections/{session_id}/records", json={ "records": record_data, }, headers=h, ) assert r.status_code == 200 r = await client.post(f"/api/{tid}/inspections/{session_id}/complete", headers=h) assert r.status_code == 200 completed = r.json() assert completed["status"] == "success" assert completed["summary"]["pass_count"] >= 2 r = await client.get(f"/api/{tid}/machines/{machine_id}", headers=h) assert r.status_code == 200 assert r.json()["parts_count"] == 1 r = await client.get(f"/api/{tid}/inspections", headers=h) assert r.status_code == 200 all_sessions = r.json()["inspections"] completed_sessions = [s for s in all_sessions if s["status"] == "completed"] assert len(completed_sessions) >= 1 @pytest.mark.asyncio async def test_tenant_isolation_e2e(client: AsyncClient, seeded_db): h_super = await _login(client) h_tenant = await _login(client, "admin@test-co.com", "pass1234") tid = "test-co" other = "other-co" r = await client.post( f"/api/{tid}/machines", json={ "name": "격리 테스트 설비", "equipment_code": "ISO-001", }, headers=h_super, ) assert r.status_code == 200 machine_id = r.json()["id"] r = await client.post( f"/api/{tid}/machines/{machine_id}/parts", json={ "name": "격리 부품", "lifecycle_type": "hours", "lifecycle_limit": 5000, }, headers=h_super, ) assert r.status_code == 200 r = await client.post( f"/api/{tid}/templates", json={ "name": "격리 템플릿", "subject_type": "equipment", "schedule_type": "daily", "inspection_mode": "checklist", "items": [{"name": "체크", "data_type": "boolean", "is_required": True}], }, headers=h_super, ) assert r.status_code == 200 r = await client.get(f"/api/{other}/machines", headers=h_super) assert r.status_code == 200 assert len(r.json()["machines"]) == 0 r = await client.get(f"/api/{other}/templates", headers=h_super) assert r.status_code == 200 assert len(r.json()["templates"]) == 0 r = await client.get(f"/api/{other}/alarms", headers=h_super) assert r.status_code == 200 assert len(r.json()["alarms"]) == 0 r = await client.get(f"/api/{other}/inspections", headers=h_super) assert r.status_code == 200 assert len(r.json()["inspections"]) == 0 @pytest.mark.asyncio async def test_inspection_with_spec_validation(client: AsyncClient, seeded_db): h = await _login(client) tid = "test-co" r = await client.post( f"/api/{tid}/machines", json={ "name": "규격 검증 설비", "equipment_code": "SPEC-001", }, headers=h, ) machine_id = r.json()["id"] r = await client.post( f"/api/{tid}/templates", json={ "name": "규격 검증 점검", "subject_type": "equipment", "machine_id": machine_id, "schedule_type": "ad_hoc", "inspection_mode": "measurement", "items": [ { "name": "치수 A", "data_type": "numeric", "unit": "mm", "spec_min": 10.0, "spec_max": 20.0, "is_required": True, }, { "name": "치수 B", "data_type": "numeric", "unit": "mm", "spec_min": 5.0, "spec_max": 8.0, "is_required": True, }, ], }, headers=h, ) template_id = r.json()["id"] r = await client.post( f"/api/{tid}/inspections", json={"template_id": template_id}, headers=h ) session = r.json() session_id = session["id"] detail_r = await client.get(f"/api/{tid}/inspections/{session_id}", headers=h) items_detail = detail_r.json()["items_detail"] record_data = [] for item_d in items_detail: item = item_d["item"] record = item_d["record"] if item["name"] == "치수 A": record_data.append( {"template_item_id": record["template_item_id"], "value_numeric": 15.0} ) elif item["name"] == "치수 B": record_data.append( {"template_item_id": record["template_item_id"], "value_numeric": 9.5} ) await client.put( f"/api/{tid}/inspections/{session_id}/records", json={"records": record_data}, headers=h, ) r = await client.post(f"/api/{tid}/inspections/{session_id}/complete", headers=h) assert r.status_code == 200 completed = r.json() assert completed["status"] == "success" assert completed["summary"]["pass_count"] == 1 assert completed["summary"]["fail_count"] == 1