| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- """B6借阅授权审批单(PDF凭证)上传/下载/清除的接口契约测试(契约1.19 12.3-12.5)。"""
- from __future__ import annotations
- import io
- VOUCHER_PDF = b"%PDF-1.4\nB6 voucher original\n%%EOF"
- REPLACED_PDF = b"%PDF-1.4\nB6 voucher replaced\n%%EOF"
- def _headers(token: str) -> dict[str, str]:
- return {"Authorization": f"Bearer {token}"}
- def _doc_id(app, name: str) -> str:
- from dms.extensions import db
- from dms.models import Document
- with app.app_context():
- document_id = db.session.scalar(
- db.select(Document.id).where(Document.document_name == name)
- )
- assert document_id is not None
- return str(document_id)
- def _entry_id(b6_client, login_token, document_id) -> str:
- response = b6_client.get(
- f"/api/v1/documents/{document_id}/permissions",
- headers=_headers(login_token),
- )
- assert response.status_code == 200
- entries = response.get_json()["data"]["entries"]
- assert entries
- return entries[0]["id"]
- def _upload(b6_client, login_token, document_id, entry_id, content, name):
- return b6_client.post(
- f"/api/v1/documents/{document_id}/permissions/entries/{entry_id}"
- "/voucher",
- data={"file": (io.BytesIO(content), name)},
- headers=_headers(login_token),
- content_type="multipart/form-data",
- )
- def test_voucher_upload_download_replace_delete_and_audit_flow(
- b6_app, b6_client, login_token
- ):
- from dms.extensions import db
- from dms.models import AuditLog
- main_id = _doc_id(b6_app, "B4_TEST_自定义主案")
- entry_id = _entry_id(b6_client, login_token, main_id)
- # 未上传凭证:下载404,详情凭证字段为空
- missing = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher",
- headers=_headers(login_token),
- )
- assert missing.status_code == 404
- assert missing.get_json()["code"] == "RESOURCE_NOT_FOUND"
- uploaded = _upload(
- b6_client, login_token, main_id, entry_id, VOUCHER_PDF, "借阅审批单.pdf"
- )
- assert uploaded.status_code == 200, uploaded.get_json()
- data = uploaded.get_json()["data"]
- assert data["documentId"] == main_id
- assert data["entryId"] == entry_id
- assert data["voucherFileName"] == "借阅审批单.pdf"
- assert data["voucherFileSize"] == len(VOUCHER_PDF)
- assert data["voucherUploadedAt"]
- detail = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions",
- headers=_headers(login_token),
- ).get_json()["data"]["entries"]
- assert [item["id"] for item in detail] == [entry_id]
- assert detail[0]["voucherFileName"] == "借阅审批单.pdf"
- assert detail[0]["voucherFileSize"] == len(VOUCHER_PDF)
- assert detail[0]["voucherUploadedAt"] == data["voucherUploadedAt"]
- downloaded = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher",
- headers=_headers(login_token),
- )
- assert downloaded.status_code == 200
- assert downloaded.data == VOUCHER_PDF
- assert downloaded.mimetype == "application/pdf"
- assert "attachment" in downloaded.headers["Content-Disposition"]
- # 重复上传为替换语义:凭证字段覆盖,下载返回新内容
- replaced = _upload(
- b6_client, login_token, main_id, entry_id, REPLACED_PDF, "替换审批单.pdf"
- )
- assert replaced.status_code == 200, replaced.get_json()
- assert replaced.get_json()["data"]["voucherFileName"] == "替换审批单.pdf"
- again = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher",
- headers=_headers(login_token),
- )
- assert again.data == REPLACED_PDF
- removed = b6_client.delete(
- f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher",
- headers=_headers(login_token),
- )
- assert removed.status_code == 200
- assert removed.get_json()["data"] == {
- "documentId": main_id,
- "entryId": entry_id,
- }
- cleared = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher",
- headers=_headers(login_token),
- )
- assert cleared.status_code == 404
- entries = b6_client.get(
- f"/api/v1/documents/{main_id}/permissions",
- headers=_headers(login_token),
- ).get_json()["data"]["entries"]
- assert entries[0]["voucherFileName"] is None
- assert entries[0]["voucherFileSize"] is None
- assert entries[0]["voucherUploadedAt"] is None
- with b6_app.app_context():
- actions = db.session.scalars(
- db.select(AuditLog.action_type)
- .where(AuditLog.target_id == int(main_id))
- .order_by(AuditLog.id.asc())
- ).all()
- assert "UPLOAD_PERMISSION_VOUCHER" in actions
- assert "DELETE_PERMISSION_VOUCHER" in actions
- def test_voucher_upload_rejects_non_pdf(b6_app, b6_client, login_token):
- main_id = _doc_id(b6_app, "B4_TEST_自定义主案")
- entry_id = _entry_id(b6_client, login_token, main_id)
- wrong_type = _upload(
- b6_client, login_token, main_id, entry_id, b"plain text", "凭证.txt"
- )
- assert wrong_type.status_code == 415
- assert wrong_type.get_json()["code"] == "UNSUPPORTED_FILE_TYPE"
- no_extension = _upload(
- b6_client, login_token, main_id, entry_id, VOUCHER_PDF, "凭证"
- )
- assert no_extension.status_code == 415
- assert no_extension.get_json()["code"] == "UNSUPPORTED_FILE_TYPE"
- def test_voucher_requires_config_permission(
- b6_app, b6_client, login_token, token_for
- ):
- main_id = _doc_id(b6_app, "B4_TEST_自定义主案")
- entry_id = _entry_id(b6_client, login_token, main_id)
- user_headers = _headers(token_for("user"))
- base = f"/api/v1/documents/{main_id}/permissions/entries/{entry_id}/voucher"
- assert (
- b6_client.post(
- base,
- data={"file": (io.BytesIO(VOUCHER_PDF), "审批单.pdf")},
- headers=user_headers,
- content_type="multipart/form-data",
- ).status_code
- == 403
- )
- assert b6_client.get(base, headers=user_headers).status_code == 403
- assert b6_client.delete(base, headers=user_headers).status_code == 403
- def test_voucher_unknown_entry_and_inherited_sources(
- b6_app, b6_client, login_token
- ):
- main_id = _doc_id(b6_app, "B4_TEST_自定义主案")
- sub_id = _doc_id(b6_app, "B4_TEST_动态继承子案")
- attachment_id = _doc_id(b6_app, "B4_TEST_规范附件")
- unknown = b6_client.post(
- f"/api/v1/documents/{main_id}/permissions/entries/99999999/voucher",
- data={"file": (io.BytesIO(VOUCHER_PDF), "审批单.pdf")},
- headers=_headers(login_token),
- content_type="multipart/form-data",
- )
- assert unknown.status_code == 404
- assert unknown.get_json()["code"] == "RESOURCE_NOT_FOUND"
- sub = b6_client.get(
- f"/api/v1/documents/{sub_id}/permissions/entries/1/voucher",
- headers=_headers(login_token),
- )
- assert sub.status_code == 409
- assert sub.get_json()["code"] == "SUB_PLAN_PERMISSION_INHERITED"
- attachment = b6_client.get(
- f"/api/v1/documents/{attachment_id}/permissions/entries/1/voucher",
- headers=_headers(login_token),
- )
- assert attachment.status_code == 400
- assert attachment.get_json()["code"] == "ATTACHMENT_HAS_NO_ACL"
|