Files
factoryOps-v2/tests/test_equipment_parts.py
Johngreen ab2a3e35b2 feat: Phase 0-2 complete — auth, machines, equipment parts with full CRUD
Multi-tenant factory inspection system (SpiFox, Enkid, Alpet):
- FastAPI backend with JWT auth, PostgreSQL (asyncpg)
- Next.js 16 frontend with App Router, SWR data fetching
- Machines CRUD with equipment parts management
- Part lifecycle tracking (hours/count/date) with counters
- Partial unique index for soft-delete support
- 24 pytest tests passing, E2E verified

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-02-10 12:05:22 +09:00

226 lines
6.8 KiB
Python

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": "T-001"},
headers=headers,
)
return resp.json()["id"]
@pytest.mark.asyncio
async def test_create_part(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "상부 금형",
"category": "mold",
"lifecycle_type": "count",
"lifecycle_limit": 10000,
"alarm_threshold": 90.0,
"counter_source": "auto_plc",
"installed_at": "2026-02-01T00:00:00+09:00",
},
headers=headers,
)
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "상부 금형"
assert data["lifecycle_type"] == "count"
assert data["lifecycle_limit"] == 10000
assert data["counter"]["current_value"] == 0
assert data["counter"]["lifecycle_pct"] == 0
@pytest.mark.asyncio
async def test_create_part_auto_creates_counter(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "히터",
"lifecycle_type": "hours",
"lifecycle_limit": 5000,
"installed_at": "2026-01-15T00:00:00Z",
},
headers=headers,
)
assert resp.status_code == 200
data = resp.json()
assert "counter" in data
assert data["counter"]["current_value"] == 0
assert data["counter"]["version"] == 0
@pytest.mark.asyncio
async def test_duplicate_part_name_rejected(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
part_data = {
"name": "동일 부품",
"lifecycle_type": "count",
"lifecycle_limit": 1000,
"installed_at": "2026-02-01T00:00:00Z",
}
resp1 = await client.post(
f"/api/test-co/machines/{machine_id}/parts", json=part_data, headers=headers
)
assert resp1.status_code == 200
resp2 = await client.post(
f"/api/test-co/machines/{machine_id}/parts", json=part_data, headers=headers
)
assert resp2.status_code == 409
@pytest.mark.asyncio
async def test_list_parts(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
for name in ["부품A", "부품B", "부품C"]:
await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": name,
"lifecycle_type": "count",
"lifecycle_limit": 1000,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
resp = await client.get(
f"/api/test-co/machines/{machine_id}/parts", headers=headers
)
assert resp.status_code == 200
assert len(resp.json()["parts"]) == 3
@pytest.mark.asyncio
async def test_get_part_detail(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
create_resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "센서",
"lifecycle_type": "hours",
"lifecycle_limit": 8000,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
part_id = create_resp.json()["id"]
resp = await client.get(f"/api/test-co/parts/{part_id}", headers=headers)
assert resp.status_code == 200
assert resp.json()["name"] == "센서"
assert resp.json()["lifecycle_limit"] == 8000
@pytest.mark.asyncio
async def test_update_part(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
create_resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "금형",
"lifecycle_type": "count",
"lifecycle_limit": 5000,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
part_id = create_resp.json()["id"]
resp = await client.put(
f"/api/test-co/parts/{part_id}",
json={"name": "상부 금형", "lifecycle_limit": 8000},
headers=headers,
)
assert resp.status_code == 200
assert resp.json()["name"] == "상부 금형"
assert resp.json()["lifecycle_limit"] == 8000
@pytest.mark.asyncio
async def test_delete_part(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
create_resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "삭제 부품",
"lifecycle_type": "date",
"lifecycle_limit": 30,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
part_id = create_resp.json()["id"]
resp = await client.delete(f"/api/test-co/parts/{part_id}", headers=headers)
assert resp.status_code == 200
assert resp.json()["status"] == "success"
list_resp = await client.get(
f"/api/test-co/machines/{machine_id}/parts", headers=headers
)
assert len(list_resp.json()["parts"]) == 0
@pytest.mark.asyncio
async def test_invalid_lifecycle_type(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "잘못된 부품",
"lifecycle_type": "invalid",
"lifecycle_limit": 100,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_delete_machine_with_parts_rejected(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "부품",
"lifecycle_type": "count",
"lifecycle_limit": 1000,
"installed_at": "2026-02-01T00:00:00Z",
},
headers=headers,
)
resp = await client.delete(f"/api/test-co/machines/{machine_id}", headers=headers)
assert resp.status_code == 409