"""Tests for upload endpoints.""" import pytest from unittest.mock import MagicMock, patch AUTH = {"Authorization": "Bearer test-token"} class TestUploadAuth: def test_logo_upload_requires_auth(self, client): resp = client.post( "/api/v1/upload/logo", files={"file": ("logo.png", b"fake-image-data", "image/png")}, ) assert resp.status_code == 401 class TestUploadLogoValidation: def test_rejects_unsupported_file_type(self, client): with patch("app.routers.upload.get_supabase"): resp = client.post( "/api/v1/upload/logo", files={"file": ("doc.pdf", b"PDF content", "application/pdf")}, headers=AUTH, ) assert resp.status_code == 400 assert "Invalid file type" in resp.json()["detail"] def test_rejects_file_over_2mb(self, client): big_bytes = b"x" * (2 * 1024 * 1024 + 1) with patch("app.routers.upload.get_supabase"): resp = client.post( "/api/v1/upload/logo", files={"file": ("big.png", big_bytes, "image/png")}, headers=AUTH, ) assert resp.status_code == 413 @pytest.mark.parametrize("mime,filename", [ ("image/png", "logo.png"), ("image/jpeg", "photo.jpg"), ("image/gif", "anim.gif"), ("image/svg+xml", "icon.svg"), ("image/webp", "img.webp"), ]) def test_accepts_all_allowed_image_types(self, client, mime, filename): fake_url = "https://cdn.example.com/logos/test.png" sb = MagicMock() sb.storage.from_.return_value.upload.return_value = MagicMock() sb.storage.from_.return_value.get_public_url.return_value = fake_url sb.auth = MagicMock() with patch("app.routers.upload.get_supabase", return_value=sb): resp = client.post( "/api/v1/upload/logo", files={"file": (filename, b"image-bytes", mime)}, headers=AUTH, ) assert resp.status_code == 200 assert resp.json()["url"] == fake_url class TestUploadLogoSuccess: def test_returns_public_url(self, client): public_url = "https://storage.example.com/logos/test-user-id/abc123.png" sb = MagicMock() sb.storage.from_.return_value.upload.return_value = MagicMock() sb.storage.from_.return_value.get_public_url.return_value = public_url sb.auth = MagicMock() with patch("app.routers.upload.get_supabase", return_value=sb): resp = client.post( "/api/v1/upload/logo", files={"file": ("logo.png", b"fake-png-data", "image/png")}, headers=AUTH, ) assert resp.status_code == 200 body = resp.json() assert "url" in body assert body["url"] == public_url def test_upload_path_uses_user_id(self, client): """Storage path should be scoped to the authenticated user's ID.""" sb = MagicMock() upload_mock = sb.storage.from_.return_value.upload sb.storage.from_.return_value.get_public_url.return_value = "https://url" sb.auth = MagicMock() # Track the actual user returned by auth auth_user = MagicMock() auth_user.id = "test-user-id" sb.auth.get_user.return_value = MagicMock(user=auth_user) # Also mock user_profiles check profile_mock = MagicMock() profile_mock.select.return_value = profile_mock profile_mock.eq.return_value = profile_mock profile_mock.execute.return_value = MagicMock(data=[]) sb.table.return_value = profile_mock with patch("app.routers.upload.get_supabase", return_value=sb), \ patch("app.dependencies.get_supabase", return_value=sb): client.post( "/api/v1/upload/logo", files={"file": ("logo.png", b"data", "image/png")}, headers=AUTH, ) upload_mock.assert_called_once() call_kwargs = upload_mock.call_args path = call_kwargs[1].get("path") or call_kwargs[0][0] # Path should start with the user's ID assert path.startswith("test-user-id") def test_storage_failure_returns_500(self, client): sb = MagicMock() sb.storage.from_.return_value.upload.side_effect = Exception("Storage error") sb.auth = MagicMock() with patch("app.routers.upload.get_supabase", return_value=sb): resp = client.post( "/api/v1/upload/logo", files={"file": ("logo.png", b"data", "image/png")}, headers=AUTH, ) assert resp.status_code == 500