- 修复前端路由守卫:未登录时不显示提示,直接跳转登录页 - 修复API拦截器:401错误不显示提示,直接跳转 - 增强验证码显示:图片尺寸从120x40增加到200x80 - 增大验证码字体:从28号增加到48号 - 优化验证码字符:排除易混淆的0和1 - 减少干扰线:从5条减少到3条,添加背景色优化 - 增强登录API日志:添加详细的调试日志 - 增强验证码生成和验证日志 - 优化异常处理和错误追踪 影响文件: - src/router/index.ts - src/api/request.ts - app/services/auth_service.py - app/api/v1/auth.py - app/schemas/user.py 测试状态: - 前端构建通过 - 后端语法检查通过 - 验证码显示效果优化完成 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
892 lines
33 KiB
Python
892 lines
33 KiB
Python
"""
|
|
维修管理 API 测试
|
|
|
|
测试范围:
|
|
- 维修记录CRUD测试 (20+用例)
|
|
- 维修状态管理测试 (15+用例)
|
|
- 维修费用测试 (10+用例)
|
|
- 维修历史测试 (5+用例)
|
|
|
|
总计: 50+ 用例
|
|
"""
|
|
|
|
import pytest
|
|
from datetime import datetime, timedelta
|
|
from typing import List
|
|
from decimal import Decimal
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.models.maintenance import Maintenance, MaintenancePart
|
|
from app.models.asset import Asset
|
|
from app.schemas.maintenance import (
|
|
MaintenanceCreate,
|
|
MaintenanceStatus,
|
|
MaintenanceType,
|
|
MaintenancePriority
|
|
)
|
|
|
|
|
|
# ================================
|
|
# Fixtures
|
|
# ================================
|
|
|
|
@pytest.fixture
|
|
def test_assets_for_maintenance(db: Session) -> List[Asset]:
|
|
"""创建需要维修的测试资产"""
|
|
assets = []
|
|
for i in range(3):
|
|
asset = Asset(
|
|
asset_code=f"TEST-MAINT-{i+1:03d}",
|
|
asset_name=f"测试维修资产{i+1}",
|
|
device_type_id=1,
|
|
organization_id=1,
|
|
status="maintenance",
|
|
purchase_date=datetime.now() - timedelta(days=365)
|
|
)
|
|
db.add(asset)
|
|
assets.append(asset)
|
|
db.commit()
|
|
for asset in assets:
|
|
db.refresh(asset)
|
|
return assets
|
|
|
|
|
|
@pytest.fixture
|
|
def test_maintenance_record(db: Session, test_assets_for_maintenance: List[Asset]) -> Maintenance:
|
|
"""创建测试维修记录"""
|
|
maintenance = Maintenance(
|
|
maintenance_no="MAINT-2025-001",
|
|
asset_id=test_assets_for_maintenance[0].id,
|
|
maintenance_type=MaintenanceType.PREVENTIVE,
|
|
priority=MaintenancePriority.MEDIUM,
|
|
status=MaintenanceStatus.PENDING,
|
|
fault_description="设备异常噪音",
|
|
reported_by=1,
|
|
reported_time=datetime.now(),
|
|
estimated_cost=Decimal("500.00"),
|
|
estimated_start_time=datetime.now() + timedelta(days=1),
|
|
estimated_completion_time=datetime.now() + timedelta(days=3)
|
|
)
|
|
db.add(maintenance)
|
|
db.commit()
|
|
db.refresh(maintenance)
|
|
return maintenance
|
|
|
|
|
|
@pytest.fixture
|
|
def test_maintenance_with_parts(db: Session, test_assets_for_maintenance: List[Asset]) -> Maintenance:
|
|
"""创建包含配件的维修记录"""
|
|
maintenance = Maintenance(
|
|
maintenance_no="MAINT-2025-002",
|
|
asset_id=test_assets_for_maintenance[1].id,
|
|
maintenance_type=MaintenanceType.CORRECTIVE,
|
|
priority=MaintenancePriority.HIGH,
|
|
status=MaintenanceStatus.IN_PROGRESS,
|
|
fault_description="设备故障无法启动",
|
|
reported_by=1,
|
|
reported_time=datetime.now(),
|
|
actual_start_time=datetime.now(),
|
|
estimated_cost=Decimal("1500.00")
|
|
)
|
|
db.add(maintenance)
|
|
db.commit()
|
|
db.refresh(maintenance)
|
|
|
|
# 添加维修配件
|
|
parts = [
|
|
MaintenancePart(
|
|
maintenance_id=maintenance.id,
|
|
part_name="电机",
|
|
part_code="PART-001",
|
|
quantity=1,
|
|
unit_price=Decimal("800.00")
|
|
),
|
|
MaintenancePart(
|
|
maintenance_id=maintenance.id,
|
|
part_name="轴承",
|
|
part_code="PART-002",
|
|
quantity=2,
|
|
unit_price=Decimal("100.00")
|
|
)
|
|
]
|
|
for part in parts:
|
|
db.add(part)
|
|
db.commit()
|
|
|
|
return maintenance
|
|
|
|
|
|
# ================================
|
|
# 维修记录CRUD测试 (20+用例)
|
|
# ================================
|
|
|
|
class TestMaintenanceCRUD:
|
|
"""维修记录CRUD操作测试"""
|
|
|
|
def test_create_maintenance_with_valid_data(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试使用有效数据创建维修记录"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "high",
|
|
"fault_description": "设备故障需要维修",
|
|
"reported_by": 1,
|
|
"estimated_cost": 1000.00,
|
|
"estimated_start_time": (datetime.now() + timedelta(hours=2)).isoformat(),
|
|
"estimated_completion_time": (datetime.now() + timedelta(days=2)).isoformat()
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["maintenance_no"] is not None
|
|
assert data["status"] == MaintenanceStatus.PENDING
|
|
assert data["asset_id"] == asset.id
|
|
|
|
def test_create_maintenance_with_invalid_asset_id(self, client, auth_headers):
|
|
"""测试使用无效资产ID创建维修记录应失败"""
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": 999999,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"fault_description": "测试",
|
|
"reported_by": 1
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 404
|
|
assert "资产不存在" in response.json()["detail"]
|
|
|
|
def test_create_maintenance_without_fault_description(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试创建维修记录时未提供故障描述应失败"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"reported_by": 1
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 400
|
|
assert "故障描述" in response.json()["detail"]
|
|
|
|
def test_create_maintenance_with_negative_cost(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试创建负费用的维修记录应失败"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"fault_description": "测试",
|
|
"reported_by": 1,
|
|
"estimated_cost": -100.00
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 400
|
|
|
|
def test_create_maintenance_auto_updates_asset_status(self, client, auth_headers, db: Session, test_assets_for_maintenance):
|
|
"""测试创建维修记录时自动更新资产状态"""
|
|
asset = test_assets_for_maintenance[0]
|
|
original_status = asset.status
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"fault_description": "测试自动更新状态",
|
|
"reported_by": 1
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
# 验证资产状态已更新
|
|
db.refresh(asset)
|
|
assert asset.status == "maintenance"
|
|
|
|
def test_get_maintenance_list_with_pagination(self, client, auth_headers, test_maintenance_record):
|
|
"""测试分页获取维修记录列表"""
|
|
response = client.get(
|
|
"/api/v1/maintenance/?page=1&page_size=10",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "total" in data
|
|
assert len(data["items"]) >= 1
|
|
|
|
def test_get_maintenance_list_with_status_filter(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按状态筛选维修记录"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/?status={MaintenanceStatus.PENDING}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
for item in data["items"]:
|
|
assert item["status"] == MaintenanceStatus.PENDING
|
|
|
|
def test_get_maintenance_list_with_asset_filter(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按资产筛选维修记录"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/?asset_id={test_maintenance_record.asset_id}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["items"]) >= 1
|
|
|
|
def test_get_maintenance_list_with_type_filter(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按维修类型筛选"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/?maintenance_type={test_maintenance_record.maintenance_type}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_list_with_priority_filter(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按优先级筛选"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/?priority={test_maintenance_record.priority}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_list_with_date_range(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按日期范围筛选"""
|
|
start_date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
|
|
end_date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
|
|
|
|
response = client.get(
|
|
f"/api/v1/maintenance/?start_date={start_date}&end_date={end_date}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_by_id(self, client, auth_headers, test_maintenance_record):
|
|
"""测试通过ID获取维修记录详情"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["id"] == test_maintenance_record.id
|
|
assert data["maintenance_no"] == test_maintenance_record.maintenance_no
|
|
assert "asset" in data
|
|
|
|
def test_get_maintenance_by_invalid_id(self, client, auth_headers):
|
|
"""测试通过无效ID获取维修记录应返回404"""
|
|
response = client.get(
|
|
"/api/v1/maintenance/999999",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
def test_update_maintenance_fault_description(self, client, auth_headers, test_maintenance_record):
|
|
"""测试更新故障描述"""
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
json={"fault_description": "更新后的故障描述"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["fault_description"] == "更新后的故障描述"
|
|
|
|
def test_update_maintenance_priority(self, client, auth_headers, test_maintenance_record):
|
|
"""测试更新优先级"""
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
json={"priority": "urgent"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["priority"] == MaintenancePriority.URGENT
|
|
|
|
def test_update_maintenance_after_start_should_fail(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试维修开始后更新某些字段应失败"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
json={"maintenance_type": "preventive"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 400
|
|
assert "不允许修改" in response.json()["detail"]
|
|
|
|
def test_delete_pending_maintenance(self, client, auth_headers, db: Session, test_assets_for_maintenance):
|
|
"""测试删除待处理的维修记录"""
|
|
maintenance = Maintenance(
|
|
maintenance_no="MAINT-DEL-001",
|
|
asset_id=test_assets_for_maintenance[0].id,
|
|
maintenance_type=MaintenanceType.CORRECTIVE,
|
|
priority=MaintenancePriority.MEDIUM,
|
|
status=MaintenanceStatus.PENDING,
|
|
fault_description="待删除",
|
|
reported_by=1
|
|
)
|
|
db.add(maintenance)
|
|
db.commit()
|
|
db.refresh(maintenance)
|
|
|
|
response = client.delete(
|
|
f"/api/v1/maintenance/{maintenance.id}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_delete_in_progress_maintenance_should_fail(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试删除进行中的维修记录应失败"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.delete(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 400
|
|
assert "不允许删除" in response.json()["detail"]
|
|
|
|
def test_create_maintenance_with_parts(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试创建包含配件的维修记录"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "high",
|
|
"fault_description": "需要更换配件",
|
|
"reported_by": 1,
|
|
"parts": [
|
|
{
|
|
"part_name": "电机",
|
|
"part_code": "PART-001",
|
|
"quantity": 1,
|
|
"unit_price": 800.00
|
|
},
|
|
{
|
|
"part_name": "轴承",
|
|
"part_code": "PART-002",
|
|
"quantity": 2,
|
|
"unit_price": 100.00
|
|
}
|
|
]
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "parts" in data
|
|
assert len(data["parts"]) == 2
|
|
|
|
|
|
# ================================
|
|
# 维修状态管理测试 (15+用例)
|
|
# ================================
|
|
|
|
class TestMaintenanceStatusManagement:
|
|
"""维修状态管理测试"""
|
|
|
|
def test_start_maintenance(self, client, auth_headers, test_maintenance_record):
|
|
"""测试开始维修"""
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/start",
|
|
json={"start_note": "开始维修", "technician_id": 2},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == MaintenanceStatus.IN_PROGRESS
|
|
assert data["actual_start_time"] is not None
|
|
|
|
def test_start_maintenance_updates_asset_status(self, client, auth_headers, test_maintenance_record, db: Session):
|
|
"""测试开始维修时更新资产状态"""
|
|
client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/start",
|
|
json={"start_note": "开始维修"},
|
|
headers=auth_headers
|
|
)
|
|
|
|
asset = db.query(Asset).filter(Asset.id == test_maintenance_record.asset_id).first()
|
|
assert asset.status == "maintenance"
|
|
|
|
def test_pause_maintenance(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试暂停维修"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/pause",
|
|
json={"pause_reason": "等待配件"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == MaintenanceStatus.PAUSED
|
|
|
|
def test_resume_maintenance(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试恢复维修"""
|
|
test_maintenance_record.status = MaintenanceStatus.PAUSED
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/resume",
|
|
json={"resume_note": "配件已到,继续维修"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == MaintenanceStatus.IN_PROGRESS
|
|
|
|
def test_complete_maintenance(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试完成维修"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/complete",
|
|
json={
|
|
"completion_note": "维修完成",
|
|
"actual_cost": 1200.00,
|
|
"technician_id": 2
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == MaintenanceStatus.COMPLETED
|
|
assert data["actual_completion_time"] is not None
|
|
assert data["actual_cost"] == 1200.00
|
|
|
|
def test_complete_maintenance_updates_asset_status(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试完成维修后恢复资产状态"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/complete",
|
|
json={"completion_note": "完成", "actual_cost": 1000.00},
|
|
headers=auth_headers
|
|
)
|
|
|
|
asset = db.query(Asset).filter(Asset.id == test_maintenance_record.asset_id).first()
|
|
assert asset.status == "in_stock"
|
|
|
|
def test_cancel_maintenance(self, client, auth_headers, test_maintenance_record):
|
|
"""测试取消维修"""
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/cancel",
|
|
json={"cancellation_reason": "资产报废"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == MaintenanceStatus.CANCELLED
|
|
|
|
def test_cancel_maintenance_updates_asset_status(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试取消维修后恢复资产状态"""
|
|
client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/cancel",
|
|
json={"cancellation_reason": "取消维修"},
|
|
headers=auth_headers
|
|
)
|
|
|
|
asset = db.query(Asset).filter(Asset.id == test_maintenance_record.asset_id).first()
|
|
assert asset.status == "in_stock"
|
|
|
|
def test_assign_technician(self, client, auth_headers, test_maintenance_record):
|
|
"""测试分配维修人员"""
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/assign-technician",
|
|
json={"technician_id": 2, "assignment_note": "指派张工负责"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["technician_id"] == 2
|
|
|
|
def test_add_maintenance_progress_note(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试添加维修进度备注"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/progress-notes",
|
|
json={"note": "已更换故障配件", "progress_percentage": 50},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_progress_notes(self, client, auth_headers, test_maintenance_record):
|
|
"""测试获取维修进度备注"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/progress-notes",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert isinstance(data, list)
|
|
|
|
def test_update_maintenance_progress(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试更新维修进度"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/progress",
|
|
json={"progress_percentage": 75},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["progress_percentage"] == 75
|
|
|
|
def test_invalid_status_transition(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试无效的状态转换"""
|
|
test_maintenance_record.status = MaintenanceStatus.COMPLETED
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/start",
|
|
json={"start_note": "尝试重新开始"},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 400
|
|
|
|
def test_get_maintenance_status_history(self, client, auth_headers, test_maintenance_record):
|
|
"""测试获取状态变更历史"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/status-history",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert isinstance(data, list)
|
|
|
|
def test_auto_calculate_duration(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试自动计算维修时长"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
test_maintenance_record.actual_start_time = datetime.now() - timedelta(days=2)
|
|
db.commit()
|
|
|
|
client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/complete",
|
|
json={"completion_note": "完成", "actual_cost": 1000.00},
|
|
headers=auth_headers
|
|
)
|
|
|
|
db.refresh(test_maintenance_record)
|
|
assert test_maintenance_record.duration_hours is not None
|
|
|
|
|
|
# ================================
|
|
# 维修费用测试 (10+用例)
|
|
# ================================
|
|
|
|
class TestMaintenanceCost:
|
|
"""维修费用测试"""
|
|
|
|
def test_record_initial_cost_estimate(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试记录初始费用估算"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"fault_description": "测试费用估算",
|
|
"reported_by": 1,
|
|
"estimated_cost": 2000.00
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["estimated_cost"] == 2000.00
|
|
|
|
def test_update_cost_estimate(self, client, auth_headers, test_maintenance_record):
|
|
"""测试更新费用估算"""
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}",
|
|
json={"estimated_cost": 800.00},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["estimated_cost"] == 800.00
|
|
|
|
def test_record_actual_cost(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试记录实际费用"""
|
|
test_maintenance_record.status = MaintenanceStatus.IN_PROGRESS
|
|
db.commit()
|
|
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/record-cost",
|
|
json={"actual_cost": 1500.00, "cost_breakdown": {"parts": 1000.00, "labor": 500.00}},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["actual_cost"] == 1500.00
|
|
|
|
def test_calculate_total_parts_cost(self, client, auth_headers, test_maintenance_with_parts):
|
|
"""测试计算配件总费用"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_with_parts.id}/parts-cost",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_parts_cost"] == 1000.00 # 800 + 100*2
|
|
|
|
def test_add_maintenance_part(self, client, auth_headers, test_maintenance_record):
|
|
"""测试添加维修配件"""
|
|
response = client.post(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/parts",
|
|
json={
|
|
"part_name": "传感器",
|
|
"part_code": "PART-003",
|
|
"quantity": 1,
|
|
"unit_price": 300.00
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_update_maintenance_part(self, client, auth_headers, test_maintenance_with_parts):
|
|
"""测试更新维修配件"""
|
|
part = test_maintenance_with_parts.parts[0]
|
|
|
|
response = client.put(
|
|
f"/api/v1/maintenance/{test_maintenance_with_parts.id}/parts/{part.id}",
|
|
json={"quantity": 2, "unit_price": 750.00},
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_delete_maintenance_part(self, client, auth_headers, test_maintenance_with_parts):
|
|
"""测试删除维修配件"""
|
|
part = test_maintenance_with_parts.parts[0]
|
|
|
|
response = client.delete(
|
|
f"/api/v1/maintenance/{test_maintenance_with_parts.id}/parts/{part.id}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_parts_list(self, client, auth_headers, test_maintenance_with_parts):
|
|
"""测试获取维修配件列表"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_with_parts.id}/parts",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data) == 2
|
|
|
|
def test_cost_variance_analysis(self, client, auth_headers, db: Session, test_maintenance_record):
|
|
"""测试费用差异分析"""
|
|
test_maintenance_record.estimated_cost = Decimal("1000.00")
|
|
test_maintenance_record.actual_cost = Decimal("1200.00")
|
|
db.commit()
|
|
|
|
response = client.get(
|
|
f"/api/v1/maintenance/{test_maintenance_record.id}/cost-analysis",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "variance" in data
|
|
assert "variance_percentage" in data
|
|
|
|
def test_get_cost_statistics_by_asset(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试获取资产维修费用统计"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.get(
|
|
f"/api/v1/maintenance/asset/{asset.id}/cost-statistics",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_cost" in data
|
|
assert "maintenance_count" in data
|
|
|
|
|
|
# ================================
|
|
# 维修历史测试 (5+用例)
|
|
# ================================
|
|
|
|
class TestMaintenanceHistory:
|
|
"""维修历史测试"""
|
|
|
|
def test_get_asset_maintenance_history(self, client, auth_headers, test_maintenance_record):
|
|
"""测试获取资产维修历史"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/asset/{test_maintenance_record.asset_id}/history",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert isinstance(data, list)
|
|
assert len(data) >= 1
|
|
|
|
def test_get_maintenance_history_with_date_range(self, client, auth_headers, test_maintenance_record):
|
|
"""测试按日期范围获取维修历史"""
|
|
start_date = (datetime.now() - timedelta(days=1)).strftime("%Y-%m-%d")
|
|
end_date = (datetime.now() + timedelta(days=1)).strftime("%Y-%m-%d")
|
|
|
|
response = client.get(
|
|
f"/api/v1/maintenance/asset/{test_maintenance_record.asset_id}/history?start_date={start_date}&end_date={end_date}",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
def test_get_maintenance_frequency_analysis(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试获取维修频率分析"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
response = client.get(
|
|
f"/api/v1/maintenance/asset/{asset.id}/frequency-analysis",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_maintenance_count" in data
|
|
assert "average_days_between_maintenance" in data
|
|
|
|
def test_export_maintenance_history(self, client, auth_headers, test_maintenance_record):
|
|
"""测试导出维修历史"""
|
|
response = client.get(
|
|
f"/api/v1/maintenance/asset/{test_maintenance_record.asset_id}/export",
|
|
headers=auth_headers
|
|
)
|
|
assert response.status_code == 200
|
|
assert "export_url" in response.json()
|
|
|
|
def test_get_maintenance_summary_report(self, client, auth_headers):
|
|
"""测试获取维修汇总报告"""
|
|
response = client.get(
|
|
"/api/v1/maintenance/summary-report",
|
|
headers=auth_headers,
|
|
params={"start_date": "2025-01-01", "end_date": "2025-12-31"}
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_maintenance_count" in data
|
|
assert "total_cost" in data
|
|
assert "by_type" in data
|
|
|
|
|
|
# ================================
|
|
# 测试标记
|
|
# ================================
|
|
|
|
@pytest.mark.unit
|
|
class TestMaintenanceUnit:
|
|
"""单元测试标记"""
|
|
|
|
def test_maintenance_number_generation(self):
|
|
"""测试维修单号生成逻辑"""
|
|
pass
|
|
|
|
def test_maintenance_type_validation(self):
|
|
"""测试维修类型验证"""
|
|
pass
|
|
|
|
|
|
@pytest.mark.integration
|
|
class TestMaintenanceIntegration:
|
|
"""集成测试标记"""
|
|
|
|
def test_full_maintenance_workflow(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""测试完整维修流程"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
# 1. 创建维修记录
|
|
create_response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "high",
|
|
"fault_description": "完整流程测试",
|
|
"reported_by": 1,
|
|
"estimated_cost": 1000.00
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert create_response.status_code == 200
|
|
maintenance_id = create_response.json()["id"]
|
|
|
|
# 2. 开始维修
|
|
start_response = client.post(
|
|
f"/api/v1/maintenance/{maintenance_id}/start",
|
|
json={"start_note": "开始"},
|
|
headers=auth_headers
|
|
)
|
|
assert start_response.status_code == 200
|
|
|
|
# 3. 完成维修
|
|
complete_response = client.post(
|
|
f"/api/v1/maintenance/{maintenance_id}/complete",
|
|
json={"completion_note": "完成", "actual_cost": 1200.00},
|
|
headers=auth_headers
|
|
)
|
|
assert complete_response.status_code == 200
|
|
|
|
|
|
@pytest.mark.smoke
|
|
class TestMaintenanceSmoke:
|
|
"""冒烟测试标记"""
|
|
|
|
def test_create_and_start_maintenance(self, client, auth_headers, test_assets_for_maintenance):
|
|
"""冒烟测试: 创建并开始维修"""
|
|
asset = test_assets_for_maintenance[0]
|
|
|
|
create_response = client.post(
|
|
"/api/v1/maintenance/",
|
|
json={
|
|
"asset_id": asset.id,
|
|
"maintenance_type": "corrective",
|
|
"priority": "medium",
|
|
"fault_description": "冒烟测试",
|
|
"reported_by": 1
|
|
},
|
|
headers=auth_headers
|
|
)
|
|
assert create_response.status_code == 200
|
|
|
|
maintenance_id = create_response.json()["id"]
|
|
start_response = client.post(
|
|
f"/api/v1/maintenance/{maintenance_id}/start",
|
|
json={"start_note": "冒烟测试开始"},
|
|
headers=auth_headers
|
|
)
|
|
assert start_response.status_code == 200
|