import pytest from fastapi.testclient import TestClient from unittest.mock import patch, MagicMock from app.main import app from app.models.wardrobe import WardrobeItem from app.models.user import User from app.core.security import get_password_hash from app.core.database import get_db from app.workers.tasks import analyze_wardrobe_item import io client = TestClient(app) @pytest.fixture(autouse=True) def override_db(db_session): # Override get_db dependency in FastAPI app app.dependency_overrides[get_db] = lambda: db_session yield app.dependency_overrides.clear() @pytest.fixture def test_user(db_session): user = User(email="test_wardrobe@example.com", password_hash=get_password_hash("password123")) db_session.add(user) db_session.commit() db_session.refresh(user) from app.api.deps import get_current_user app.dependency_overrides[get_current_user] = lambda: user yield user app.dependency_overrides.clear() def test_upload_wardrobe_item_success(db_session, test_user): # Construct a dummy JPEG file with valid Magic Bytes signature dummy_file = io.BytesIO(b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xd9") with patch("app.api.v1.wardrobe.storage_service.upload_file", return_value="http://localhost:8002/static/dummy.jpg") as mock_upload: with patch("app.api.v1.wardrobe.analyze_wardrobe_item.delay") as mock_delay: response = client.post( "/api/v1/wardrobe/upload", files={"file": ("test.jpg", dummy_file, "image/jpeg")} ) assert response.status_code == 201 data = response.json() assert data["category"] == "DETECTING" assert data["image_url"] == "http://localhost:8002/static/dummy.jpg" mock_upload.assert_called_once() mock_delay.assert_called_once_with(data["id"]) def test_upload_file_too_large(db_session, test_user): # Make a dummy file that exceeds 8MB huge_file = io.BytesIO(b"\xff\xd8\xff" + b"\x00" * (8 * 1024 * 1024 + 1)) response = client.post( "/api/v1/wardrobe/upload", files={"file": ("test.jpg", huge_file, "image/jpeg")} ) assert response.status_code == 413 assert "vượt quá" in response.json()["detail"] def test_upload_invalid_extension(db_session, test_user): dummy_file = io.BytesIO(b"\xff\xd8\xff\xe0\x00\x10JFIF\x00\x01\x01\x01\x00H\x00H\x00\x00\xff\xd9") response = client.post( "/api/v1/wardrobe/upload", files={"file": ("test.txt", dummy_file, "text/plain")} ) assert response.status_code == 400 assert "Định dạng tệp" in response.json()["detail"] def test_upload_invalid_signature(db_session, test_user): invalid_file = io.BytesIO(b"NotAJpegRealSignatureText") response = client.post( "/api/v1/wardrobe/upload", files={"file": ("test.jpg", invalid_file, "image/jpeg")} ) assert response.status_code == 400 assert "Nội dung tệp tin" in response.json()["detail"] def test_get_wardrobe_items(db_session, test_user): item1 = WardrobeItem(user_id=test_user.id, category="shirt", image_url="url1") item2 = WardrobeItem(user_id=test_user.id, category="pants", image_url="url2") db_session.add(item1) db_session.add(item2) db_session.commit() response = client.get("/api/v1/wardrobe/items") assert response.status_code == 200 data = response.json() assert len(data) == 2 assert {d["category"] for d in data} == {"shirt", "pants"} def test_delete_wardrobe_item(db_session, test_user): item = WardrobeItem(user_id=test_user.id, category="shirt", image_url="http://localhost:8002/static/users/id/raw/test.jpg") db_session.add(item) db_session.commit() db_session.refresh(item) with patch("app.api.v1.wardrobe.storage_service.delete_file", return_value=True) as mock_delete: response = client.delete(f"/api/v1/wardrobe/items/{item.id}") assert response.status_code == 200 assert response.json()["status"] == "success" mock_delete.assert_called_once()