import pytest from httpx import AsyncClient from tests.conftest import get_auth_headers async def _create_machine( client: AsyncClient, headers: dict, tenant_id: str = "test-co" ) -> str: resp = await client.post( f"/api/{tenant_id}/machines", json={"name": "알람 테스트 설비", "equipment_code": "A-001"}, headers=headers, ) return resp.json()["id"] async def _create_part( client: AsyncClient, headers: dict, machine_id: str, tenant_id: str = "test-co", alarm_threshold: float = 80.0, ) -> str: resp = await client.post( f"/api/{tenant_id}/machines/{machine_id}/parts", json={ "name": "알람 부품", "lifecycle_type": "hours", "lifecycle_limit": 1000, "alarm_threshold": alarm_threshold, "counter_source": "manual", }, headers=headers, ) return resp.json()["id"] @pytest.mark.asyncio async def test_no_alarm_below_threshold(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 700}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) assert resp.status_code == 200 assert len(resp.json()["alarms"]) == 0 @pytest.mark.asyncio async def test_warning_alarm_at_threshold(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 850}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) assert resp.status_code == 200 alarms = resp.json()["alarms"] assert len(alarms) == 1 assert alarms[0]["severity"] == "warning" assert alarms[0]["alarm_type"] == "threshold" assert alarms[0]["is_acknowledged"] is False assert "85.0%" in alarms[0]["message"] @pytest.mark.asyncio async def test_critical_alarm_at_100pct(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 1100}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) assert resp.status_code == 200 alarms = resp.json()["alarms"] assert len(alarms) == 1 assert alarms[0]["severity"] == "critical" assert alarms[0]["alarm_type"] == "critical" @pytest.mark.asyncio async def test_no_duplicate_alarm(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 900}, headers=headers, ) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 950}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) alarms = resp.json()["alarms"] assert len(alarms) == 1 @pytest.mark.asyncio async def test_acknowledge_alarm(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 850}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) alarm_id = resp.json()["alarms"][0]["id"] ack_resp = await client.post( f"/api/test-co/alarms/{alarm_id}/acknowledge", headers=headers, ) assert ack_resp.status_code == 200 assert ack_resp.json()["is_acknowledged"] is True assert ack_resp.json()["acknowledged_by"] is not None @pytest.mark.asyncio async def test_acknowledge_already_acknowledged(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 850}, headers=headers, ) resp = await client.get("/api/test-co/alarms", headers=headers) alarm_id = resp.json()["alarms"][0]["id"] await client.post(f"/api/test-co/alarms/{alarm_id}/acknowledge", headers=headers) ack2 = await client.post( f"/api/test-co/alarms/{alarm_id}/acknowledge", headers=headers ) assert ack2.status_code == 400 @pytest.mark.asyncio async def test_filter_unacknowledged(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 850}, headers=headers, ) resp = await client.get( "/api/test-co/alarms?is_acknowledged=false", headers=headers ) assert len(resp.json()["alarms"]) == 1 alarm_id = resp.json()["alarms"][0]["id"] await client.post(f"/api/test-co/alarms/{alarm_id}/acknowledge", headers=headers) resp2 = await client.get( "/api/test-co/alarms?is_acknowledged=false", headers=headers ) assert len(resp2.json()["alarms"]) == 0 @pytest.mark.asyncio async def test_alarm_summary(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 850}, headers=headers, ) resp = await client.get("/api/test-co/alarms/summary", headers=headers) assert resp.status_code == 200 data = resp.json() assert data["active_count"] == 1 assert data["warning_count"] == 1 assert data["critical_count"] == 0 @pytest.mark.asyncio async def test_tenant_isolation(client: AsyncClient, seeded_db): headers = await get_auth_headers(client) mid = await _create_machine(client, headers) pid = await _create_part(client, headers, mid) await client.put( f"/api/test-co/parts/{pid}/counter", json={"value": 900}, headers=headers, ) resp_other = await client.get("/api/other-co/alarms", headers=headers) assert resp_other.status_code == 200 assert len(resp_other.json()["alarms"]) == 0