test_overlay_branding.py 3.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """Logo persistence, image validation, and overlay authentication."""
  2. import io
  3. import pytest
  4. from PIL import Image
  5. from backend.tests.overlay_helpers import mint_token, setup_admin
  6. pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
  7. def logo_bytes(fmt="PNG"):
  8. output = io.BytesIO()
  9. Image.new("RGBA", (80, 40), (255, 0, 0, 128)).save(output, format=fmt)
  10. return output.getvalue()
  11. async def test_logo_upload_read_remove(async_client, tmp_path, monkeypatch):
  12. from backend.app.core.config import settings
  13. monkeypatch.setattr(settings, "base_dir", tmp_path)
  14. response = await async_client.post(
  15. "/api/v1/settings/overlay-logo", files={"file": ("logo.webp", logo_bytes("WEBP"), "image/webp")}
  16. )
  17. assert response.status_code == 200
  18. image = await async_client.get("/api/v1/overlay-branding/logo")
  19. assert image.status_code == 200
  20. assert image.headers["content-type"] == "image/png"
  21. with Image.open(io.BytesIO(image.content)) as decoded:
  22. assert decoded.size == (80, 40)
  23. assert decoded.mode == "RGBA"
  24. assert (await async_client.delete("/api/v1/settings/overlay-logo")).status_code == 200
  25. assert (await async_client.get("/api/v1/overlay-branding/logo")).status_code == 404
  26. @pytest.mark.parametrize(
  27. ("content", "status"),
  28. [(b"<svg></svg>", 400), (b"invalid", 400), (b"x" * (2 * 1024 * 1024 + 1), 413)],
  29. ids=["svg", "invalid", "oversized"],
  30. )
  31. async def test_rejects_invalid_uploads(async_client, tmp_path, monkeypatch, content, status):
  32. from backend.app.core.config import settings
  33. monkeypatch.setattr(settings, "base_dir", tmp_path)
  34. response = await async_client.post(
  35. "/api/v1/settings/overlay-logo", files={"file": ("logo.png", content, "image/png")}
  36. )
  37. assert response.status_code == status
  38. assert not (tmp_path / "overlay-branding" / "logo.png").exists()
  39. async def test_logo_auth_and_token_scope(async_client, tmp_path, monkeypatch):
  40. from backend.app.core.config import settings
  41. monkeypatch.setattr(settings, "base_dir", tmp_path)
  42. jwt = await setup_admin(async_client, suffix="_branding")
  43. headers = {"Authorization": f"Bearer {jwt}"}
  44. response = await async_client.post(
  45. "/api/v1/settings/overlay-logo",
  46. headers=headers,
  47. files={"file": ("logo.png", logo_bytes(), "image/png")},
  48. )
  49. assert response.status_code == 200
  50. assert (await async_client.get("/api/v1/settings/overlay-logo", headers=headers)).status_code == 200
  51. assert (await async_client.get("/api/v1/settings/overlay-logo")).status_code == 401
  52. assert (await async_client.get("/api/v1/overlay-branding/logo")).status_code == 401
  53. for scope in ("camera_stream", "camwall", "overlay"):
  54. token = await mint_token(async_client, jwt, scope=scope, name=scope)
  55. response = await async_client.get("/api/v1/overlay-branding/logo", params={"token": token})
  56. assert response.status_code == (200 if scope == "overlay" else 401)
  57. assert (await async_client.delete("/api/v1/settings/overlay-logo", params={"token": token})).status_code == 401
  58. assert (await async_client.get("/api/v1/overlay-branding/logo?token=invalid")).status_code == 401
  59. async def test_invalid_replacement_preserves_logo(async_client, tmp_path, monkeypatch):
  60. from backend.app.core.config import settings
  61. monkeypatch.setattr(settings, "base_dir", tmp_path)
  62. await async_client.post("/api/v1/settings/overlay-logo", files={"file": ("logo.png", logo_bytes(), "image/png")})
  63. before = (await async_client.get("/api/v1/overlay-branding/logo")).content
  64. output = io.BytesIO()
  65. Image.new("RGB", (2100, 2100)).save(output, "PNG")
  66. response = await async_client.post(
  67. "/api/v1/settings/overlay-logo", files={"file": ("huge.png", output.getvalue(), "image/png")}
  68. )
  69. assert response.status_code == 400
  70. assert (await async_client.get("/api/v1/overlay-branding/logo")).content == before