File size: 6,524 Bytes
690eb5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee7e58b
 
 
 
690eb5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27e940d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
"""
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)