生成代码工程

This commit is contained in:
root 2026-05-14 16:04:47 +08:00
parent 6d5aced43d
commit f3cd8962e6
10 changed files with 11233 additions and 2 deletions

View File

@ -1,3 +1,51 @@
# ODF光纤配线单元 # ODF 光纤配线单元管理系统
暂无描述 本项目是一个 ODFOptical Distribution Frame光纤配线架配线单元管理系统的 Python 示例工程。使用 FastAPI 构建,采用内存假数据存储,无需外部基础设施。
## 功能特性
- 管理 ODF 机架Rack添加、查询、删除
- 管理配线单元Unit每个机架包含多个配线单元
- 管理光纤端口Port每个单元包含多个光纤端口
- 管理跳接连接Connection记录端口之间的跳纤关系
- 查询空闲端口、指定端口的连接路径
## 安装依赖
```bash
pip install -r requirements.txt
```
## 启动服务
```bash
cd codegen-runs/codegen_ff965d9f6029446ca6b6913dd1d7b45c
uvicorn app.main:app --reload --port 8000
```
启动后访问 API 文档:
- Swagger UIhttp://127.0.0.1:8000/docs
- ReDochttp://127.0.0.1:8000/redoc
## 运行测试
```bash
cd codegen-runs/codegen_ff965d9f6029446ca6b6913dd1d7b45c
python -m pytest tests/test_basic.py -v
```
## 工程结构
```
.
├── README.md
├── requirements.txt
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI 应用入口,路由定义
│ ├── models.py # 数据模型(内存存储结构)
│ ├── schemas.py # Pydantic 请求/响应模型
│ └── services.py # 业务逻辑层(内存假数据)
└── tests/
└── test_basic.py # 基础测试
```

1
app/__init__.py Normal file
View File

@ -0,0 +1 @@
# ODF 光纤配线单元管理系统

157
app/main.py Normal file
View File

@ -0,0 +1,157 @@
"""ODF 光纤配线单元管理系统 - FastAPI 应用入口。
定义所有 RESTful API 路由提供对 ODF 机架配线单元端口和
跳接连接的管理接口
"""
from typing import Optional
from fastapi import FastAPI, HTTPException
from app.models import PortType
from app.schemas import (
RackCreate,
RackResponse,
RackListResponse,
UnitCreate,
UnitResponse,
PortResponse,
PortDetail,
FreePortResponse,
ConnectionCreate,
ConnectionResponse,
)
from app.services import service
app = FastAPI(
title="ODF 光纤配线单元管理系统",
description="提供对 ODF 光纤配线架、配线单元、光纤端口和跳接连接的统一管理能力。",
version="1.0.0",
)
# ==================== 机架 API ====================
@app.get("/api/racks", response_model=list[RackListResponse], summary="获取所有机架列表")
def list_racks():
"""获取所有 ODF 机架的简要列表,包含每个机架下的单元数量。"""
return service.list_racks()
@app.get("/api/racks/{rack_id}", response_model=RackResponse, summary="获取机架详情")
def get_rack(rack_id: str):
"""根据机架 ID 获取机架的详细信息,包含所有配线单元和端口。"""
rack = service.get_rack(rack_id)
if not rack:
raise HTTPException(status_code=404, detail=f"机架 {rack_id} 不存在")
return rack
@app.post("/api/racks", response_model=RackResponse, status_code=201, summary="创建新机架")
def create_rack(body: RackCreate):
"""创建一个新的空 ODF 机架,不含配线单元。"""
rack = service.create_rack(body.rack_name, body.location)
return rack
@app.delete("/api/racks/{rack_id}", status_code=204, summary="删除机架")
def delete_rack(rack_id: str):
"""删除指定机架及其包含的所有配线单元和端口。"""
ok = service.delete_rack(rack_id)
if not ok:
raise HTTPException(status_code=404, detail=f"机架 {rack_id} 不存在")
# ==================== 配线单元 API ====================
@app.post("/api/racks/{rack_id}/units", response_model=UnitResponse, status_code=201, summary="创建配线单元")
def create_unit(rack_id: str, body: UnitCreate):
"""在指定机架下创建一个新的配线单元,并自动生成指定数量的端口。"""
unit = service.create_unit(
rack_id=rack_id,
unit_number=body.unit_number,
unit_name=body.unit_name,
position=body.position,
port_count=body.port_count,
port_type=body.port_type,
)
if not unit:
raise HTTPException(status_code=404, detail=f"机架 {rack_id} 不存在")
return unit
@app.delete("/api/units/{unit_id}", status_code=204, summary="删除配线单元")
def delete_unit(unit_id: str):
"""删除指定配线单元及其所有端口。"""
ok = service.delete_unit(unit_id)
if not ok:
raise HTTPException(status_code=404, detail=f"单元 {unit_id} 不存在")
# ==================== 端口 API ====================
@app.get("/api/ports/free", response_model=list[FreePortResponse], summary="查询空闲端口")
def get_free_ports(rack_id: Optional[str] = None):
"""查询所有空闲端口,可选择按机架过滤。"""
return service.get_free_ports(rack_id)
@app.get("/api/ports/{port_id}", response_model=PortDetail, summary="获取端口详情")
def get_port_detail(port_id: str):
"""获取指定端口的详细信息,包含所属单元和机架信息。"""
detail = service.get_port_detail(port_id)
if not detail:
raise HTTPException(status_code=404, detail=f"端口 {port_id} 不存在")
return detail
# ==================== 跳接连接 API ====================
@app.get("/api/connections", response_model=list[ConnectionResponse], summary="获取所有跳接连接")
def list_connections():
"""获取所有跳接连接列表。"""
return service.list_connections()
@app.post("/api/connections", response_model=ConnectionResponse, status_code=201, summary="创建跳接连接")
def create_connection(body: ConnectionCreate):
"""在两个空闲端口之间创建跳接连接。两个端口状态必须为空闲FREE"""
conn = service.create_connection(
port_a_id=body.port_a_id,
port_b_id=body.port_b_id,
fiber_length=body.fiber_length,
remark=body.remark,
)
if not conn:
raise HTTPException(
status_code=400,
detail="创建连接失败,请检查端口是否存在、是否空闲、是否同一端口",
)
return conn
@app.delete("/api/connections/{connection_id}", status_code=204, summary="删除跳接连接")
def delete_connection(connection_id: str):
"""删除指定跳接连接,并自动释放两个端口的状态为空闲。"""
ok = service.delete_connection(connection_id)
if not ok:
raise HTTPException(status_code=404, detail=f"连接 {connection_id} 不存在")
# ==================== 路径查询 API ====================
@app.get("/api/ports/{port_id}/path", summary="查询端口连接路径")
def get_port_path(port_id: str):
"""查询指定端口的连接路径信息,包括对端端口和连接详情。"""
result = service.get_port_path(port_id)
if not result:
raise HTTPException(status_code=404, detail=f"端口 {port_id} 不存在")
return result
# ==================== 健康检查 ====================
@app.get("/api/health", summary="健康检查")
def health_check():
"""服务健康检查接口。"""
return {"status": "ok", "service": "ODF光纤配线单元管理系统"}

99
app/models.py Normal file
View File

@ -0,0 +1,99 @@
"""ODF 光纤配线单元管理系统 - 数据模型定义。
本模块定义了系统中使用的内部数据模型内存存储结构
"""
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
class PortType(str, Enum):
"""光纤端口类型枚举。"""
LC = "LC" # LC 接口
SC = "SC" # SC 接口
FC = "FC" # FC 接口
ST = "ST" # ST 接口
class PortStatus(str, Enum):
"""光纤端口状态枚举。"""
FREE = "FREE" # 空闲
IN_USE = "IN_USE" # 使用中
FAULT = "FAULT" # 故障
@dataclass
class Port:
"""光纤端口数据模型。
Attributes:
port_id: 端口唯一标识
unit_id: 所属配线单元 ID
port_number: 端口编号同一单元内唯一
port_type: 端口类型
status: 端口状态
label: 端口标签/标识说明
"""
port_id: str
unit_id: str
port_number: int
port_type: PortType = PortType.LC
status: PortStatus = PortStatus.FREE
label: str = ""
@dataclass
class Unit:
"""配线单元数据模型。
Attributes:
unit_id: 配线单元唯一标识
rack_id: 所属机架 ID
unit_number: 单元编号同一机架内唯一
unit_name: 单元名称
position: 在机架中的位置描述 "上排""下排"
ports: 单元包含的光纤端口列表
"""
unit_id: str
rack_id: str
unit_number: int
unit_name: str = ""
position: str = ""
ports: list[Port] = field(default_factory=list)
@dataclass
class Rack:
"""ODF 机架数据模型。
Attributes:
rack_id: 机架唯一标识
rack_name: 机架名称/编号
location: 物理位置描述如机房
units: 机架中包含的配线单元列表
"""
rack_id: str
rack_name: str
location: str = ""
units: list[Unit] = field(default_factory=list)
@dataclass
class Connection:
"""跳接连接数据模型,记录光纤端口之间的跳纤关系。
Attributes:
connection_id: 连接唯一标识
port_a_id: 起始端口 IDA端
port_b_id: 终止端口 IDB端
fiber_length: 光纤长度
create_time: 创建时间
remark: 备注说明
"""
connection_id: str
port_a_id: str
port_b_id: str
fiber_length: float = 0.0
create_time: str = ""
remark: str = ""

132
app/schemas.py Normal file
View File

@ -0,0 +1,132 @@
"""ODF 光纤配线单元管理系统 - Pydantic 请求/响应模型定义。
定义了 API 接口使用的数据验证和序列化模型
"""
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
from app.models import PortType, PortStatus
# ========== 端口相关 ==========
class PortCreate(BaseModel):
"""创建端口请求模型。"""
port_number: int = Field(..., ge=1, description="端口编号")
port_type: PortType = PortType.LC
label: str = ""
class PortResponse(BaseModel):
"""端口响应模型。"""
port_id: str
unit_id: str
port_number: int
port_type: PortType
status: PortStatus
label: str
model_config = {"from_attributes": True}
# ========== 配线单元相关 ==========
class UnitCreate(BaseModel):
"""创建配线单元请求模型。"""
unit_number: int = Field(..., ge=1, description="单元编号")
unit_name: str = ""
position: str = ""
port_count: int = Field(12, ge=1, le=48, description="端口数量")
port_type: PortType = PortType.LC
class UnitResponse(BaseModel):
"""配线单元响应模型。"""
unit_id: str
rack_id: str
unit_number: int
unit_name: str
position: str
ports: list[PortResponse]
model_config = {"from_attributes": True}
# ========== 机架相关 ==========
class RackCreate(BaseModel):
"""创建机架请求模型。"""
rack_name: str = Field(..., min_length=1, max_length=64, description="机架名称/编号")
location: str = ""
class RackResponse(BaseModel):
"""机架响应模型。"""
rack_id: str
rack_name: str
location: str
units: list[UnitResponse]
model_config = {"from_attributes": True}
class RackListResponse(BaseModel):
"""机架列表响应模型(不包含嵌套单元)。"""
rack_id: str
rack_name: str
location: str
unit_count: int = 0
model_config = {"from_attributes": True}
# ========== 连接相关 ==========
class ConnectionCreate(BaseModel):
"""创建跳接连接请求模型。"""
port_a_id: str = Field(..., description="起始端口 IDA端")
port_b_id: str = Field(..., description="终止端口 IDB端")
fiber_length: float = Field(0.0, ge=0, description="光纤长度(米)")
remark: str = ""
class ConnectionResponse(BaseModel):
"""跳接连接响应模型。"""
connection_id: str
port_a_id: str
port_b_id: str
fiber_length: float
create_time: str
remark: str
model_config = {"from_attributes": True}
class PortDetail(BaseModel):
"""端口详细信息(含所属单元和机架)。"""
port_id: str
port_number: int
port_type: PortType
status: PortStatus
label: str
unit_id: str
unit_number: int
rack_id: str
rack_name: str
class FreePortResponse(BaseModel):
"""空闲端口列表响应元素。"""
port_id: str
port_number: int
port_type: PortType
label: str
unit_id: str
unit_number: int
rack_id: str
rack_name: str
model_config = {"from_attributes": True}

432
app/services.py Normal file
View File

@ -0,0 +1,432 @@
"""ODF 光纤配线单元管理系统 - 业务逻辑层。
使用内存假数据存储模拟 ODF 配线单元的管理功能
提供机架配线单元端口跳接连接的 CRUD 操作
"""
import uuid
from datetime import datetime
from typing import Optional
from app.models import Rack, Unit, Port, Connection, PortType, PortStatus
class OdfService:
"""ODF 光纤配线单元管理服务。
使用内存字典存储数据提供对机架配线单元端口和跳接连接的管理能力
此类为单例服务所有数据在服务运行期间保存在内存中
"""
def __init__(self) -> None:
"""初始化内存数据存储和初始假数据。"""
self._racks: dict[str, Rack] = {}
self._units: dict[str, Unit] = {}
self._ports: dict[str, Port] = {}
self._connections: dict[str, Connection] = {}
self._init_demo_data()
def _init_demo_data(self) -> None:
"""初始化演示用假数据,方便测试和展示。"""
# 创建 2 个机架
rack1 = Rack(
rack_id="rack-001",
rack_name="ODF-001",
location="A机房-1列-01排",
)
rack2 = Rack(
rack_id="rack-002",
rack_name="ODF-002",
location="A机房-1列-02排",
)
# 为机架1创建2个单元每个单元12口
unit1 = self._create_unit_internal("rack-001", 1, "上排单元A", "上排", 12, PortType.LC)
unit2 = self._create_unit_internal("rack-001", 2, "下排单元B", "下排", 12, PortType.SC)
# 为机架2创建1个单元24口
unit3 = self._create_unit_internal("rack-002", 1, "上排单元C", "上排", 24, PortType.LC)
rack1.units = [unit1, unit2]
rack2.units = [unit3]
self._racks[rack1.rack_id] = rack1
self._racks[rack2.rack_id] = rack2
# 创建一条示例跳接连接单元1的端口1 -> 单元2的端口1
unit1_ports = [p for p in self._ports.values() if p.unit_id == unit1.unit_id]
unit2_ports = [p for p in self._ports.values() if p.unit_id == unit2.unit_id]
if unit1_ports and unit2_ports:
pa = unit1_ports[0]
pb = unit2_ports[0]
pa.status = PortStatus.IN_USE
pb.status = PortStatus.IN_USE
conn = Connection(
connection_id=f"conn-{uuid.uuid4().hex[:8]}",
port_a_id=pa.port_id,
port_b_id=pb.port_id,
fiber_length=3.5,
create_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
remark="示例跳接",
)
self._connections[conn.connection_id] = conn
def _create_unit_internal(
self,
rack_id: str,
unit_number: int,
unit_name: str,
position: str,
port_count: int,
port_type: PortType,
) -> Unit:
"""内部方法:创建一个配线单元及其端口。
Args:
rack_id: 所属机架 ID
unit_number: 单元编号
unit_name: 单元名称
position: 位置描述
port_count: 端口数量
port_type: 端口类型
Returns:
创建的 Unit 实例
"""
unit_id = f"unit-{uuid.uuid4().hex[:8]}"
unit = Unit(
unit_id=unit_id,
rack_id=rack_id,
unit_number=unit_number,
unit_name=unit_name,
position=position,
ports=[],
)
ports: list[Port] = []
for i in range(1, port_count + 1):
port_id = f"port-{uuid.uuid4().hex[:8]}"
port = Port(
port_id=port_id,
unit_id=unit_id,
port_number=i,
port_type=port_type,
status=PortStatus.FREE,
label=f"{rack_id}-{unit_number}-{i:02d}",
)
self._ports[port_id] = port
ports.append(port)
unit.ports = ports
self._units[unit_id] = unit
return unit
# ==================== 机架管理 ====================
def list_racks(self) -> list[dict]:
"""获取所有机架的简要列表。
Returns:
包含机架信息和单元数量的字典列表
"""
result = []
for rack in self._racks.values():
result.append({
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
"location": rack.location,
"unit_count": len(rack.units),
})
return result
def get_rack(self, rack_id: str) -> Optional[Rack]:
"""根据 ID 获取机架详情(包含单元和端口)。
Args:
rack_id: 机架 ID
Returns:
机架对象未找到时返回 None
"""
return self._racks.get(rack_id)
def create_rack(self, rack_name: str, location: str) -> Rack:
"""创建一个新的空机架。
Args:
rack_name: 机架名称/编号
location: 物理位置
Returns:
新创建的机架对象
"""
rack_id = f"rack-{uuid.uuid4().hex[:8]}"
rack = Rack(
rack_id=rack_id,
rack_name=rack_name,
location=location,
units=[],
)
self._racks[rack_id] = rack
return rack
def delete_rack(self, rack_id: str) -> bool:
"""删除指定机架(同时删除其下的单元和端口)。
Args:
rack_id: 机架 ID
Returns:
删除成功返回 True机架不存在返回 False
"""
if rack_id not in self._racks:
return False
rack = self._racks[rack_id]
for unit in rack.units:
for port in unit.ports:
self._ports.pop(port.port_id, None)
self._units.pop(unit.unit_id, None)
del self._racks[rack_id]
return True
# ==================== 配线单元管理 ====================
def create_unit(
self,
rack_id: str,
unit_number: int,
unit_name: str,
position: str,
port_count: int,
port_type: PortType,
) -> Optional[Unit]:
"""在指定机架下创建配线单元。
Args:
rack_id: 所属机架 ID
unit_number: 单元编号
unit_name: 单元名称
position: 位置描述
port_count: 端口数量
port_type: 端口类型
Returns:
创建的 Unit 对象机架不存在时返回 None
"""
if rack_id not in self._racks:
return None
unit = self._create_unit_internal(rack_id, unit_number, unit_name, position, port_count, port_type)
self._racks[rack_id].units.append(unit)
return unit
def delete_unit(self, unit_id: str) -> bool:
"""删除指定配线单元(同时删除其端口)。
Args:
unit_id: 单元 ID
Returns:
删除成功返回 True单元不存在返回 False
"""
if unit_id not in self._units:
return False
unit = self._units[unit_id]
for port in unit.ports:
self._ports.pop(port.port_id, None)
rack = self._racks.get(unit.rack_id)
if rack:
rack.units = [u for u in rack.units if u.unit_id != unit_id]
del self._units[unit_id]
return True
# ==================== 端口管理 ====================
def get_free_ports(self, rack_id: Optional[str] = None) -> list[dict]:
"""查询空闲端口列表。
Args:
rack_id: 可选的机架 ID指定时只查询该机架下的空闲端口
Returns:
空闲端口信息列表包含所属单元和机架信息
"""
result = []
for port in self._ports.values():
if port.status != PortStatus.FREE:
continue
unit = self._units.get(port.unit_id)
if not unit:
continue
rack = self._racks.get(unit.rack_id)
if not rack:
continue
if rack_id and rack.rack_id != rack_id:
continue
result.append({
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"label": port.label,
"unit_id": unit.unit_id,
"unit_number": unit.unit_number,
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
})
return result
def get_port_detail(self, port_id: str) -> Optional[dict]:
"""获取端口的详细信息(含所属单元和机架)。
Args:
port_id: 端口 ID
Returns:
端口详细信息字典端口不存在时返回 None
"""
port = self._ports.get(port_id)
if not port:
return None
unit = self._units.get(port.unit_id)
if not unit:
return None
rack = self._racks.get(unit.rack_id)
if not rack:
return None
return {
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"status": port.status,
"label": port.label,
"unit_id": unit.unit_id,
"unit_number": unit.unit_number,
"rack_id": rack.rack_id,
"rack_name": rack.rack_name,
}
# ==================== 跳接连接管理 ====================
def create_connection(
self,
port_a_id: str,
port_b_id: str,
fiber_length: float,
remark: str,
) -> Optional[Connection]:
"""创建端口之间的跳接连接。
两个端口必须都存在且状态为空闲FREE
Args:
port_a_id: A 端端口 ID
port_b_id: B 端端口 ID
fiber_length: 光纤长度
remark: 备注
Returns:
创建的 Connection 对象端口不存在或端口繁忙时返回 None
"""
port_a = self._ports.get(port_a_id)
port_b = self._ports.get(port_b_id)
if not port_a or not port_b:
return None
if port_a.status != PortStatus.FREE or port_b.status != PortStatus.FREE:
return None
if port_a_id == port_b_id:
return None
connection_id = f"conn-{uuid.uuid4().hex[:8]}"
conn = Connection(
connection_id=connection_id,
port_a_id=port_a_id,
port_b_id=port_b_id,
fiber_length=fiber_length,
create_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
remark=remark,
)
port_a.status = PortStatus.IN_USE
port_b.status = PortStatus.IN_USE
self._connections[connection_id] = conn
return conn
def list_connections(self) -> list[Connection]:
"""获取所有跳接连接列表。
Returns:
连接对象列表
"""
return list(self._connections.values())
def delete_connection(self, connection_id: str) -> bool:
"""删除跳接连接(释放两个端口状态为空闲)。
Args:
connection_id: 连接 ID
Returns:
删除成功返回 True连接不存在返回 False
"""
conn = self._connections.get(connection_id)
if not conn:
return False
port_a = self._ports.get(conn.port_a_id)
port_b = self._ports.get(conn.port_b_id)
if port_a:
port_a.status = PortStatus.FREE
if port_b:
port_b.status = PortStatus.FREE
del self._connections[connection_id]
return True
def get_port_path(self, port_id: str) -> Optional[dict]:
"""查询指定端口的连接路径信息。
Args:
port_id: 端口 ID
Returns:
包含端口信息和关联连接的字典端口不存在时返回 None
"""
port = self._ports.get(port_id)
if not port:
return None
unit = self._units.get(port.unit_id)
rack = self._racks.get(unit.rack_id) if unit else None
connected_conn = None
for conn in self._connections.values():
if conn.port_a_id == port_id or conn.port_b_id == port_id:
connected_conn = conn
break
other_port = None
if connected_conn:
other_port_id = (
connected_conn.port_b_id
if connected_conn.port_a_id == port_id
else connected_conn.port_a_id
)
other_port = self.get_port_detail(other_port_id)
return {
"port": {
"port_id": port.port_id,
"port_number": port.port_number,
"port_type": port.port_type,
"status": port.status,
"label": port.label,
"unit_id": unit.unit_id if unit else "",
"unit_number": unit.unit_number if unit else 0,
"rack_id": rack.rack_id if rack else "",
"rack_name": rack.rack_name if rack else "",
},
"connection": {
"connection_id": connected_conn.connection_id if connected_conn else None,
"fiber_length": connected_conn.fiber_length if connected_conn else 0,
"create_time": connected_conn.create_time if connected_conn else "",
"remark": connected_conn.remark if connected_conn else "",
} if connected_conn else None,
"connected_port": other_port,
}
# 全局单例服务实例
service = OdfService()

10045
events.ndjson Normal file

File diff suppressed because one or more lines are too long

30
generation.json Normal file
View File

@ -0,0 +1,30 @@
{
"projectId": 41,
"generationId": "codegen_ff965d9f6029446ca6b6913dd1d7b45c",
"language": "python",
"status": "completed",
"fileIds": [],
"outputDir": "D:\\pro\\DocumentGenerateAgent\\agents\\ai_agents\\project-files\\codegen-runs\\codegen_ff965d9f6029446ca6b6913dd1d7b45c",
"relativeOutputDir": "codegen-runs/codegen_ff965d9f6029446ca6b6913dd1d7b45c",
"generatedFiles": [
"README.md",
"app/__init__.py",
"app/main.py",
"app/models.py",
"app/schemas.py",
"app/services.py",
"events.ndjson",
"requirements.txt",
"tests/test_basic.py"
],
"analysisSummary": "未提供参考文件请仅根据用户自然语言描述生成Python工程。",
"eventLogFile": "D:\\pro\\DocumentGenerateAgent\\agents\\ai_agents\\project-files\\codegen-runs\\codegen_ff965d9f6029446ca6b6913dd1d7b45c\\events.ndjson",
"repoSettings": {
"username": "root",
"password": "pAssW0rd",
"repoUrl": "http://47.108.255.216:3000/root/ODF.git",
"branch": "main"
},
"repoUrl": "http://47.108.255.216:3000/root/ODF.git",
"branch": "main"
}

3
requirements.txt Normal file
View File

@ -0,0 +1,3 @@
fastapi==0.115.6
uvicorn==0.34.0
pydantic==2.10.4

284
tests/test_basic.py Normal file
View File

@ -0,0 +1,284 @@
"""ODF 光纤配线单元管理系统 - 基础功能测试。"""
import pytest
from fastapi.testclient import TestClient
from app.main import app
@pytest.fixture
def client():
"""创建测试客户端。"""
return TestClient(app)
class TestRackAPI:
"""机架 API 测试。"""
def test_list_racks(self, client):
"""测试获取机架列表。"""
resp = client.get("/api/racks")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
assert len(data) >= 1
# 验证初始假数据中的机架
rack_names = [r["rack_name"] for r in data]
assert "ODF-001" in rack_names
def test_get_rack_detail(self, client):
"""测试获取机架详情。"""
resp = client.get("/api/racks/rack-001")
assert resp.status_code == 200
data = resp.json()
assert data["rack_id"] == "rack-001"
assert data["rack_name"] == "ODF-001"
assert len(data["units"]) >= 1
def test_get_rack_not_found(self, client):
"""测试获取不存在的机架。"""
resp = client.get("/api/racks/not-exist")
assert resp.status_code == 404
def test_create_rack(self, client):
"""测试创建新机架。"""
resp = client.post("/api/racks", json={
"rack_name": "ODF-TEST-001",
"location": "测试位置",
})
assert resp.status_code == 201
data = resp.json()
assert data["rack_name"] == "ODF-TEST-001"
assert data["location"] == "测试位置"
assert "rack_id" in data
def test_delete_rack(self, client):
"""测试删除机架。"""
# 先创建一个新机架用于删除测试
create_resp = client.post("/api/racks", json={
"rack_name": "ODF-DELETE",
"location": "待删除",
})
rack_id = create_resp.json()["rack_id"]
resp = client.delete(f"/api/racks/{rack_id}")
assert resp.status_code == 204
# 确认已删除
get_resp = client.get(f"/api/racks/{rack_id}")
assert get_resp.status_code == 404
def test_delete_rack_not_found(self, client):
"""测试删除不存在的机架。"""
resp = client.delete("/api/racks/not-exist")
assert resp.status_code == 404
class TestUnitAPI:
"""配线单元 API 测试。"""
def test_create_unit(self, client):
"""测试创建配线单元。"""
resp = client.post("/api/racks/rack-001/units", json={
"unit_number": 10,
"unit_name": "测试单元",
"position": "左侧",
"port_count": 8,
"port_type": "LC",
})
assert resp.status_code == 201
data = resp.json()
assert data["unit_number"] == 10
assert data["unit_name"] == "测试单元"
assert len(data["ports"]) == 8
def test_create_unit_rack_not_found(self, client):
"""测试在不存在机架上创建单元。"""
resp = client.post("/api/racks/not-exist/units", json={
"unit_number": 1,
"port_count": 4,
"port_type": "SC",
})
assert resp.status_code == 404
def test_delete_unit(self, client):
"""测试删除配线单元。"""
# 先创建一个单元
create_resp = client.post("/api/racks/rack-001/units", json={
"unit_number": 20,
"unit_name": "待删除单元",
"port_count": 4,
})
unit_id = create_resp.json()["unit_id"]
resp = client.delete(f"/api/units/{unit_id}")
assert resp.status_code == 204
class TestPortAPI:
"""端口 API 测试。"""
def test_get_free_ports(self, client):
"""测试查询空闲端口。"""
resp = client.get("/api/ports/free")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
# 至少有空闲端口
assert len(data) >= 1
for p in data:
assert "port_id" in p
assert "port_number" in p
def test_get_free_ports_by_rack(self, client):
"""测试按机架过滤空闲端口。"""
resp = client.get("/api/ports/free?rack_id=rack-001")
assert resp.status_code == 200
data = resp.json()
for p in data:
assert p["rack_id"] == "rack-001"
def test_get_port_detail(self, client):
"""测试获取端口详情。"""
# 先获取一个空闲端口
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if free_ports:
port_id = free_ports[0]["port_id"]
resp = client.get(f"/api/ports/{port_id}")
assert resp.status_code == 200
data = resp.json()
assert data["port_id"] == port_id
assert "rack_name" in data
assert "unit_number" in data
def test_get_port_detail_not_found(self, client):
"""测试获取不存在端口的详情。"""
resp = client.get("/api/ports/not-exist")
assert resp.status_code == 404
class TestConnectionAPI:
"""跳接连接 API 测试。"""
def test_list_connections(self, client):
"""测试获取连接列表。"""
resp = client.get("/api/connections")
assert resp.status_code == 200
data = resp.json()
assert isinstance(data, list)
def test_create_connection(self, client):
"""测试创建跳接连接。"""
# 获取两个空闲端口
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if len(free_ports) >= 2:
pa = free_ports[0]["port_id"]
pb = free_ports[1]["port_id"]
resp = client.post("/api/connections", json={
"port_a_id": pa,
"port_b_id": pb,
"fiber_length": 5.0,
"remark": "测试连接",
})
assert resp.status_code == 201
data = resp.json()
assert data["port_a_id"] == pa
assert data["port_b_id"] == pb
def test_create_connection_same_port(self, client):
"""测试同一端口自连接(应失败)。"""
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if free_ports:
port_id = free_ports[0]["port_id"]
resp = client.post("/api/connections", json={
"port_a_id": port_id,
"port_b_id": port_id,
})
assert resp.status_code == 400
def test_create_connection_busy_port(self, client):
"""测试连接到已使用端口(应失败)。"""
# 使用已存在的示例连接中的端口
resp = client.get("/api/connections")
conns = resp.json()
if conns:
busy_port = conns[0]["port_a_id"]
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if free_ports:
free_port = free_ports[0]["port_id"]
resp2 = client.post("/api/connections", json={
"port_a_id": busy_port,
"port_b_id": free_port,
})
assert resp2.status_code == 400
def test_delete_connection(self, client):
"""测试删除跳接连接。"""
# 先创建一个新连接
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if len(free_ports) >= 2:
pa = free_ports[0]["port_id"]
pb = free_ports[1]["port_id"]
create_resp = client.post("/api/connections", json={
"port_a_id": pa,
"port_b_id": pb,
})
conn_id = create_resp.json()["connection_id"]
del_resp = client.delete(f"/api/connections/{conn_id}")
assert del_resp.status_code == 204
# 确认已删除
get_resp = client.get("/api/connections")
ids = [c["connection_id"] for c in get_resp.json()]
assert conn_id not in ids
class TestPortPathAPI:
"""端口路径查询 API 测试。"""
def test_get_port_path(self, client):
"""测试查询端口路径。"""
# 使用示例连接中的端口
resp = client.get("/api/connections")
conns = resp.json()
if conns:
port_id = conns[0]["port_a_id"]
path_resp = client.get(f"/api/ports/{port_id}/path")
assert path_resp.status_code == 200
data = path_resp.json()
assert "port" in data
assert "connection" in data
assert data["connection"] is not None
def test_get_port_path_free_port(self, client):
"""测试查询空闲端口的路径。"""
free_resp = client.get("/api/ports/free")
free_ports = free_resp.json()
if free_ports:
port_id = free_ports[0]["port_id"]
resp = client.get(f"/api/ports/{port_id}/path")
assert resp.status_code == 200
data = resp.json()
assert data["connection"] is None
def test_get_port_path_not_found(self, client):
"""测试查询不存在端口的路径。"""
resp = client.get("/api/ports/not-exist/path")
assert resp.status_code == 404
class TestHealthAPI:
"""健康检查 API 测试。"""
def test_health_check(self, client):
"""测试健康检查接口。"""
resp = client.get("/api/health")
assert resp.status_code == 200
data = resp.json()
assert data["status"] == "ok"