123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- from types import SimpleNamespace
- from typing import Any, Iterator, TypeAlias
- from unittest import mock
- import numpy as np
- import pytest
- from fastapi.testclient import TestClient
- from PIL import Image
- from .main import app, init_state
- ndarray: TypeAlias = np.ndarray[int, np.dtype[np.float32]]
- @pytest.fixture
- def pil_image() -> Image.Image:
- return Image.new("RGB", (600, 800))
- @pytest.fixture
- def cv_image(pil_image: Image.Image) -> ndarray:
- return np.asarray(pil_image)[:, :, ::-1] # PIL uses RGB while cv2 uses BGR
- @pytest.fixture
- def mock_classifier_pipeline() -> Iterator[mock.Mock]:
- with mock.patch("app.models.image_classification.pipeline") as model:
- classifier_preds = [
- {"label": "that's an image alright", "score": 0.8},
- {"label": "well it ends with .jpg", "score": 0.1},
- {"label": "idk, im just seeing bytes", "score": 0.05},
- {"label": "not sure", "score": 0.04},
- {"label": "probably a virus", "score": 0.01},
- ]
- def forward(
- inputs: Image.Image | list[Image.Image], **kwargs: Any
- ) -> list[dict[str, Any]] | list[list[dict[str, Any]]]:
- if isinstance(inputs, list) and not all([isinstance(img, Image.Image) for img in inputs]):
- raise TypeError
- elif not isinstance(inputs, Image.Image):
- raise TypeError
- if isinstance(inputs, list):
- return [classifier_preds] * len(inputs)
- return classifier_preds
- model.return_value = forward
- yield model
- @pytest.fixture
- def mock_st() -> Iterator[mock.Mock]:
- with mock.patch("app.models.clip.SentenceTransformer") as model:
- embedding = np.random.rand(512).astype(np.float32)
- def encode(inputs: Image.Image | list[Image.Image], **kwargs: Any) -> ndarray | list[ndarray]:
- # mypy complains unless isinstance(inputs, list) is used explicitly
- img_batch = isinstance(inputs, list) and all([isinstance(inst, Image.Image) for inst in inputs])
- text_batch = isinstance(inputs, list) and all([isinstance(inst, str) for inst in inputs])
- if isinstance(inputs, list) and not any([img_batch, text_batch]):
- raise TypeError
- if isinstance(inputs, list):
- return np.stack([embedding] * len(inputs))
- return embedding
- mocked = mock.Mock()
- mocked.encode = encode
- model.return_value = mocked
- yield model
- @pytest.fixture
- def mock_faceanalysis() -> Iterator[mock.Mock]:
- with mock.patch("app.models.facial_recognition.FaceAnalysis") as model:
- face_preds = [
- SimpleNamespace( # this is so these fields can be accessed through dot notation
- **{
- "bbox": np.random.rand(4).astype(np.float32),
- "kps": np.random.rand(5, 2).astype(np.float32),
- "det_score": np.array([0.67]).astype(np.float32),
- "normed_embedding": np.random.rand(512).astype(np.float32),
- }
- ),
- SimpleNamespace(
- **{
- "bbox": np.random.rand(4).astype(np.float32),
- "kps": np.random.rand(5, 2).astype(np.float32),
- "det_score": np.array([0.4]).astype(np.float32),
- "normed_embedding": np.random.rand(512).astype(np.float32),
- }
- ),
- ]
- def get(image: np.ndarray[int, np.dtype[np.float32]], **kwargs: Any) -> list[SimpleNamespace]:
- if not isinstance(image, np.ndarray):
- raise TypeError
- return face_preds
- mocked = mock.Mock()
- mocked.get = get
- model.return_value = mocked
- yield model
- @pytest.fixture
- def mock_get_model() -> Iterator[mock.Mock]:
- with mock.patch("app.models.cache.InferenceModel.from_model_type", autospec=True) as mocked:
- yield mocked
- @pytest.fixture(scope="session")
- def deployed_app() -> TestClient:
- init_state()
- return TestClient(app)
|