slide-deck-ai / tests /unit /test_image_search.py
barunsaha's picture
Fix image search test case when API key is missing
ee7e58b
"""
Tests for the image search module.
"""
from io import BytesIO
from typing import Any, Dict
import pytest
from slidedeckai.helpers import image_search
class _MockResponse:
"""A tiny response-like object to simulate `requests` responses."""
def __init__(
self,
*,
content: bytes = b'',
json_data: Any = None,
status_ok: bool = True
) -> None:
self.content = content
self._json = json_data
self._status_ok = status_ok
def raise_for_status(self) -> None:
"""Raise an exception when status is not OK."""
if not self._status_ok:
raise RuntimeError('status not ok')
def json(self) -> Any:
"""Return preconfigured JSON data."""
return self._json
def _dummy_requests_get_success_search(
url: str,
headers: Dict[str, str],
params: Dict[str, Any],
timeout: int
):
"""Return a successful mock response for search_pexels."""
# Validate that the function under test passes expected args
assert 'Authorization' in headers
assert 'User-Agent' in headers
assert 'query' in params
photos = [
{
'url': 'https://pexels.com/photo/1',
'src': {'large': 'https://images/1_large.jpg'}
},
{
'url': 'https://pexels.com/photo/2',
'src': {'original': 'https://images/2_original.jpg'}
},
{
'url': 'https://pexels.com/photo/3',
'src': {'large': 'https://images/3_large.jpg'}
}
]
return _MockResponse(json_data={'photos': photos})
def _dummy_requests_get_image(
url: str,
headers: Dict[str, str],
stream: bool, timeout: int
):
"""Return a mock image response for get_image_from_url."""
assert stream is True
assert 'Authorization' in headers
data = b'\x89PNG\r\n\x1a\n...'
return _MockResponse(content=data)
def test_extract_dimensions_with_params() -> None:
"""Extract_dimensions extracts width and height from URL query params."""
url = 'https://images.example.com/photo.jpg?w=800&h=600'
width, height = image_search.extract_dimensions(url)
assert isinstance(width, int)
assert isinstance(height, int)
assert (width, height) == (800, 600)
def test_extract_dimensions_missing_params() -> None:
"""When dimensions are missing the function returns (0, 0)."""
url = 'https://images.example.com/photo.jpg'
assert image_search.extract_dimensions(url) == (0, 0)
def test_get_photo_url_from_api_response_none() -> None:
"""Returns (None, None) when there are no photos in the response."""
result = image_search.get_photo_url_from_api_response({'not_photos': []})
assert result == (None, None)
def test_get_photo_url_from_api_response_selects_large_and_original(monkeypatch) -> None:
"""Ensure the function picks the expected photo and returns correct URLs.
This test patches random.choice to deterministically pick indices that exercise
the 'large' and 'original' branches.
"""
photos = [
{'url': 'https://pexels.com/photo/1', 'src': {'large': 'https://images/1_large.jpg'}},
{'url': 'https://pexels.com/photo/2', 'src': {'original': 'https://images/2_original.jpg'}},
{'url': 'https://pexels.com/photo/3', 'src': {'large': 'https://images/3_large.jpg'}},
]
# Ensure the Pexels API key is present so the helper will attempt to select
# and return photo URLs rather than early-returning (None, None).
monkeypatch.setenv('PEXEL_API_KEY', 'akey')
# Force selection of index 1 (second photo) which only has 'original'
monkeypatch.setattr(image_search.random, 'choice', lambda seq: 1)
photo_url, page_url = image_search.get_photo_url_from_api_response({'photos': photos})
assert page_url == 'https://pexels.com/photo/2'
assert photo_url == 'https://images/2_original.jpg'
# Force selection of index 0 which has 'large'
monkeypatch.setattr(image_search.random, 'choice', lambda seq: 0)
photo_url, page_url = image_search.get_photo_url_from_api_response({'photos': photos})
assert page_url == 'https://pexels.com/photo/1'
assert photo_url == 'https://images/1_large.jpg'
def test_get_image_from_url_success(monkeypatch) -> None:
"""get_image_from_url returns a BytesIO object with image content."""
monkeypatch.setattr(
'slidedeckai.helpers.image_search.requests.get',
lambda *a, **k: _dummy_requests_get_image(*a, **k)
)
monkeypatch.setenv('PEXEL_API_KEY', 'dummykey')
img = image_search.get_image_from_url('https://images/1_large.jpg')
assert isinstance(img, BytesIO)
data = img.getvalue()
assert data.startswith(b'\x89PNG')
def test_search_pexels_success(monkeypatch) -> None:
"""search_pexels forwards the request and returns parsed JSON."""
monkeypatch.setattr(
'slidedeckai.helpers.image_search.requests.get',
lambda *a, **k: _dummy_requests_get_success_search(*a, **k)
)
monkeypatch.setenv('PEXEL_API_KEY', 'akey')
result = image_search.search_pexels(query='people', size='medium', per_page=3)
assert isinstance(result, dict)
assert 'photos' in result
assert len(result['photos']) == 3
def test_search_pexels_raises_on_request_error(monkeypatch) -> None:
"""When requests.get raises an exception, it should propagate from search_pexels."""
def _raise(*a, **k):
raise RuntimeError('network')
monkeypatch.setattr('slidedeckai.helpers.image_search.requests.get', _raise)
monkeypatch.setenv('PEXEL_API_KEY', 'akey')
with pytest.raises(RuntimeError):
image_search.search_pexels(query='x')
def test_search_pexels_returns_empty_when_no_api_key(monkeypatch) -> None:
"""When PEXEL_API_KEY is not set, search_pexels should return an empty dict."""
monkeypatch.delenv('PEXEL_API_KEY', raising=False)
result = image_search.search_pexels(query='people')
assert result == {}
def test_get_photo_url_from_api_response_returns_none_when_no_api_key(monkeypatch) -> None:
"""When PEXEL_API_KEY is not set, get_photo_url_from_api_response should return (None, None)."""
photos = [
{'url': 'https://pexels.com/photo/1', 'src': {'large': 'https://images/1_large.jpg'}}
]
monkeypatch.delenv('PEXEL_API_KEY', raising=False)
result = image_search.get_photo_url_from_api_response({'photos': photos})
assert result == (None, None)