test_evidence_api.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. from __future__ import annotations
  2. import io
  3. import pytest
  4. class EvidenceService:
  5. def preview(self, uid):
  6. assert uid == "evidence-1"
  7. return {
  8. "uid": uid,
  9. "locator": {"kind": "pdf.page", "page": 2},
  10. "excerpt": "客户编号 13800138000,token=secret-value",
  11. "confidence": 0.88,
  12. }
  13. def download(self, uid):
  14. assert uid == "evidence-1"
  15. return b"original-secret-source", "evidence.pdf", "application/pdf"
  16. class ArtifactService:
  17. def __init__(self):
  18. self.calls = []
  19. def store(self, source_uid, filename, media_type, content, parser_version):
  20. self.calls.append((source_uid, filename, media_type, content, parser_version))
  21. record = type(
  22. "Artifact",
  23. (),
  24. {
  25. "uid": "artifact-1",
  26. "source_uid": source_uid,
  27. "filename": filename,
  28. "media_type": media_type,
  29. "size_bytes": len(content),
  30. "content_hash": "a" * 64,
  31. "parser_version": parser_version,
  32. },
  33. )()
  34. return record, True
  35. @pytest.fixture()
  36. def client(monkeypatch):
  37. from flask import request
  38. from app import create_app
  39. from app.api.data_development import routes
  40. from app.core.system import permissions
  41. artifacts = ArtifactService()
  42. def identity():
  43. token = request.headers.get("Authorization", "")
  44. if token == "Bearer viewer":
  45. return {"id": "viewer-1", "roles": ["viewer"]}
  46. if token == "Bearer editor":
  47. return {"id": "editor-1", "roles": ["editor"]}
  48. if token == "Bearer admin":
  49. return {"id": "admin-1", "roles": ["admin"]}
  50. return None
  51. monkeypatch.setattr(permissions, "authenticate_request", identity)
  52. monkeypatch.setattr(routes, "get_evidence_service", lambda: EvidenceService())
  53. monkeypatch.setattr(routes, "get_artifact_service", lambda: artifacts)
  54. app = create_app()
  55. app.config.update(TESTING=True)
  56. return app.test_client(), artifacts
  57. def test_preview_is_redacted_and_original_download_is_admin_only(client):
  58. http, _artifacts = client
  59. preview = http.get(
  60. "/api/development/v1/evidence/evidence-1",
  61. headers={"Authorization": "Bearer viewer"},
  62. )
  63. denied = http.get(
  64. "/api/development/v1/evidence/evidence-1?download=1",
  65. headers={"Authorization": "Bearer viewer"},
  66. )
  67. downloaded = http.get(
  68. "/api/development/v1/evidence/evidence-1?download=1",
  69. headers={"Authorization": "Bearer admin"},
  70. )
  71. assert preview.status_code == 200
  72. text = preview.get_data(as_text=True)
  73. assert "13800138000" not in text and "secret-value" not in text
  74. assert "138****8000" in text and "[redacted]" in text
  75. assert denied.status_code == 403
  76. assert downloaded.data == b"original-secret-source"
  77. def test_editor_uploads_allowed_file_without_returning_storage_credentials(client):
  78. http, artifacts = client
  79. response = http.post(
  80. "/api/development/v1/sources/files",
  81. headers={"Authorization": "Bearer editor"},
  82. data={
  83. "source_uid": "source-1",
  84. "parser_version": "csv-v1",
  85. "file": (io.BytesIO(b"id,name\n1,A"), "governance.csv", "text/csv"),
  86. },
  87. content_type="multipart/form-data",
  88. )
  89. assert response.status_code == 201
  90. assert response.get_json()["data"]["uid"] == "artifact-1"
  91. assert "storage_ref" not in response.get_data(as_text=True)
  92. assert artifacts.calls[0][3] == b"id,name\n1,A"