Files
factoryOps-v2/tests/test_templates.py
Johngreen 7de011621d
All checks were successful
Deploy to Production / deploy (push) Successful in 1m7s
feat: Phase 3 — inspection templates (backend + frontend)
- Add InspectionTemplate and InspectionTemplateItem models
- Add 9 API endpoints for template CRUD and item management
- Add Alembic migration for inspection_templates tables
- Add 15 backend tests (39/39 total pass)
- Add TemplateEditor component with item management UI
- Add templates list, create, and edit pages
- Add tab bar, template grid, form row CSS classes
- Fix CORS middleware ordering in main.py
- Move CORS middleware before router registration

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-02-10 13:24:30 +09:00

390 lines
12 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"]
async def _create_template(
client: AsyncClient,
headers: dict,
tenant_id: str = "test-co",
name: str = "일일 설비검사",
subject_type: str = "equipment",
schedule_type: str = "daily",
machine_id: str = None,
items: list = None,
) -> dict:
body = {
"name": name,
"subject_type": subject_type,
"schedule_type": schedule_type,
"inspection_mode": "measurement",
}
if machine_id:
body["machine_id"] = machine_id
if items:
body["items"] = items
resp = await client.post(f"/api/{tenant_id}/templates", json=body, headers=headers)
assert resp.status_code == 200, f"Template create failed: {resp.json()}"
return resp.json()
@pytest.mark.asyncio
async def test_create_template_with_items(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
data = await _create_template(
client,
headers,
machine_id=machine_id,
items=[
{
"name": "유압 압력",
"data_type": "numeric",
"unit": "bar",
"spec_min": 10.0,
"spec_max": 20.0,
},
{
"name": "외관 상태",
"data_type": "boolean",
},
{
"name": "비고",
"data_type": "text",
"is_required": False,
},
],
)
assert data["name"] == "일일 설비검사"
assert data["subject_type"] == "equipment"
assert data["machine_id"] == machine_id
assert data["version"] == 1
assert data["items_count"] == 3
assert len(data["items"]) == 3
assert data["items"][0]["name"] == "유압 압력"
assert data["items"][0]["sort_order"] == 1
assert data["items"][0]["spec_min"] == 10.0
assert data["items"][1]["name"] == "외관 상태"
assert data["items"][1]["sort_order"] == 2
assert data["items"][2]["is_required"] is False
@pytest.mark.asyncio
async def test_create_template_no_items(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
data = await _create_template(
client,
headers,
name="빈 템플릿",
subject_type="product",
schedule_type="weekly",
)
assert data["subject_type"] == "product"
assert data["items_count"] == 0
@pytest.mark.asyncio
async def test_list_templates(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
await _create_template(client, headers, name="설비검사A", subject_type="equipment")
await _create_template(client, headers, name="품질검사A", subject_type="product")
await _create_template(client, headers, name="설비검사B", subject_type="equipment")
resp = await client.get("/api/test-co/templates", headers=headers)
assert resp.status_code == 200
assert len(resp.json()["templates"]) == 3
resp2 = await client.get(
"/api/test-co/templates?subject_type=equipment", headers=headers
)
assert len(resp2.json()["templates"]) == 2
resp3 = await client.get(
"/api/test-co/templates?subject_type=product", headers=headers
)
assert len(resp3.json()["templates"]) == 1
@pytest.mark.asyncio
async def test_get_template_detail(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(
client,
headers,
items=[{"name": "온도", "data_type": "numeric", "unit": ""}],
)
template_id = created["id"]
resp = await client.get(f"/api/test-co/templates/{template_id}", headers=headers)
assert resp.status_code == 200
data = resp.json()
assert data["id"] == template_id
assert len(data["items"]) == 1
assert data["items"][0]["unit"] == ""
@pytest.mark.asyncio
async def test_update_template_increments_version(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(client, headers)
template_id = created["id"]
assert created["version"] == 1
resp = await client.put(
f"/api/test-co/templates/{template_id}",
json={"name": "수정된 설비검사", "schedule_type": "weekly"},
headers=headers,
)
assert resp.status_code == 200
data = resp.json()
assert data["name"] == "수정된 설비검사"
assert data["schedule_type"] == "weekly"
assert data["version"] == 2
@pytest.mark.asyncio
async def test_delete_template_soft_delete(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(client, headers)
template_id = created["id"]
resp = await client.delete(f"/api/test-co/templates/{template_id}", headers=headers)
assert resp.status_code == 200
assert resp.json()["status"] == "success"
list_resp = await client.get("/api/test-co/templates", headers=headers)
assert len(list_resp.json()["templates"]) == 0
detail_resp = await client.get(
f"/api/test-co/templates/{template_id}", headers=headers
)
assert detail_resp.status_code == 404
@pytest.mark.asyncio
async def test_add_item_to_template(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(client, headers)
template_id = created["id"]
resp = await client.post(
f"/api/test-co/templates/{template_id}/items",
json={
"name": "습도",
"data_type": "numeric",
"unit": "%",
"spec_min": 30.0,
"spec_max": 70.0,
},
headers=headers,
)
assert resp.status_code == 200
item = resp.json()
assert item["name"] == "습도"
assert item["sort_order"] == 1
assert item["spec_min"] == 30.0
detail = await client.get(f"/api/test-co/templates/{template_id}", headers=headers)
assert detail.json()["version"] == 2
@pytest.mark.asyncio
async def test_update_item(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(
client,
headers,
items=[{"name": "압력", "data_type": "numeric", "unit": "bar"}],
)
template_id = created["id"]
item_id = created["items"][0]["id"]
resp = await client.put(
f"/api/test-co/templates/{template_id}/items/{item_id}",
json={"name": "유압 압력", "spec_min": 5.0, "spec_max": 15.0},
headers=headers,
)
assert resp.status_code == 200
assert resp.json()["name"] == "유압 압력"
assert resp.json()["spec_min"] == 5.0
@pytest.mark.asyncio
async def test_delete_item_reorders(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(
client,
headers,
items=[
{"name": "항목1", "data_type": "boolean"},
{"name": "항목2", "data_type": "boolean"},
{"name": "항목3", "data_type": "boolean"},
],
)
template_id = created["id"]
item_to_delete = created["items"][0]["id"]
resp = await client.delete(
f"/api/test-co/templates/{template_id}/items/{item_to_delete}",
headers=headers,
)
assert resp.status_code == 200
detail = await client.get(f"/api/test-co/templates/{template_id}", headers=headers)
items = detail.json()["items"]
assert len(items) == 2
assert items[0]["name"] == "항목2"
assert items[0]["sort_order"] == 1
assert items[1]["name"] == "항목3"
assert items[1]["sort_order"] == 2
@pytest.mark.asyncio
async def test_reorder_items(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(
client,
headers,
items=[
{"name": "A", "data_type": "boolean"},
{"name": "B", "data_type": "boolean"},
{"name": "C", "data_type": "boolean"},
],
)
template_id = created["id"]
ids = [item["id"] for item in created["items"]]
resp = await client.put(
f"/api/test-co/templates/{template_id}/items/reorder",
json={"item_ids": [ids[2], ids[0], ids[1]]},
headers=headers,
)
assert resp.status_code == 200
items = resp.json()["items"]
assert items[0]["name"] == "C"
assert items[0]["sort_order"] == 1
assert items[1]["name"] == "A"
assert items[1]["sort_order"] == 2
assert items[2]["name"] == "B"
assert items[2]["sort_order"] == 3
@pytest.mark.asyncio
async def test_select_data_type_with_options(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
created = await _create_template(
client,
headers,
items=[
{
"name": "외관 상태",
"data_type": "select",
"select_options": ["양호", "불량", "N/A"],
},
],
)
item = created["items"][0]
assert item["data_type"] == "select"
assert item["select_options"] == ["양호", "불량", "N/A"]
@pytest.mark.asyncio
async def test_invalid_subject_type(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
resp = await client.post(
"/api/test-co/templates",
json={
"name": "잘못된 템플릿",
"subject_type": "invalid",
"schedule_type": "daily",
},
headers=headers,
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_invalid_data_type(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
resp = await client.post(
"/api/test-co/templates",
json={
"name": "잘못된 항목",
"subject_type": "equipment",
"schedule_type": "daily",
"items": [{"name": "잘못", "data_type": "invalid_type"}],
},
headers=headers,
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_tenant_isolation(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
await _create_template(client, headers, name="테스트 템플릿")
other_headers = await get_auth_headers(
client, email="admin@test-co.com", password="pass1234"
)
resp = await client.get("/api/other-co/templates", headers=other_headers)
assert resp.status_code == 403
@pytest.mark.asyncio
async def test_equipment_part_link(client: AsyncClient, seeded_db):
headers = await get_auth_headers(client)
machine_id = await _create_machine(client, headers)
part_resp = await client.post(
f"/api/test-co/machines/{machine_id}/parts",
json={
"name": "베어링",
"lifecycle_type": "hours",
"lifecycle_limit": 5000,
},
headers=headers,
)
part_id = part_resp.json()["id"]
created = await _create_template(
client,
headers,
machine_id=machine_id,
items=[
{
"name": "베어링 온도",
"data_type": "numeric",
"unit": "",
"equipment_part_id": part_id,
},
],
)
assert created["items"][0]["equipment_part_id"] == part_id