test_error_codes_pytest.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. """
  2. 施工方案审查API错误码测试 - pytest版本
  3. 使用pytest运行的标准测试套件
  4. """
  5. import pytest
  6. import requests
  7. import json
  8. import uuid
  9. import time
  10. import os
  11. from typing import Dict, Any
  12. # pytest fixtures
  13. @pytest.fixture(scope="class")
  14. def api_config():
  15. """API配置fixture"""
  16. return {
  17. "base_url": "http://127.0.0.1:8034",
  18. "api_prefix": "/sgsc",
  19. "valid_user": "user-001",
  20. "valid_project_type": "bridge_up_part",
  21. "test_callback_url": "http://test.callback.com"
  22. }
  23. @pytest.fixture
  24. def test_file():
  25. """测试文件fixture - 每个测试都创建新的文件对象"""
  26. file_path = "data_pipeline/test_rawdata/1f3e1d98-5b4a-4a06-87b3-c7f0413b901a.pdf"
  27. class TestFile:
  28. def __init__(self):
  29. if os.path.exists(file_path):
  30. self.file = open(file_path, 'rb')
  31. self.file_tuple = (os.path.basename(file_path), self.file, 'application/pdf')
  32. self.close_file = True
  33. else:
  34. self.file = None
  35. self.file_tuple = ("test.pdf", b"mock pdf content", "application/pdf")
  36. self.close_file = False
  37. def get_file(self):
  38. """获取文件元组"""
  39. if self.close_file and self.file:
  40. # 重新打开文件,确保文件未被关闭
  41. self.file.seek(0)
  42. return self.file_tuple
  43. def cleanup(self):
  44. """清理资源"""
  45. if self.close_file and self.file:
  46. self.file.close()
  47. test_file_obj = TestFile()
  48. yield test_file_obj
  49. test_file_obj.cleanup()
  50. class TestFileUploadErrors:
  51. """文件上传接口错误码测试"""
  52. @pytest.mark.parametrize("test_case,expected_code", [
  53. ("missing_file", "WJSC001"),
  54. ("empty_file", "WJSC003"),
  55. ("unsupported_format", "WJSC004"),
  56. ("invalid_project_type", "WJSC006")
  57. ])
  58. def test_file_upload_errors(self, api_config, test_case, expected_code):
  59. """测试文件上传各种错误场景"""
  60. url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
  61. if test_case == "missing_file":
  62. # 不上传文件
  63. data = {
  64. "callback_url": api_config["test_callback_url"],
  65. "project_plan_type": api_config["valid_project_type"],
  66. "user": api_config["valid_user"]
  67. }
  68. response = requests.post(url, data=data)
  69. elif test_case == "empty_file":
  70. # 上传空文件
  71. files = {"file": ("empty.pdf", b"", "application/pdf")}
  72. data = {
  73. "callback_url": api_config["test_callback_url"],
  74. "project_plan_type": api_config["valid_project_type"],
  75. "user": api_config["valid_user"]
  76. }
  77. response = requests.post(url, files=files, data=data)
  78. elif test_case == "unsupported_format":
  79. # 上传不支持的格式
  80. files = {"file": ("test.txt", b"text content", "text/plain")}
  81. data = {
  82. "callback_url": api_config["test_callback_url"],
  83. "project_plan_type": api_config["valid_project_type"],
  84. "user": api_config["valid_user"]
  85. }
  86. response = requests.post(url, files=files, data=data)
  87. elif test_case == "invalid_project_type":
  88. # 无效的工程方案类型
  89. files = {"file": ("test.pdf", b"mock pdf content", "application/pdf")}
  90. data = {
  91. "callback_url": api_config["test_callback_url"],
  92. "project_plan_type": "invalid_type",
  93. "user": api_config["valid_user"]
  94. }
  95. response = requests.post(url, files=files, data=data)
  96. # 验证错误响应
  97. assert response.status_code in [400, 403, 404] # 允许的业务错误状态码
  98. try:
  99. error_data = response.json()
  100. assert error_data["code"] == expected_code
  101. assert "error_type" in error_data
  102. assert "message" in error_data
  103. except json.JSONDecodeError:
  104. pytest.fail(f"响应不是有效的JSON: {response.text}")
  105. def test_file_upload_success(self, api_config, test_file):
  106. """测试文件上传成功"""
  107. url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
  108. files = {"file": test_file.get_file()}
  109. data = {
  110. "callback_url": api_config["test_callback_url"],
  111. "project_plan_type": api_config["valid_project_type"],
  112. "user": api_config["valid_user"]
  113. }
  114. response = requests.post(url, files=files, data=data)
  115. assert response.status_code == 200
  116. result = response.json()
  117. assert "data" in result
  118. assert "callback_task_id" in result["data"]
  119. assert "id" in result["data"]
  120. @pytest.mark.skip(reason="文件大小检查未实现")
  121. def test_wjsc005_file_size_exceeded(self, api_config):
  122. """测试WJSC005: 文件过大 - 跳过因为未实现"""
  123. pass
  124. @pytest.mark.skip(reason="认证检查未实现")
  125. def test_wjsc007_unauthorized(self, api_config):
  126. """测试WJSC007: 认证失败 - 跳过因为未实现"""
  127. pass
  128. class TestTaskProgressErrors:
  129. """进度查询接口错误码测试"""
  130. def test_jdlx001_missing_parameters(self, api_config):
  131. """测试JDLX001: 请求参数缺失"""
  132. url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/test-callback-id"
  133. response = requests.get(url) # 不提供user参数
  134. assert response.status_code == 400
  135. error_data = response.json()
  136. assert error_data["code"] == "JDLX001"
  137. @pytest.mark.parametrize("invalid_id", ["short", "123", "invalid-format"])
  138. def test_jdlx002_invalid_param_format(self, api_config, invalid_id):
  139. """测试JDLX002: 请求参数格式错误"""
  140. url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{invalid_id}"
  141. params = {"user": api_config["valid_user"]}
  142. response = requests.get(url, params=params)
  143. assert response.status_code == 400
  144. error_data = response.json()
  145. assert error_data["code"] == "JDLX002"
  146. @pytest.mark.parametrize("invalid_user", ["invalid_user", "user-999", ""])
  147. def test_jdlx004_invalid_user(self, api_config, test_file, invalid_user):
  148. """测试JDLX004: 用户标识无效"""
  149. # 先上传文件获取有效的callback_task_id
  150. callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
  151. url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
  152. params = {"user": invalid_user}
  153. response = requests.get(url, params=params)
  154. assert response.status_code == 403
  155. error_data = response.json()
  156. assert error_data["code"] == "JDLX004"
  157. def test_jdlx005_task_not_found(self, api_config):
  158. """测试JDLX005: 任务不存在"""
  159. fake_callback_id = f"{uuid.uuid4()}-{int(time.time())}"
  160. url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{fake_callback_id}"
  161. params = {"user": api_config["valid_user"]}
  162. response = requests.get(url, params=params)
  163. assert response.status_code == 404
  164. error_data = response.json()
  165. assert error_data["code"] == "JDLX005"
  166. def _upload_file_and_get_callback(self, api_config, test_file):
  167. """辅助方法:上传文件并获取callback_task_id"""
  168. url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
  169. files = {"file": test_file.get_file()}
  170. data = {
  171. "callback_url": api_config["test_callback_url"],
  172. "project_plan_type": api_config["valid_project_type"],
  173. "user": api_config["valid_user"]
  174. }
  175. response = requests.post(url, files=files, data=data)
  176. assert response.status_code == 200
  177. result = response.json()
  178. return result["data"]["callback_task_id"]
  179. @pytest.mark.skip(reason="认证检查未实现")
  180. def test_jdlx003_unauthorized(self, api_config):
  181. """测试JDLX003: 认证失败 - 跳过因为未实现"""
  182. pass
  183. class TestReviewResultsErrors:
  184. """审查结果接口错误码测试"""
  185. @pytest.mark.parametrize("invalid_type", ["invalid", "risk", "detail", ""])
  186. def test_scjg001_invalid_type(self, api_config, invalid_type):
  187. """测试SCJG001: 结果类型无效"""
  188. url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
  189. payload = {
  190. "id": str(uuid.uuid4()),
  191. "user": api_config["valid_user"],
  192. "type": invalid_type
  193. }
  194. response = requests.post(url, json=payload)
  195. assert response.status_code == 400
  196. error_data = response.json()
  197. assert error_data["code"] == "SCJG001"
  198. @pytest.mark.parametrize("invalid_id", ["", None])
  199. def test_scjg002_missing_param_id(self, api_config, invalid_id):
  200. """测试SCJG002: 缺少文档ID"""
  201. url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
  202. if invalid_id is None:
  203. payload = {
  204. "user": api_config["valid_user"],
  205. "type": "summary"
  206. }
  207. else:
  208. payload = {
  209. "id": invalid_id,
  210. "user": api_config["valid_user"],
  211. "type": "summary"
  212. }
  213. response = requests.post(url, json=payload)
  214. assert response.status_code == 400
  215. error_data = response.json()
  216. assert error_data["code"] == "SCJG002"
  217. @pytest.mark.parametrize("invalid_format", ["123", "short-id", "invalid-uuid-format"])
  218. def test_scjg003_invalid_id_format(self, api_config, invalid_format):
  219. """测试SCJG003: 文档ID格式错误"""
  220. url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
  221. payload = {
  222. "id": invalid_format,
  223. "user": api_config["valid_user"],
  224. "type": "summary"
  225. }
  226. response = requests.post(url, json=payload)
  227. assert response.status_code == 400
  228. error_data = response.json()
  229. assert error_data["code"] == "SCJG003"
  230. def test_scjg005_invalid_user_review_results(self, api_config, test_file):
  231. """测试SCJG005: 用户标识无效(审查结果接口)"""
  232. # 先上传文件获取有效的文件ID
  233. file_id = self._upload_file_and_get_file_id(api_config, test_file)
  234. url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
  235. payload = {
  236. "id": file_id,
  237. "user": "invalid_user",
  238. "type": "summary"
  239. }
  240. response = requests.post(url, json=payload)
  241. assert response.status_code == 403
  242. error_data = response.json()
  243. assert error_data["code"] == "SCJG005"
  244. def test_scjg006_task_not_found_review_results(self, api_config):
  245. """测试SCJG006: 任务不存在(审查结果接口)"""
  246. url = f"{api_config['base_url']}{api_config['api_prefix']}/review_results"
  247. payload = {
  248. "id": str(uuid.uuid4()),
  249. "user": api_config["valid_user"],
  250. "type": "summary"
  251. }
  252. response = requests.post(url, json=payload)
  253. assert response.status_code == 404
  254. error_data = response.json()
  255. assert error_data["code"] == "SCJG006"
  256. def _upload_file_and_get_file_id(self, api_config, test_file):
  257. """辅助方法:上传文件并获取文件ID"""
  258. url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
  259. files = {"file": test_file.get_file()}
  260. data = {
  261. "callback_url": api_config["test_callback_url"],
  262. "project_plan_type": api_config["valid_project_type"],
  263. "user": api_config["valid_user"]
  264. }
  265. response = requests.post(url, files=files, data=data)
  266. assert response.status_code == 200
  267. result = response.json()
  268. return result["data"]["id"]
  269. @pytest.mark.skip(reason="认证检查未实现")
  270. def test_scjg004_unauthorized(self, api_config):
  271. """测试SCJG004: 认证失败 - 跳过因为未实现"""
  272. pass
  273. class TestIntegration:
  274. """集成测试"""
  275. def test_complete_workflow_success(self, api_config, test_file):
  276. """测试完整工作流程成功场景"""
  277. # 1. 文件上传
  278. callback_task_id = self._upload_file_and_get_callback(api_config, test_file)
  279. assert callback_task_id is not None
  280. # 2. 进度查询
  281. url = f"{api_config['base_url']}{api_config['api_prefix']}/task_progress/{callback_task_id}"
  282. params = {"user": api_config["valid_user"]}
  283. response = requests.get(url, params=params)
  284. assert response.status_code == 200
  285. def _upload_file_and_get_callback(self, api_config, test_file):
  286. """辅助方法:上传文件并获取callback_task_id"""
  287. url = f"{api_config['base_url']}{api_config['api_prefix']}/file_upload"
  288. files = {"file": test_file.get_file()}
  289. data = {
  290. "callback_url": api_config["test_callback_url"],
  291. "project_plan_type": api_config["valid_project_type"],
  292. "user": api_config["valid_user"]
  293. }
  294. response = requests.post(url, files=files, data=data)
  295. assert response.status_code == 200
  296. result = response.json()
  297. return result["data"]["callback_task_id"]
  298. if __name__ == "__main__":
  299. # 如果直接运行此文件,给出提示
  300. print("请使用 pytest 运行此测试文件:")
  301. print("pytest test/construction_review/test_error_codes_pytest.py -v")
  302. print("或者运行所有测试:")
  303. print("pytest test/ -v")