test_upload.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. """Public attachment upload (local storage)."""
  2. import pytest
  3. from app.core.config import get_settings
  4. PNG = b"\x89PNG\r\n\x1a\n" + b"\x00" * 8
  5. PDF = b"%PDF-1.4" + b"\x00" * 16
  6. @pytest.fixture
  7. def local_storage_env(tmp_path, monkeypatch):
  8. monkeypatch.setenv("STORAGE_TYPE", "local")
  9. monkeypatch.setenv("LOCAL_STORAGE_DIR", str(tmp_path))
  10. get_settings.cache_clear()
  11. yield tmp_path
  12. get_settings.cache_clear()
  13. def test_public_upload(client, local_storage_env):
  14. files = {"file": ("a.png", PNG, "image/png")}
  15. r = client.post(
  16. "/api/public/attachment/upload",
  17. files=files,
  18. data={"folder": "feedback"},
  19. )
  20. assert r.status_code == 200
  21. data = r.json()["data"]
  22. assert data["url"]
  23. assert data["id"]
  24. assert data["fileName"] == "a.png"
  25. assert data["size"] == len(PNG)
  26. assert data["url"].startswith("/uploads/feedback/")
  27. def test_public_upload_pdf(client, local_storage_env):
  28. files = {"file": ("report.pdf", PDF, "application/pdf")}
  29. r = client.post(
  30. "/api/public/attachment/upload",
  31. files=files,
  32. data={"folder": "suggestion"},
  33. )
  34. assert r.status_code == 200
  35. data = r.json()["data"]
  36. assert data["fileName"] == "report.pdf"
  37. assert data["url"].endswith(".pdf") or ".pdf" in data["url"]
  38. def test_public_upload_rejects_disallowed_extension(client, local_storage_env):
  39. files = {"file": ("malware.exe", b"MZ", "application/octet-stream")}
  40. r = client.post(
  41. "/api/public/attachment/upload",
  42. files=files,
  43. data={"folder": "feedback"},
  44. )
  45. assert r.status_code == 400
  46. body = r.json()
  47. assert body["code"] == 400
  48. assert "类型" in body["message"] or "格式" in body["message"] or "不支持" in body["message"]
  49. def test_public_upload_allows_large_file(client, local_storage_env):
  50. # 原 20MB 限制已取消;略超原阈值仍应成功
  51. big = b"%PDF-1.4" + b"x" * (20 * 1024 * 1024)
  52. files = {"file": ("big.pdf", big, "application/pdf")}
  53. r = client.post(
  54. "/api/public/attachment/upload",
  55. files=files,
  56. data={"folder": "feedback"},
  57. )
  58. assert r.status_code == 200
  59. data = r.json()["data"]
  60. assert data["fileName"] == "big.pdf"
  61. assert data["size"] == len(big)
  62. def test_public_upload_rejects_path_traversal(client, local_storage_env):
  63. base = local_storage_env
  64. outside = (base / ".." / "outside").resolve()
  65. assert not outside.exists()
  66. files = {"file": ("evil.png", PNG, "image/png")}
  67. r = client.post(
  68. "/api/public/attachment/upload",
  69. files=files,
  70. data={"folder": "../outside"},
  71. )
  72. assert r.status_code == 400
  73. body = r.json()
  74. assert body["code"] == 400
  75. assert "message" in body
  76. assert "detail" not in body
  77. assert not outside.exists()
  78. assert not any(base.rglob("*"))
  79. def test_public_upload_rejects_qrcode_folder(client, local_storage_env):
  80. files = {"file": ("a.png", PNG, "image/png")}
  81. r = client.post(
  82. "/api/public/attachment/upload",
  83. files=files,
  84. data={"folder": "qrcode"},
  85. )
  86. assert r.status_code == 400
  87. assert r.json()["code"] == 400
  88. def test_public_upload_allows_feedback_folder(client, local_storage_env):
  89. files = {"file": ("a.png", PNG, "image/png")}
  90. r = client.post(
  91. "/api/public/attachment/upload",
  92. files=files,
  93. data={"folder": "feedback"},
  94. )
  95. assert r.status_code == 200
  96. assert "/feedback/" in r.json()["data"]["url"]