11"""Tests for yle_utils module - YLE API integration."""
22
3- import json
43from unittest .mock import patch , MagicMock
54import pytest
5+ import requests
66
77from 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