|
@@ -313,7 +313,9 @@ class FinalReviewApiTests(unittest.TestCase):
|
|
|
"txbId": 1,
|
|
"txbId": 1,
|
|
|
"TENDER_FILE": str(tender),
|
|
"TENDER_FILE": str(tender),
|
|
|
"PROCUREMENT_FILE": str(procurement),
|
|
"PROCUREMENT_FILE": str(procurement),
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": True,
|
|
|
"REFERENCE_BID": str(reference),
|
|
"REFERENCE_BID": str(reference),
|
|
|
|
|
+ "REFERENCE_IS_ABSOLUTE": True,
|
|
|
"CLA_FILE": "",
|
|
"CLA_FILE": "",
|
|
|
}
|
|
}
|
|
|
with patch.object(http_api, "API_WORK_ROOT", root / "jobs"), patch.object(
|
|
with patch.object(http_api, "API_WORK_ROOT", root / "jobs"), patch.object(
|
|
@@ -354,7 +356,8 @@ class FinalReviewApiTests(unittest.TestCase):
|
|
|
"txbId": 1,
|
|
"txbId": 1,
|
|
|
"TENDER_FILE": str(tender),
|
|
"TENDER_FILE": str(tender),
|
|
|
"PROCUREMENT_FILE": str(procurement),
|
|
"PROCUREMENT_FILE": str(procurement),
|
|
|
- "REFERENCE_BID": "参考投书.docx",
|
|
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": True,
|
|
|
|
|
+ "REFERENCE_BID": "/参考投书.docx",
|
|
|
"REFERENCE_IS_ABSOLUTE": False,
|
|
"REFERENCE_IS_ABSOLUTE": False,
|
|
|
"CLA_FILE": "",
|
|
"CLA_FILE": "",
|
|
|
}
|
|
}
|
|
@@ -375,6 +378,97 @@ class FinalReviewApiTests(unittest.TestCase):
|
|
|
worker.assert_not_called()
|
|
worker.assert_not_called()
|
|
|
shutil.rmtree(root / "jobs", ignore_errors=True)
|
|
shutil.rmtree(root / "jobs", ignore_errors=True)
|
|
|
|
|
|
|
|
|
|
+ def test_json_relative_tpc_files_join_tpc_dir(self):
|
|
|
|
|
+ with tempfile.TemporaryDirectory() as temp_dir:
|
|
|
|
|
+ root = Path(temp_dir)
|
|
|
|
|
+ tpc_dir = root / "tpc_root"
|
|
|
|
|
+ tpc_dir.mkdir()
|
|
|
|
|
+ tender = tpc_dir / "招标文件.pdf"
|
|
|
|
|
+ procurement = tpc_dir / "采购需求.docx"
|
|
|
|
|
+ clarification = tpc_dir / "澄清公告.docx"
|
|
|
|
|
+ reference = root / "参考投书.docx"
|
|
|
|
|
+ tender.write_bytes(_pdf_bytes())
|
|
|
|
|
+ procurement.write_bytes(_docx_bytes("procurement"))
|
|
|
|
|
+ clarification.write_bytes(_docx_bytes("clarification"))
|
|
|
|
|
+ reference.write_bytes(_docx_bytes("reference"))
|
|
|
|
|
+ executor = RecordingExecutor()
|
|
|
|
|
+ payload = {
|
|
|
|
|
+ "CALLBACK_URL": "http://127.0.0.1:9999/callback",
|
|
|
|
|
+ "txbId": 1,
|
|
|
|
|
+ "TENDER_FILE": f"/{tender.name}",
|
|
|
|
|
+ "PROCUREMENT_FILE": f"/{procurement.name}",
|
|
|
|
|
+ "REFERENCE_BID": str(reference),
|
|
|
|
|
+ "REFERENCE_IS_ABSOLUTE": True,
|
|
|
|
|
+ "CLA_FILE": f"/{clarification.name}",
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": False,
|
|
|
|
|
+ }
|
|
|
|
|
+ with patch.object(http_api, "TPC_DIR", str(tpc_dir)), patch.object(
|
|
|
|
|
+ http_api, "API_WORK_ROOT", root / "jobs"
|
|
|
|
|
+ ), patch.object(http_api, "_JOB_EXECUTOR", executor), patch.object(
|
|
|
|
|
+ http_api, "_run_pipeline_worker"
|
|
|
|
|
+ ) as worker:
|
|
|
|
|
+ response = TestClient(http_api.app).post(
|
|
|
|
|
+ "/api/v1/final-review",
|
|
|
|
|
+ json=payload,
|
|
|
|
|
+ )
|
|
|
|
|
+ function, args = executor.submitted[0]
|
|
|
|
|
+ job = args[0]
|
|
|
|
|
+
|
|
|
|
|
+ self.assertEqual(response.status_code, 202)
|
|
|
|
|
+ self.assertEqual(job["files"]["TENDER_FILE"]["filename"], tender.name)
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ job["files"]["PROCUREMENT_FILE"]["filename"], procurement.name
|
|
|
|
|
+ )
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ job["files"]["CLA_FILE"]["filename"], clarification.name
|
|
|
|
|
+ )
|
|
|
|
|
+ worker.assert_not_called()
|
|
|
|
|
+ shutil.rmtree(root / "jobs", ignore_errors=True)
|
|
|
|
|
+
|
|
|
|
|
+ def test_invalid_tpc_is_absolute_value_is_rejected(self):
|
|
|
|
|
+ with patch.object(http_api, "_JOB_EXECUTOR") as executor:
|
|
|
|
|
+ response = TestClient(http_api.app).post(
|
|
|
|
|
+ "/api/v1/final-review",
|
|
|
|
|
+ json={
|
|
|
|
|
+ "CALLBACK_URL": "http://127.0.0.1:9999/callback",
|
|
|
|
|
+ "txbId": "bad-tpc",
|
|
|
|
|
+ "TENDER_FILE": "tender.pdf",
|
|
|
|
|
+ "PROCUREMENT_FILE": "procurement.docx",
|
|
|
|
|
+ "REFERENCE_BID": "reference.docx",
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": "false",
|
|
|
|
|
+ "REFERENCE_IS_ABSOLUTE": True,
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ self.assertEqual(response.status_code, 400)
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ response.json()["detail"],
|
|
|
|
|
+ "TPC_IS_ABSOLUTE 必须是布尔值 true/false",
|
|
|
|
|
+ )
|
|
|
|
|
+ executor.submit.assert_not_called()
|
|
|
|
|
+
|
|
|
|
|
+ def test_relative_tpc_files_require_tpc_dir(self):
|
|
|
|
|
+ with patch.object(http_api, "TPC_DIR", ""), patch.object(
|
|
|
|
|
+ http_api, "_JOB_EXECUTOR"
|
|
|
|
|
+ ) as executor:
|
|
|
|
|
+ response = TestClient(http_api.app).post(
|
|
|
|
|
+ "/api/v1/final-review",
|
|
|
|
|
+ json={
|
|
|
|
|
+ "CALLBACK_URL": "http://127.0.0.1:9999/callback",
|
|
|
|
|
+ "txbId": "missing-tpc-dir",
|
|
|
|
|
+ "TENDER_FILE": "tender.pdf",
|
|
|
|
|
+ "PROCUREMENT_FILE": "procurement.docx",
|
|
|
|
|
+ "REFERENCE_BID": "reference.docx",
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": False,
|
|
|
|
|
+ "REFERENCE_IS_ABSOLUTE": True,
|
|
|
|
|
+ },
|
|
|
|
|
+ )
|
|
|
|
|
+ self.assertEqual(response.status_code, 400)
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ response.json()["detail"],
|
|
|
|
|
+ "TENDER_FILE 为相对路径,但未配置 TPC_DIR",
|
|
|
|
|
+ )
|
|
|
|
|
+ executor.submit.assert_not_called()
|
|
|
|
|
+
|
|
|
def test_invalid_reference_is_absolute_value_is_rejected(self):
|
|
def test_invalid_reference_is_absolute_value_is_rejected(self):
|
|
|
with patch.object(http_api, "_JOB_EXECUTOR") as executor:
|
|
with patch.object(http_api, "_JOB_EXECUTOR") as executor:
|
|
|
response = TestClient(http_api.app).post(
|
|
response = TestClient(http_api.app).post(
|
|
@@ -384,6 +478,7 @@ class FinalReviewApiTests(unittest.TestCase):
|
|
|
"txbId": "bad-ref",
|
|
"txbId": "bad-ref",
|
|
|
"TENDER_FILE": "tender.pdf",
|
|
"TENDER_FILE": "tender.pdf",
|
|
|
"PROCUREMENT_FILE": "procurement.docx",
|
|
"PROCUREMENT_FILE": "procurement.docx",
|
|
|
|
|
+ "TPC_IS_ABSOLUTE": True,
|
|
|
"REFERENCE_BID": "reference.docx",
|
|
"REFERENCE_BID": "reference.docx",
|
|
|
"REFERENCE_IS_ABSOLUTE": "maybe",
|
|
"REFERENCE_IS_ABSOLUTE": "maybe",
|
|
|
},
|
|
},
|
|
@@ -391,6 +486,34 @@ class FinalReviewApiTests(unittest.TestCase):
|
|
|
self.assertEqual(response.status_code, 400)
|
|
self.assertEqual(response.status_code, 400)
|
|
|
executor.submit.assert_not_called()
|
|
executor.submit.assert_not_called()
|
|
|
|
|
|
|
|
|
|
+ def test_missing_absolute_flags_are_rejected(self):
|
|
|
|
|
+ base_payload = {
|
|
|
|
|
+ "CALLBACK_URL": "http://127.0.0.1:9999/callback",
|
|
|
|
|
+ "txbId": "missing-flags",
|
|
|
|
|
+ "TENDER_FILE": "tender.pdf",
|
|
|
|
|
+ "PROCUREMENT_FILE": "procurement.docx",
|
|
|
|
|
+ "REFERENCE_BID": "reference.docx",
|
|
|
|
|
+ }
|
|
|
|
|
+ with patch.object(http_api, "_JOB_EXECUTOR") as executor:
|
|
|
|
|
+ missing_tpc = TestClient(http_api.app).post(
|
|
|
|
|
+ "/api/v1/final-review", json=base_payload
|
|
|
|
|
+ )
|
|
|
|
|
+ missing_reference = TestClient(http_api.app).post(
|
|
|
|
|
+ "/api/v1/final-review",
|
|
|
|
|
+ json={**base_payload, "TPC_IS_ABSOLUTE": True},
|
|
|
|
|
+ )
|
|
|
|
|
+ self.assertEqual(missing_tpc.status_code, 400)
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ missing_tpc.json()["detail"],
|
|
|
|
|
+ "TPC_IS_ABSOLUTE 必须是布尔值 true/false",
|
|
|
|
|
+ )
|
|
|
|
|
+ self.assertEqual(missing_reference.status_code, 400)
|
|
|
|
|
+ self.assertEqual(
|
|
|
|
|
+ missing_reference.json()["detail"],
|
|
|
|
|
+ "REFERENCE_IS_ABSOLUTE 必须是布尔值 true/false",
|
|
|
|
|
+ )
|
|
|
|
|
+ executor.submit.assert_not_called()
|
|
|
|
|
+
|
|
|
def test_multipart_accepts_reference_is_absolute_without_path_join(self):
|
|
def test_multipart_accepts_reference_is_absolute_without_path_join(self):
|
|
|
with tempfile.TemporaryDirectory() as temp_dir, patch.object(
|
|
with tempfile.TemporaryDirectory() as temp_dir, patch.object(
|
|
|
http_api, "API_WORK_ROOT", Path(temp_dir)
|
|
http_api, "API_WORK_ROOT", Path(temp_dir)
|