第一次提交
This commit is contained in:
141
backend/tests/README.md
Normal file
141
backend/tests/README.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# 单元测试说明
|
||||
|
||||
## 测试框架
|
||||
|
||||
本项目使用 `pytest` 作为测试框架,支持异步测试和数据库测试。
|
||||
|
||||
## 运行测试
|
||||
|
||||
### 运行所有测试
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
pytest
|
||||
```
|
||||
|
||||
### 运行特定测试文件
|
||||
|
||||
```bash
|
||||
pytest tests/test_auth.py
|
||||
```
|
||||
|
||||
### 运行特定测试类或函数
|
||||
|
||||
```bash
|
||||
pytest tests/test_auth.py::TestAuth::test_register_user
|
||||
```
|
||||
|
||||
### 运行带标记的测试
|
||||
|
||||
```bash
|
||||
# 只运行单元测试
|
||||
pytest -m unit
|
||||
|
||||
# 只运行工作流相关测试
|
||||
pytest -m workflow
|
||||
|
||||
# 只运行认证相关测试
|
||||
pytest -m auth
|
||||
```
|
||||
|
||||
### 运行并显示覆盖率
|
||||
|
||||
```bash
|
||||
pytest --cov=app --cov-report=html
|
||||
```
|
||||
|
||||
## 测试标记
|
||||
|
||||
- `@pytest.mark.unit` - 单元测试
|
||||
- `@pytest.mark.integration` - 集成测试
|
||||
- `@pytest.mark.slow` - 慢速测试(需要网络或数据库)
|
||||
- `@pytest.mark.api` - API测试
|
||||
- `@pytest.mark.workflow` - 工作流测试
|
||||
- `@pytest.mark.auth` - 认证测试
|
||||
|
||||
## 测试结构
|
||||
|
||||
```
|
||||
tests/
|
||||
├── __init__.py
|
||||
├── conftest.py # 共享fixtures和配置
|
||||
├── test_auth.py # 认证API测试
|
||||
├── test_workflows.py # 工作流API测试
|
||||
├── test_workflow_engine.py # 工作流引擎测试
|
||||
└── test_workflow_validator.py # 工作流验证器测试
|
||||
```
|
||||
|
||||
## Fixtures
|
||||
|
||||
### `db_session`
|
||||
创建测试数据库会话,每个测试函数都会获得一个独立的会话。
|
||||
|
||||
### `client`
|
||||
创建FastAPI测试客户端,用于API测试。
|
||||
|
||||
### `authenticated_client`
|
||||
创建已认证的测试客户端,自动注册用户并登录。
|
||||
|
||||
### `test_user_data`
|
||||
提供测试用户数据。
|
||||
|
||||
### `sample_workflow_data`
|
||||
提供示例工作流数据。
|
||||
|
||||
## 测试数据库
|
||||
|
||||
测试使用SQLite内存数据库,每个测试函数都会:
|
||||
1. 创建所有表
|
||||
2. 执行测试
|
||||
3. 删除所有表
|
||||
|
||||
这样可以确保测试之间的隔离性。
|
||||
|
||||
## 编写新测试
|
||||
|
||||
### 示例:API测试
|
||||
|
||||
```python
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.api
|
||||
class TestMyAPI:
|
||||
def test_my_endpoint(self, authenticated_client):
|
||||
response = authenticated_client.get("/api/v1/my-endpoint")
|
||||
assert response.status_code == 200
|
||||
```
|
||||
|
||||
### 示例:服务测试
|
||||
|
||||
```python
|
||||
@pytest.mark.unit
|
||||
class TestMyService:
|
||||
@pytest.mark.asyncio
|
||||
async def test_my_service_method(self):
|
||||
service = MyService()
|
||||
result = await service.my_method()
|
||||
assert result is not None
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **测试隔离**:每个测试函数都应该独立,不依赖其他测试的执行顺序。
|
||||
|
||||
2. **数据库清理**:使用 `db_session` fixture 确保每个测试都有干净的数据库。
|
||||
|
||||
3. **异步测试**:使用 `@pytest.mark.asyncio` 标记异步测试函数。
|
||||
|
||||
4. **标记测试**:使用适当的标记(`@pytest.mark.unit` 等)来组织测试。
|
||||
|
||||
5. **测试数据**:使用 fixtures 提供测试数据,避免硬编码。
|
||||
|
||||
## CI/CD集成
|
||||
|
||||
在CI/CD流程中运行测试:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/test.yml
|
||||
- name: Run tests
|
||||
run: |
|
||||
cd backend
|
||||
pytest --cov=app --cov-report=xml
|
||||
```
|
||||
3
backend/tests/__init__.py
Normal file
3
backend/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
测试包
|
||||
"""
|
||||
136
backend/tests/conftest.py
Normal file
136
backend/tests/conftest.py
Normal file
@@ -0,0 +1,136 @@
|
||||
"""
|
||||
Pytest配置和共享fixtures
|
||||
"""
|
||||
import pytest
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from fastapi.testclient import TestClient
|
||||
from app.core.database import Base, get_db, SessionLocal
|
||||
from app.main import app
|
||||
from app.core.config import settings
|
||||
import os
|
||||
|
||||
# 测试数据库URL(使用SQLite内存数据库)
|
||||
TEST_DATABASE_URL = "sqlite:///:memory:"
|
||||
|
||||
# 创建测试数据库引擎
|
||||
test_engine = create_engine(
|
||||
TEST_DATABASE_URL,
|
||||
connect_args={"check_same_thread": False}
|
||||
)
|
||||
|
||||
# 创建测试会话工厂
|
||||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=test_engine)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def db_session():
|
||||
"""创建测试数据库会话"""
|
||||
# 创建所有表
|
||||
Base.metadata.create_all(bind=test_engine)
|
||||
|
||||
# 创建会话
|
||||
session = TestingSessionLocal()
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
session.close()
|
||||
# 删除所有表
|
||||
Base.metadata.drop_all(bind=test_engine)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def client(db_session):
|
||||
"""创建测试客户端"""
|
||||
def override_get_db():
|
||||
try:
|
||||
yield db_session
|
||||
finally:
|
||||
pass
|
||||
|
||||
app.dependency_overrides[get_db] = override_get_db
|
||||
|
||||
with TestClient(app) as test_client:
|
||||
yield test_client
|
||||
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def test_user_data():
|
||||
"""测试用户数据"""
|
||||
return {
|
||||
"username": "testuser",
|
||||
"email": "test@example.com",
|
||||
"password": "testpassword123"
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authenticated_client(client, test_user_data):
|
||||
"""创建已认证的测试客户端"""
|
||||
# 注册用户
|
||||
response = client.post("/api/v1/auth/register", json=test_user_data)
|
||||
assert response.status_code == 201
|
||||
|
||||
# 登录获取token
|
||||
login_response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={
|
||||
"username": test_user_data["username"],
|
||||
"password": test_user_data["password"]
|
||||
}
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
token = login_response.json()["access_token"]
|
||||
|
||||
# 设置认证头
|
||||
client.headers.update({"Authorization": f"Bearer {token}"})
|
||||
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_workflow_data():
|
||||
"""示例工作流数据"""
|
||||
return {
|
||||
"name": "测试工作流",
|
||||
"description": "这是一个测试工作流",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "start-1",
|
||||
"type": "start",
|
||||
"position": {"x": 0, "y": 0},
|
||||
"data": {"label": "开始"}
|
||||
},
|
||||
{
|
||||
"id": "llm-1",
|
||||
"type": "llm",
|
||||
"position": {"x": 200, "y": 0},
|
||||
"data": {
|
||||
"label": "LLM节点",
|
||||
"provider": "deepseek",
|
||||
"prompt": "请回答:{input}",
|
||||
"model": "deepseek-chat"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "end-1",
|
||||
"type": "end",
|
||||
"position": {"x": 400, "y": 0},
|
||||
"data": {"label": "结束"}
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e1",
|
||||
"source": "start-1",
|
||||
"target": "llm-1"
|
||||
},
|
||||
{
|
||||
"id": "e2",
|
||||
"source": "llm-1",
|
||||
"target": "end-1"
|
||||
}
|
||||
]
|
||||
}
|
||||
100
backend/tests/test_auth.py
Normal file
100
backend/tests/test_auth.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
用户认证API测试
|
||||
"""
|
||||
import pytest
|
||||
from fastapi import status
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.auth
|
||||
class TestAuth:
|
||||
"""认证相关测试"""
|
||||
|
||||
def test_register_user(self, client, test_user_data):
|
||||
"""测试用户注册"""
|
||||
response = client.post("/api/v1/auth/register", json=test_user_data)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
data = response.json()
|
||||
assert "id" in data
|
||||
assert data["username"] == test_user_data["username"]
|
||||
assert data["email"] == test_user_data["email"]
|
||||
assert "password_hash" not in data # 密码哈希不应该返回
|
||||
|
||||
def test_register_duplicate_username(self, client, test_user_data):
|
||||
"""测试重复用户名注册"""
|
||||
# 第一次注册
|
||||
response1 = client.post("/api/v1/auth/register", json=test_user_data)
|
||||
assert response1.status_code == status.HTTP_201_CREATED
|
||||
|
||||
# 第二次注册相同用户名
|
||||
response2 = client.post("/api/v1/auth/register", json=test_user_data)
|
||||
assert response2.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_register_duplicate_email(self, client, test_user_data):
|
||||
"""测试重复邮箱注册"""
|
||||
# 第一次注册
|
||||
response1 = client.post("/api/v1/auth/register", json=test_user_data)
|
||||
assert response1.status_code == status.HTTP_201_CREATED
|
||||
|
||||
# 使用相同邮箱但不同用户名
|
||||
duplicate_data = test_user_data.copy()
|
||||
duplicate_data["username"] = "another_user"
|
||||
response2 = client.post("/api/v1/auth/register", json=duplicate_data)
|
||||
assert response2.status_code == status.HTTP_400_BAD_REQUEST
|
||||
|
||||
def test_login_success(self, client, test_user_data):
|
||||
"""测试登录成功"""
|
||||
# 先注册
|
||||
client.post("/api/v1/auth/register", json=test_user_data)
|
||||
|
||||
# 登录
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={
|
||||
"username": test_user_data["username"],
|
||||
"password": test_user_data["password"]
|
||||
}
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert "access_token" in data
|
||||
assert data["token_type"] == "bearer"
|
||||
|
||||
def test_login_wrong_password(self, client, test_user_data):
|
||||
"""测试错误密码登录"""
|
||||
# 先注册
|
||||
client.post("/api/v1/auth/register", json=test_user_data)
|
||||
|
||||
# 使用错误密码登录
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={
|
||||
"username": test_user_data["username"],
|
||||
"password": "wrongpassword"
|
||||
}
|
||||
)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
def test_login_nonexistent_user(self, client):
|
||||
"""测试不存在的用户登录"""
|
||||
response = client.post(
|
||||
"/api/v1/auth/login",
|
||||
data={
|
||||
"username": "nonexistent",
|
||||
"password": "password123"
|
||||
}
|
||||
)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
|
||||
def test_get_current_user(self, authenticated_client, test_user_data):
|
||||
"""测试获取当前用户信息"""
|
||||
response = authenticated_client.get("/api/v1/auth/me")
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["username"] == test_user_data["username"]
|
||||
assert data["email"] == test_user_data["email"]
|
||||
|
||||
def test_get_current_user_unauthorized(self, client):
|
||||
"""测试未授权访问当前用户信息"""
|
||||
response = client.get("/api/v1/auth/me")
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
152
backend/tests/test_workflow_engine.py
Normal file
152
backend/tests/test_workflow_engine.py
Normal file
@@ -0,0 +1,152 @@
|
||||
"""
|
||||
工作流执行引擎测试
|
||||
"""
|
||||
import pytest
|
||||
from app.services.workflow_engine import WorkflowEngine
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.workflow
|
||||
class TestWorkflowEngine:
|
||||
"""工作流引擎测试"""
|
||||
|
||||
def test_build_execution_graph(self):
|
||||
"""测试构建执行图"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "llm-1", "type": "llm"},
|
||||
{"id": "end-1", "type": "end"}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "e1", "source": "start-1", "target": "llm-1"},
|
||||
{"id": "e2", "source": "llm-1", "target": "end-1"}
|
||||
]
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
execution_order = engine.build_execution_graph()
|
||||
|
||||
assert "start-1" in execution_order
|
||||
assert "llm-1" in execution_order
|
||||
assert "end-1" in execution_order
|
||||
assert execution_order.index("start-1") < execution_order.index("llm-1")
|
||||
assert execution_order.index("llm-1") < execution_order.index("end-1")
|
||||
|
||||
def test_get_node_input(self):
|
||||
"""测试获取节点输入"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "llm-1", "type": "llm"}
|
||||
],
|
||||
"edges": [
|
||||
{"id": "e1", "source": "start-1", "target": "llm-1"}
|
||||
]
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
engine.node_outputs = {
|
||||
"start-1": {"input": "test data"}
|
||||
}
|
||||
|
||||
input_data = engine.get_node_input("llm-1", engine.node_outputs)
|
||||
assert "input" in input_data
|
||||
assert input_data["input"] == "test data"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_start_node(self):
|
||||
"""测试执行开始节点"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{"id": "start-1", "type": "start", "data": {"label": "开始"}}
|
||||
],
|
||||
"edges": []
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
node = workflow_data["nodes"][0]
|
||||
input_data = {"test": "data"}
|
||||
|
||||
result = await engine.execute_node(node, input_data)
|
||||
assert result["status"] == "success"
|
||||
assert result["output"] == input_data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_end_node(self):
|
||||
"""测试执行结束节点"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{"id": "end-1", "type": "end", "data": {"label": "结束"}}
|
||||
],
|
||||
"edges": []
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
node = workflow_data["nodes"][0]
|
||||
input_data = {"result": "final output"}
|
||||
|
||||
result = await engine.execute_node(node, input_data)
|
||||
assert result["status"] == "success"
|
||||
assert result["output"] == input_data
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_condition_node(self):
|
||||
"""测试执行条件节点"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "condition-1",
|
||||
"type": "condition",
|
||||
"data": {
|
||||
"label": "条件判断",
|
||||
"expression": "{value} > 10"
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": []
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
node = workflow_data["nodes"][0]
|
||||
|
||||
# 测试条件为真
|
||||
input_data = {"value": 15}
|
||||
result = await engine.execute_node(node, input_data)
|
||||
assert result["status"] == "success"
|
||||
assert result["branch"] == "true"
|
||||
|
||||
# 测试条件为假
|
||||
input_data = {"value": 5}
|
||||
result = await engine.execute_node(node, input_data)
|
||||
assert result["status"] == "success"
|
||||
assert result["branch"] == "false"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_transform_node(self):
|
||||
"""测试执行转换节点"""
|
||||
workflow_data = {
|
||||
"nodes": [
|
||||
{
|
||||
"id": "transform-1",
|
||||
"type": "transform",
|
||||
"data": {
|
||||
"label": "数据转换",
|
||||
"mode": "mapping",
|
||||
"mapping": {
|
||||
"new_field": "{old_field}"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"edges": []
|
||||
}
|
||||
|
||||
engine = WorkflowEngine("test-workflow", workflow_data)
|
||||
node = workflow_data["nodes"][0]
|
||||
input_data = {"old_field": "test value"}
|
||||
|
||||
result = await engine.execute_node(node, input_data)
|
||||
assert result["status"] == "success"
|
||||
assert "new_field" in result["output"]
|
||||
assert result["output"]["new_field"] == "test value"
|
||||
115
backend/tests/test_workflow_validator.py
Normal file
115
backend/tests/test_workflow_validator.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""
|
||||
工作流验证器测试
|
||||
"""
|
||||
import pytest
|
||||
from app.services.workflow_validator import WorkflowValidator
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.workflow
|
||||
class TestWorkflowValidator:
|
||||
"""工作流验证器测试"""
|
||||
|
||||
def test_validate_valid_workflow(self):
|
||||
"""测试验证有效工作流"""
|
||||
nodes = [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "llm-1", "type": "llm"},
|
||||
{"id": "end-1", "type": "end"}
|
||||
]
|
||||
edges = [
|
||||
{"id": "e1", "source": "start-1", "target": "llm-1"},
|
||||
{"id": "e2", "source": "llm-1", "target": "end-1"}
|
||||
]
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
assert is_valid is True
|
||||
assert len(errors) == 0
|
||||
|
||||
def test_validate_no_start_node(self):
|
||||
"""测试验证缺少开始节点"""
|
||||
nodes = [
|
||||
{"id": "llm-1", "type": "llm"},
|
||||
{"id": "end-1", "type": "end"}
|
||||
]
|
||||
edges = [
|
||||
{"id": "e1", "source": "llm-1", "target": "end-1"}
|
||||
]
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
assert is_valid is False
|
||||
assert any("开始节点" in error for error in errors)
|
||||
|
||||
def test_validate_duplicate_node_id(self):
|
||||
"""测试验证重复节点ID"""
|
||||
nodes = [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "start-1", "type": "llm"}, # 重复ID
|
||||
{"id": "end-1", "type": "end"}
|
||||
]
|
||||
edges = []
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
assert is_valid is False
|
||||
assert any("重复" in error for error in errors)
|
||||
|
||||
def test_validate_cycle_detection(self):
|
||||
"""测试循环检测"""
|
||||
nodes = [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "node-1", "type": "llm"},
|
||||
{"id": "node-2", "type": "llm"}
|
||||
]
|
||||
edges = [
|
||||
{"id": "e1", "source": "start-1", "target": "node-1"},
|
||||
{"id": "e2", "source": "node-1", "target": "node-2"},
|
||||
{"id": "e3", "source": "node-2", "target": "node-1"} # 形成循环
|
||||
]
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
assert is_valid is False
|
||||
assert any("循环" in error for error in errors)
|
||||
|
||||
def test_validate_invalid_edge(self):
|
||||
"""测试验证无效边"""
|
||||
nodes = [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "end-1", "type": "end"}
|
||||
]
|
||||
edges = [
|
||||
{"id": "e1", "source": "nonexistent", "target": "end-1"} # 源节点不存在
|
||||
]
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
assert is_valid is False
|
||||
assert any("不存在" in error for error in errors)
|
||||
|
||||
def test_validate_condition_branches(self):
|
||||
"""测试验证条件节点分支"""
|
||||
nodes = [
|
||||
{"id": "start-1", "type": "start"},
|
||||
{"id": "condition-1", "type": "condition"},
|
||||
{"id": "end-1", "type": "end"}
|
||||
]
|
||||
edges = [
|
||||
{"id": "e1", "source": "start-1", "target": "condition-1"},
|
||||
{"id": "e2", "source": "condition-1", "target": "end-1", "sourceHandle": "true"}
|
||||
# 缺少false分支
|
||||
]
|
||||
|
||||
validator = WorkflowValidator(nodes, edges)
|
||||
is_valid, errors, warnings = validator.validate()
|
||||
|
||||
# 缺少分支是警告,不是错误
|
||||
assert is_valid is True
|
||||
assert any("False分支" in warning for warning in warnings)
|
||||
108
backend/tests/test_workflows.py
Normal file
108
backend/tests/test_workflows.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""
|
||||
工作流API测试
|
||||
"""
|
||||
import pytest
|
||||
from fastapi import status
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
@pytest.mark.workflow
|
||||
class TestWorkflows:
|
||||
"""工作流相关测试"""
|
||||
|
||||
def test_create_workflow(self, authenticated_client, sample_workflow_data):
|
||||
"""测试创建工作流"""
|
||||
response = authenticated_client.post(
|
||||
"/api/v1/workflows",
|
||||
json=sample_workflow_data
|
||||
)
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
data = response.json()
|
||||
assert data["name"] == sample_workflow_data["name"]
|
||||
assert data["description"] == sample_workflow_data["description"]
|
||||
assert "id" in data
|
||||
assert "nodes" in data
|
||||
assert "edges" in data
|
||||
|
||||
def test_get_workflow_list(self, authenticated_client, sample_workflow_data):
|
||||
"""测试获取工作流列表"""
|
||||
# 创建几个工作流
|
||||
for i in range(3):
|
||||
workflow_data = sample_workflow_data.copy()
|
||||
workflow_data["name"] = f"工作流{i+1}"
|
||||
authenticated_client.post("/api/v1/workflows", json=workflow_data)
|
||||
|
||||
# 获取列表
|
||||
response = authenticated_client.get("/api/v1/workflows")
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert isinstance(data, list)
|
||||
assert len(data) >= 3
|
||||
|
||||
def test_get_workflow_detail(self, authenticated_client, sample_workflow_data):
|
||||
"""测试获取工作流详情"""
|
||||
# 创建工作流
|
||||
create_response = authenticated_client.post(
|
||||
"/api/v1/workflows",
|
||||
json=sample_workflow_data
|
||||
)
|
||||
workflow_id = create_response.json()["id"]
|
||||
|
||||
# 获取详情
|
||||
response = authenticated_client.get(f"/api/v1/workflows/{workflow_id}")
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["id"] == workflow_id
|
||||
assert data["name"] == sample_workflow_data["name"]
|
||||
assert len(data["nodes"]) == len(sample_workflow_data["nodes"])
|
||||
assert len(data["edges"]) == len(sample_workflow_data["edges"])
|
||||
|
||||
def test_update_workflow(self, authenticated_client, sample_workflow_data):
|
||||
"""测试更新工作流"""
|
||||
# 创建工作流
|
||||
create_response = authenticated_client.post(
|
||||
"/api/v1/workflows",
|
||||
json=sample_workflow_data
|
||||
)
|
||||
workflow_id = create_response.json()["id"]
|
||||
|
||||
# 更新工作流
|
||||
update_data = sample_workflow_data.copy()
|
||||
update_data["name"] = "更新后的工作流"
|
||||
update_data["description"] = "更新后的描述"
|
||||
|
||||
response = authenticated_client.put(
|
||||
f"/api/v1/workflows/{workflow_id}",
|
||||
json=update_data
|
||||
)
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
data = response.json()
|
||||
assert data["name"] == "更新后的工作流"
|
||||
assert data["description"] == "更新后的描述"
|
||||
|
||||
def test_delete_workflow(self, authenticated_client, sample_workflow_data):
|
||||
"""测试删除工作流"""
|
||||
# 创建工作流
|
||||
create_response = authenticated_client.post(
|
||||
"/api/v1/workflows",
|
||||
json=sample_workflow_data
|
||||
)
|
||||
workflow_id = create_response.json()["id"]
|
||||
|
||||
# 删除工作流
|
||||
response = authenticated_client.delete(f"/api/v1/workflows/{workflow_id}")
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
|
||||
# 验证已删除
|
||||
get_response = authenticated_client.get(f"/api/v1/workflows/{workflow_id}")
|
||||
assert get_response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_get_workflow_not_found(self, authenticated_client):
|
||||
"""测试获取不存在的工作流"""
|
||||
response = authenticated_client.get("/api/v1/workflows/nonexistent-id")
|
||||
assert response.status_code == status.HTTP_404_NOT_FOUND
|
||||
|
||||
def test_create_workflow_unauthorized(self, client, sample_workflow_data):
|
||||
"""测试未授权创建工作流"""
|
||||
response = client.post("/api/v1/workflows", json=sample_workflow_data)
|
||||
assert response.status_code == status.HTTP_401_UNAUTHORIZED
|
||||
Reference in New Issue
Block a user