|
|
@@ -0,0 +1,370 @@
|
|
|
+"""
|
|
|
+施工方案审查API错误码测试 - pytest版本
|
|
|
+使用pytest运行的标准测试套件
|
|
|
+"""
|
|
|
+
|
|
|
+import pytest
|
|
|
+import requests
|
|
|
+import json
|
|
|
+import uuid
|
|
|
+import time
|
|
|
+import os
|
|
|
+from typing import Dict, Any
|
|
|
+
|
|
|
+# pytest fixtures
|
|
|
+@pytest.fixture(scope="class")
|
|
|
+def api_config():
|
|
|
+ """API配置fixture"""
|
|
|
+ return {
|
|
|
+ "base_url": "http://127.0.0.1:8034",
|
|
|
+ "api_prefix": "/sgsc",
|
|
|
+ "valid_user": "user-001",
|
|
|
+ "valid_project_type": "bridge_up_part",
|
|
|
+ "test_callback_url": "http://test.callback.com"
|
|
|
+ }
|
|
|
+
|
|
|
+@pytest.fixture
|
|
|
+def test_file():
|
|
|
+ """测试文件fixture - 每个测试都创建新的文件对象"""
|
|
|
+ file_path = "data_pipeline/test_rawdata/1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf"
|
|
|
+
|
|
|
+ class TestFile:
|
|
|
+ def __init__(self):
|
|
|
+ if os.path.exists(file_path):
|
|
|
+ self.file = open(file_path, 'rb')
|
|
|
+ self.file_tuple = (os.path.basename(file_path), self.file, 'application/pdf')
|
|
|
+ self.close_file = True
|
|
|
+ else:
|
|
|
+ self.file = None
|
|
|
+ self.file_tuple = ("test.pdf", b"mock pdf content", "application/pdf")
|
|
|
+ self.close_file = False
|
|
|
+
|
|
|
+ def get_file(self):
|
|
|
+ """获取文件元组"""
|
|
|
+ if self.close_file and self.file:
|
|
|
+ # 重新打开文件,确保文件未被关闭
|
|
|
+ self.file.seek(0)
|
|
|
+ return self.file_tuple
|
|
|
+
|
|
|
+ def cleanup(self):
|
|
|
+ """清理资源"""
|
|
|
+ if self.close_file and self.file:
|
|
|
+ self.file.close()
|
|
|
+
|
|
|
+ test_file_obj = TestFile()
|
|
|
+ yield test_file_obj
|
|
|
+ test_file_obj.cleanup()
|
|
|
+
|
|
|
+class TestFileUploadErrors:
|
|
|
+ """文件上传接口错误码测试"""
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("test_case,expected_code", [
|
|
|
+ ("missing_file", "WJSC001"),
|
|
|
+ ("empty_file", "WJSC003"),
|
|
|
+ ("unsupported_format", "WJSC004"),
|
|
|
+ ("invalid_project_type", "WJSC006")
|
|
|
+ ])
|
|
|
+ def test_file_upload_errors(self, api_config, test_case, expected_code):
|
|
|
+ """测试文件上传各种错误场景"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
|
|
|
+
|
|
|
+ if test_case == "missing_file":
|
|
|
+ # 不上传文件
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+ response = requests.post(url, data=data)
|
|
|
+
|
|
|
+ elif test_case == "empty_file":
|
|
|
+ # 上传空文件
|
|
|
+ files = {"file": ("empty.pdf", b"", "application/pdf")}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+
|
|
|
+ elif test_case == "unsupported_format":
|
|
|
+ # 上传不支持的格式
|
|
|
+ files = {"file": ("test.txt", b"text content", "text/plain")}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+
|
|
|
+ elif test_case == "invalid_project_type":
|
|
|
+ # 无效的工程方案类型
|
|
|
+ files = {"file": ("test.pdf", b"mock pdf content", "application/pdf")}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": "invalid_type",
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+
|
|
|
+ # 验证错误响应
|
|
|
+ assert response.status_code in [400, 403, 404] # 允许的业务错误状态码
|
|
|
+
|
|
|
+ try:
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == expected_code
|
|
|
+ assert "error_type" in error_data
|
|
|
+ assert "message" in error_data
|
|
|
+ except json.JSONDecodeError:
|
|
|
+ pytest.fail(f"响应不是有效的JSON: {response.text}")
|
|
|
+
|
|
|
+ def test_file_upload_success(self, api_config, test_file):
|
|
|
+ """测试文件上传成功"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
|
|
|
+ files = {"file": test_file.get_file()}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+ assert response.status_code == 200
|
|
|
+
|
|
|
+ result = response.json()
|
|
|
+ assert "data" in result
|
|
|
+ assert "callback_task_id" in result["data"]
|
|
|
+ assert "id" in result["data"]
|
|
|
+
|
|
|
+ @pytest.mark.skip(reason="文件大小检查未实现")
|
|
|
+ def test_wjsc005_file_size_exceeded(self, api_config):
|
|
|
+ """测试WJSC005: 文件过大 - 跳过因为未实现"""
|
|
|
+ pass
|
|
|
+
|
|
|
+ @pytest.mark.skip(reason="认证检查未实现")
|
|
|
+ def test_wjsc007_unauthorized(self, api_config):
|
|
|
+ """测试WJSC007: 认证失败 - 跳过因为未实现"""
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class TestTaskProgressErrors:
|
|
|
+ """进度查询接口错误码测试"""
|
|
|
+
|
|
|
+ def test_jdlx001_missing_parameters(self, api_config):
|
|
|
+ """测试JDLX001: 请求参数缺失"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/test-callback-id"
|
|
|
+ response = requests.get(url) # 不提供user参数
|
|
|
+
|
|
|
+ assert response.status_code == 400
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "JDLX001"
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("invalid_id", ["short", "123", "invalid-format"])
|
|
|
+ def test_jdlx002_invalid_param_format(self, api_config, invalid_id):
|
|
|
+ """测试JDLX002: 请求参数格式错误"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{invalid_id}"
|
|
|
+ params = {"user": api_config["valid_user"]}
|
|
|
+
|
|
|
+ response = requests.get(url, params=params)
|
|
|
+ assert response.status_code == 400
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "JDLX002"
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("invalid_user", ["invalid_user", "user-999", ""])
|
|
|
+ def test_jdlx004_invalid_user(self, api_config, test_file, invalid_user):
|
|
|
+ """测试JDLX004: 用户标识无效"""
|
|
|
+ # 先上传文件获取有效的callback_task_id
|
|
|
+ callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
|
|
|
+
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
|
|
|
+ params = {"user": invalid_user}
|
|
|
+
|
|
|
+ response = requests.get(url, params=params)
|
|
|
+ assert response.status_code == 403
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "JDLX004"
|
|
|
+
|
|
|
+ def test_jdlx005_task_not_found(self, api_config):
|
|
|
+ """测试JDLX005: 任务不存在"""
|
|
|
+ fake_callback_id = f"{uuid.uuid4()}-{int(time.time())}"
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{fake_callback_id}"
|
|
|
+ params = {"user": api_config["valid_user"]}
|
|
|
+
|
|
|
+ response = requests.get(url, params=params)
|
|
|
+ assert response.status_code == 404
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "JDLX005"
|
|
|
+
|
|
|
+ def _upload_file_and_get_callback(self, api_config, test_file):
|
|
|
+ """辅助方法:上传文件并获取callback_task_id"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
|
|
|
+ files = {"file": test_file.get_file()}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+ assert response.status_code == 200
|
|
|
+ result = response.json()
|
|
|
+ return result["data"]["callback_task_id"]
|
|
|
+
|
|
|
+ @pytest.mark.skip(reason="认证检查未实现")
|
|
|
+ def test_jdlx003_unauthorized(self, api_config):
|
|
|
+ """测试JDLX003: 认证失败 - 跳过因为未实现"""
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class TestReviewResultsErrors:
|
|
|
+ """审查结果接口错误码测试"""
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("invalid_type", ["invalid", "risk", "detail", ""])
|
|
|
+ def test_scjg001_invalid_type(self, api_config, invalid_type):
|
|
|
+ """测试SCJG001: 结果类型无效"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
|
|
|
+ payload = {
|
|
|
+ "id": str(uuid.uuid4()),
|
|
|
+ "user": api_config["valid_user"],
|
|
|
+ "type": invalid_type
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, json=payload)
|
|
|
+ assert response.status_code == 400
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "SCJG001"
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("invalid_id", ["", None])
|
|
|
+ def test_scjg002_missing_param_id(self, api_config, invalid_id):
|
|
|
+ """测试SCJG002: 缺少文档ID"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
|
|
|
+
|
|
|
+ if invalid_id is None:
|
|
|
+ payload = {
|
|
|
+ "user": api_config["valid_user"],
|
|
|
+ "type": "summary"
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ payload = {
|
|
|
+ "id": invalid_id,
|
|
|
+ "user": api_config["valid_user"],
|
|
|
+ "type": "summary"
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, json=payload)
|
|
|
+ assert response.status_code == 400
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "SCJG002"
|
|
|
+
|
|
|
+ @pytest.mark.parametrize("invalid_format", ["123", "short-id", "invalid-uuid-format"])
|
|
|
+ def test_scjg003_invalid_id_format(self, api_config, invalid_format):
|
|
|
+ """测试SCJG003: 文档ID格式错误"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
|
|
|
+ payload = {
|
|
|
+ "id": invalid_format,
|
|
|
+ "user": api_config["valid_user"],
|
|
|
+ "type": "summary"
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, json=payload)
|
|
|
+ assert response.status_code == 400
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "SCJG003"
|
|
|
+
|
|
|
+ def test_scjg005_invalid_user_review_results(self, api_config, test_file):
|
|
|
+ """测试SCJG005: 用户标识无效(审查结果接口)"""
|
|
|
+ # 先上传文件获取有效的文件ID
|
|
|
+ file_id = self._upload_file_and_get_file_id(api_config, test_file)
|
|
|
+
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
|
|
|
+ payload = {
|
|
|
+ "id": file_id,
|
|
|
+ "user": "invalid_user",
|
|
|
+ "type": "summary"
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, json=payload)
|
|
|
+ assert response.status_code == 403
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "SCJG005"
|
|
|
+
|
|
|
+ def test_scjg006_task_not_found_review_results(self, api_config):
|
|
|
+ """测试SCJG006: 任务不存在(审查结果接口)"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
|
|
|
+ payload = {
|
|
|
+ "id": str(uuid.uuid4()),
|
|
|
+ "user": api_config["valid_user"],
|
|
|
+ "type": "summary"
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, json=payload)
|
|
|
+ assert response.status_code == 404
|
|
|
+
|
|
|
+ error_data = response.json()
|
|
|
+ assert error_data["code"] == "SCJG006"
|
|
|
+
|
|
|
+ def _upload_file_and_get_file_id(self, api_config, test_file):
|
|
|
+ """辅助方法:上传文件并获取文件ID"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
|
|
|
+ files = {"file": test_file.get_file()}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+ assert response.status_code == 200
|
|
|
+ result = response.json()
|
|
|
+ return result["data"]["id"]
|
|
|
+
|
|
|
+ @pytest.mark.skip(reason="认证检查未实现")
|
|
|
+ def test_scjg004_unauthorized(self, api_config):
|
|
|
+ """测试SCJG004: 认证失败 - 跳过因为未实现"""
|
|
|
+ pass
|
|
|
+
|
|
|
+
|
|
|
+class TestIntegration:
|
|
|
+ """集成测试"""
|
|
|
+
|
|
|
+ def test_complete_workflow_success(self, api_config, test_file):
|
|
|
+ """测试完整工作流程成功场景"""
|
|
|
+ # 1. 文件上传
|
|
|
+ callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
|
|
|
+ assert callback_task_id is not None
|
|
|
+
|
|
|
+ # 2. 进度查询
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
|
|
|
+ params = {"user": api_config["valid_user"]}
|
|
|
+ response = requests.get(url, params=params)
|
|
|
+ assert response.status_code == 200
|
|
|
+
|
|
|
+ def _upload_file_and_get_callback(self, api_config, test_file):
|
|
|
+ """辅助方法:上传文件并获取callback_task_id"""
|
|
|
+ url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
|
|
|
+ files = {"file": test_file.get_file()}
|
|
|
+ data = {
|
|
|
+ "callback_url": api_config["test_callback_url"],
|
|
|
+ "project_plan_type": api_config["valid_project_type"],
|
|
|
+ "user": api_config["valid_user"]
|
|
|
+ }
|
|
|
+
|
|
|
+ response = requests.post(url, files=files, data=data)
|
|
|
+ assert response.status_code == 200
|
|
|
+ result = response.json()
|
|
|
+ return result["data"]["callback_task_id"]
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ # 如果直接运行此文件,给出提示
|
|
|
+ print("请使用 pytest 运行此测试文件:")
|
|
|
+ print("pytest test/construction_review/test_error_codes_pytest.py -v")
|
|
|
+ print("或者运行所有测试:")
|
|
|
+ print("pytest test/ -v")
|