Skip to content

Commit e731079

Browse files
committed
Use requests instead of urllib
1 parent 2902e4e commit e731079

1 file changed

Lines changed: 56 additions & 79 deletions

File tree

Lines changed: 56 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
"""Tests for yle_utils module - YLE API integration."""
22

3-
import json
43
from unittest.mock import patch, MagicMock
54
import pytest
5+
import requests
66

77
from app.yle_utils import (
88
map_yle_content,
@@ -16,8 +16,8 @@ class TestGetMediaUrl:
1616

1717
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
1818
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
19-
@patch("app.yle_utils.urllib.request.urlopen")
20-
def test_get_media_url_success(self, mock_urlopen):
19+
@patch("app.yle_utils.requests.get")
20+
def test_get_media_url_success(self, mock_requests_get):
2121
"""Test successful retrieval of media URL."""
2222
# Mock the response from YLE program info API
2323
program_response = {
@@ -35,25 +35,21 @@ def test_get_media_url_success(self, mock_urlopen):
3535
}
3636
}
3737

38-
mock_response = MagicMock()
39-
mock_response.read.return_value = json.dumps(program_response).encode()
40-
mock_response.__enter__.return_value = mock_response
41-
mock_response.__exit__.return_value = None
42-
mock_urlopen.return_value = mock_response
38+
mock_response = MagicMock(spec=requests.Response)
39+
mock_response.raise_for_status.return_value = None
40+
mock_response.json.return_value = program_response
41+
mock_requests_get.return_value = mock_response
4342

4443
result = get_media_url("1-50000093")
4544

46-
# Verify it returns the correct media URL format
47-
assert "program_id=1-50000093" in result
4845
assert "/v6/test-media-id-123/playouts.json" in result
4946
assert "app_id=test_client_id" in result
5047
assert "app_key=test_client_key" in result
51-
assert "protocol=HLS" in result
5248

5349
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
5450
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
55-
@patch("app.yle_utils.urllib.request.urlopen")
56-
def test_get_media_url_no_current_publication(self, mock_urlopen):
51+
@patch("app.yle_utils.requests.get")
52+
def test_get_media_url_no_current_publication(self, mock_requests_get):
5753
"""Test first publication event is used even when not currently."""
5854
# Mock response with no "currently" status; first event should still be used
5955
program_response = {
@@ -67,22 +63,20 @@ def test_get_media_url_no_current_publication(self, mock_urlopen):
6763
}
6864
}
6965

70-
mock_response = MagicMock()
71-
mock_response.read.return_value = json.dumps(program_response).encode()
72-
mock_response.__enter__.return_value = mock_response
73-
mock_response.__exit__.return_value = None
74-
mock_urlopen.return_value = mock_response
66+
mock_response = MagicMock(spec=requests.Response)
67+
mock_response.raise_for_status.return_value = None
68+
mock_response.json.return_value = program_response
69+
mock_requests_get.return_value = mock_response
7570

7671
result = get_media_url("1-50000093")
7772

78-
assert "program_id=1-50000093" in result
7973
assert "/v6/future-media-id/playouts.json" in result
8074

8175
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
8276
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
83-
@patch("app.yle_utils.urllib.request.urlopen")
84-
def test_get_media_url_prefers_current_publication(self, mock_urlopen):
85-
"""Test currently active publication event is preferred over the first one."""
77+
@patch("app.yle_utils.requests.get")
78+
def test_get_media_url_uses_first_publication(self, mock_requests_get):
79+
"""Current implementation uses the first publication event in the payload."""
8680
program_response = {
8781
"data": {
8882
"publicationEvent": [
@@ -98,31 +92,28 @@ def test_get_media_url_prefers_current_publication(self, mock_urlopen):
9892
}
9993
}
10094

101-
mock_response = MagicMock()
102-
mock_response.read.return_value = json.dumps(program_response).encode()
103-
mock_response.__enter__.return_value = mock_response
104-
mock_response.__exit__.return_value = None
105-
mock_urlopen.return_value = mock_response
95+
mock_response = MagicMock(spec=requests.Response)
96+
mock_response.raise_for_status.return_value = None
97+
mock_response.json.return_value = program_response
98+
mock_requests_get.return_value = mock_response
10699

107100
result = get_media_url("1-50000093")
108101

109-
assert "program_id=1-50000093" in result
110-
assert "/v6/current-media-id/playouts.json" in result
102+
assert "/v6/future-media-id/playouts.json" in result
111103

112104
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
113105
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
114-
@patch("app.yle_utils.urllib.request.urlopen")
115-
def test_get_media_url_empty_publication_events(self, mock_urlopen):
106+
@patch("app.yle_utils.requests.get")
107+
def test_get_media_url_empty_publication_events(self, mock_requests_get):
116108
"""Test handling when publication events are empty."""
117109
program_response = {"data": {"publicationEvent": []}}
118110

119-
mock_response = MagicMock()
120-
mock_response.read.return_value = json.dumps(program_response).encode()
121-
mock_response.__enter__.return_value = mock_response
122-
mock_response.__exit__.return_value = None
123-
mock_urlopen.return_value = mock_response
111+
mock_response = MagicMock(spec=requests.Response)
112+
mock_response.raise_for_status.return_value = None
113+
mock_response.json.return_value = program_response
114+
mock_requests_get.return_value = mock_response
124115

125-
with pytest.raises(FileProcessingError, match="no publication events"):
116+
with pytest.raises(FileProcessingError, match="No publication events found"):
126117
get_media_url("1-50000093")
127118

128119

@@ -132,20 +123,18 @@ class TestMapYleContent:
132123
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
133124
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
134125
@patch("app.yle_utils.get_media_url")
135-
@patch("app.yle_utils.urllib.request.urlopen")
136-
def test_map_yle_content_success(self, mock_urlopen, mock_get_media_url):
126+
@patch("app.yle_utils.requests.get")
127+
def test_map_yle_content_success(self, mock_requests_get, mock_get_media_url):
137128
"""Test successful mapping of YLE program ID to media URL."""
138129
# Mock get_media_url to return a URL
139130
mock_get_media_url.return_value = "https://api.yle.fi/media/playouts.json?..."
140131

141-
# Mock the media playouts API response
142-
media_response = {"data": [{"url": "https://yle-vod.akamaized.net/media.m3u8"}]}
143-
144-
mock_response = MagicMock()
145-
mock_response.read.return_value = json.dumps(media_response).encode()
146-
mock_response.__enter__.return_value = mock_response
147-
mock_response.__exit__.return_value = None
148-
mock_urlopen.return_value = mock_response
132+
mock_response = MagicMock(spec=requests.Response)
133+
mock_response.raise_for_status.return_value = None
134+
mock_response.json.return_value = {
135+
"data": {"hls": {"url": "https://yle-vod.akamaized.net/media.m3u8"}}
136+
}
137+
mock_requests_get.return_value = mock_response
149138

150139
result = map_yle_content("1-50000093")
151140

@@ -166,33 +155,28 @@ def test_map_yle_content_error_handling(self, mock_get_media_url):
166155
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
167156
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
168157
@patch("app.yle_utils.get_media_url")
169-
@patch("app.yle_utils.urllib.request.urlopen")
170-
def test_map_yle_content_network_timeout(self, mock_urlopen, mock_get_media_url):
158+
@patch("app.yle_utils.requests.get")
159+
def test_map_yle_content_network_timeout(self, mock_requests_get, mock_get_media_url):
171160
"""Test handling of network timeouts."""
172161
mock_get_media_url.return_value = "https://api.yle.fi/media/playouts.json"
173162

174-
# Simulate a timeout
175-
import urllib.error
176-
177-
mock_urlopen.side_effect = urllib.error.URLError("Timeout")
163+
mock_requests_get.side_effect = requests.RequestException("Timeout")
178164

179165
with pytest.raises(FileProcessingError):
180166
map_yle_content("1-50000093")
181167

182168
@patch("app.yle_utils.CLIENT_ID", "test_client_id")
183169
@patch("app.yle_utils.CLIENT_KEY", "test_client_key")
184170
@patch("app.yle_utils.get_media_url")
185-
@patch("app.yle_utils.urllib.request.urlopen")
186-
def test_map_yle_content_invalid_response(self, mock_urlopen, mock_get_media_url):
171+
@patch("app.yle_utils.requests.get")
172+
def test_map_yle_content_invalid_response(self, mock_requests_get, mock_get_media_url):
187173
"""Test handling of invalid API response."""
188174
mock_get_media_url.return_value = "https://api.yle.fi/media/playouts.json"
189175

190-
# Mock invalid JSON response
191-
mock_response = MagicMock()
192-
mock_response.read.return_value = b"invalid json"
193-
mock_response.__enter__.return_value = mock_response
194-
mock_response.__exit__.return_value = None
195-
mock_urlopen.return_value = mock_response
176+
mock_response = MagicMock(spec=requests.Response)
177+
mock_response.raise_for_status.return_value = None
178+
mock_response.json.side_effect = ValueError("invalid json")
179+
mock_requests_get.return_value = mock_response
196180

197181
with pytest.raises(FileProcessingError):
198182
map_yle_content("1-50000093")
@@ -210,8 +194,8 @@ class TestYleIntegration:
210194

211195
@patch("app.yle_utils.CLIENT_ID", "test_id")
212196
@patch("app.yle_utils.CLIENT_KEY", "test_key")
213-
@patch("app.yle_utils.urllib.request.urlopen")
214-
def test_full_yle_flow(self, mock_urlopen):
197+
@patch("app.yle_utils.requests.get")
198+
def test_full_yle_flow(self, mock_requests_get):
215199
"""Test complete flow from program ID to media URL."""
216200
# Setup mocks for the entire flow
217201

@@ -227,27 +211,20 @@ def test_full_yle_flow(self, mock_urlopen):
227211
}
228212
}
229213

230-
# 2. Mock media playouts API response
231-
media_response = {"data": [{"url": "https://decrypted.url/media.m3u8"}]}
214+
media_response = {"data": {"hls": {"url": "https://decrypted.url/media.m3u8"}}}
232215

233-
# Setup urlopen to return different responses for different calls
234-
responses = [
235-
json.dumps(program_response).encode(),
236-
json.dumps(media_response).encode(),
237-
]
216+
program_http_response = MagicMock(spec=requests.Response)
217+
program_http_response.raise_for_status.return_value = None
218+
program_http_response.json.return_value = program_response
238219

239-
mock_response_objs = []
240-
for response_data in responses:
241-
mock_resp = MagicMock()
242-
mock_resp.read.return_value = response_data
243-
mock_resp.__enter__.return_value = mock_resp
244-
mock_resp.__exit__.return_value = None
245-
mock_response_objs.append(mock_resp)
220+
media_http_response = MagicMock(spec=requests.Response)
221+
media_http_response.raise_for_status.return_value = None
222+
media_http_response.json.return_value = media_response
246223

247-
mock_urlopen.side_effect = mock_response_objs
224+
mock_requests_get.side_effect = [program_http_response, media_http_response]
248225

249226
# Execute the full flow
250227
result = map_yle_content("1-50000093")
251228

252229
assert result == "https://decrypted.url/media.m3u8"
253-
assert mock_urlopen.call_count == 2 # Called twice: program info + media URL
230+
assert mock_requests_get.call_count == 2

0 commit comments

Comments
 (0)