11import json
22import urllib .parse
33
4+ from unittest .mock import MagicMock , patch
5+
46import pytest
7+ import requests
58import requests_mock
69
10+ from django .core .cache import cache
711from django .urls import reverse
812
913from pontoon .base .models import (
@@ -62,6 +66,62 @@ def test_view_microsoft_translator_bad_locale(member, ms_locale, ms_api_key):
6266 assert response .status_code == 401
6367
6468
69+ @pytest .mark .django_db
70+ def test_view_microsoft_translator_missing_api_key (member , ms_locale , settings ):
71+ settings .MICROSOFT_TRANSLATOR_API_KEY = ""
72+ cache .clear ()
73+ url = reverse ("pontoon.microsoft_translator" )
74+ response = member .client .get (
75+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
76+ )
77+ assert response .status_code == 400
78+
79+
80+ @pytest .mark .django_db
81+ def test_view_microsoft_translator_api_http_error (member , ms_locale , ms_api_key ):
82+ cache .clear ()
83+ url = reverse ("pontoon.microsoft_translator" )
84+ with requests_mock .mock () as m :
85+ m .post (
86+ "https://api.cognitive.microsofttranslator.com/translate" ,
87+ status_code = 401 ,
88+ )
89+ response = member .client .get (
90+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
91+ )
92+ assert response .status_code == 401
93+
94+
95+ @pytest .mark .django_db
96+ def test_view_microsoft_translator_api_connection_error (member , ms_locale , ms_api_key ):
97+ cache .clear ()
98+ url = reverse ("pontoon.microsoft_translator" )
99+ with requests_mock .mock () as m :
100+ m .post (
101+ "https://api.cognitive.microsofttranslator.com/translate" ,
102+ exc = requests .exceptions .ConnectionError ,
103+ )
104+ response = member .client .get (
105+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
106+ )
107+ assert response .status_code == 500
108+
109+
110+ @pytest .mark .django_db
111+ def test_view_microsoft_translator_api_error_in_response (member , ms_locale , ms_api_key ):
112+ cache .clear ()
113+ url = reverse ("pontoon.microsoft_translator" )
114+ with requests_mock .mock () as m :
115+ m .post (
116+ "https://api.cognitive.microsofttranslator.com/translate" ,
117+ json = {"error" : {"code" : 400000 , "message" : "Bad request" }},
118+ )
119+ response = member .client .get (
120+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
121+ )
122+ assert response .status_code == 400
123+
124+
65125@pytest .mark .django_db
66126def test_view_google_translate_not_logged_in (
67127 client , google_translate_locale , google_translate_api_key
@@ -90,7 +150,6 @@ def test_view_google_translate(
90150
91151 assert response .status_code == 200
92152 assert json .loads (response .content ) == {
93- "status" : True ,
94153 "translation" : "target" ,
95154 }
96155
@@ -117,6 +176,146 @@ def test_view_google_translate_bad_locale(
117176 assert response .status_code == 400
118177
119178
179+ @pytest .mark .django_db
180+ def test_view_google_translate_missing_api_key (
181+ member , google_translate_locale , settings
182+ ):
183+ settings .GOOGLE_TRANSLATE_API_KEY = ""
184+ cache .clear ()
185+ url = reverse ("pontoon.google_translate" )
186+ response = member .client .get (
187+ url , {"text" : "text" , "locale" : google_translate_locale .code }
188+ )
189+ assert response .status_code == 400
190+
191+
192+ @pytest .mark .django_db
193+ def test_view_google_translate_api_http_error (
194+ member , google_translate_locale , google_translate_api_key
195+ ):
196+ cache .clear ()
197+ url = reverse ("pontoon.google_translate" )
198+ with requests_mock .mock () as m :
199+ m .post (
200+ "https://translation.googleapis.com/language/translate/v2" ,
201+ status_code = 403 ,
202+ )
203+ response = member .client .get (
204+ url , {"text" : "text" , "locale" : google_translate_locale .code }
205+ )
206+ assert response .status_code == 403
207+
208+
209+ @pytest .mark .django_db
210+ def test_view_google_translate_api_connection_error (
211+ member , google_translate_locale , google_translate_api_key
212+ ):
213+ cache .clear ()
214+ url = reverse ("pontoon.google_translate" )
215+ with requests_mock .mock () as m :
216+ m .post (
217+ "https://translation.googleapis.com/language/translate/v2" ,
218+ exc = requests .exceptions .ConnectionError ,
219+ )
220+ response = member .client .get (
221+ url , {"text" : "text" , "locale" : google_translate_locale .code }
222+ )
223+ assert response .status_code == 500
224+
225+
226+ @pytest .mark .django_db
227+ def test_view_google_translate_api_unexpected_response (
228+ member , google_translate_locale , google_translate_api_key
229+ ):
230+ cache .clear ()
231+ url = reverse ("pontoon.google_translate" )
232+ with requests_mock .mock () as m :
233+ m .post (
234+ "https://translation.googleapis.com/language/translate/v2" ,
235+ json = {"unexpected" : "response" },
236+ )
237+ response = member .client .get (
238+ url , {"text" : "text" , "locale" : google_translate_locale .code }
239+ )
240+ assert response .status_code == 400
241+
242+
243+ @pytest .mark .django_db
244+ def test_view_microsoft_translator_cache (member , ms_locale , ms_api_key ):
245+ url = reverse ("pontoon.microsoft_translator" )
246+ cache .clear ()
247+
248+ with requests_mock .mock () as m :
249+ data = [{"translations" : [{"text" : "target" }]}]
250+ m .post ("https://api.cognitive.microsofttranslator.com/translate" , json = data )
251+
252+ response1 = member .client .get (
253+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
254+ )
255+ assert len (m .request_history ) == 1
256+
257+ # Second identical request should be served from cache
258+ response2 = member .client .get (
259+ url , {"text" : "text" , "locale" : ms_locale .ms_translator_code }
260+ )
261+ assert len (m .request_history ) == 1
262+
263+ assert json .loads (response1 .content ) == json .loads (response2 .content )
264+
265+
266+ @pytest .mark .django_db
267+ def test_view_google_translate_cache (
268+ member , google_translate_locale , google_translate_api_key
269+ ):
270+ url = reverse ("pontoon.google_translate" )
271+ cache .clear ()
272+
273+ with requests_mock .mock () as m :
274+ data = {"data" : {"translations" : [{"translatedText" : "target" }]}}
275+ m .post ("https://translation.googleapis.com/language/translate/v2" , json = data )
276+
277+ response1 = member .client .get (
278+ url , {"text" : "text" , "locale" : google_translate_locale .code }
279+ )
280+ assert len (m .request_history ) == 1
281+
282+ # Second identical request should be served from cache
283+ response2 = member .client .get (
284+ url , {"text" : "text" , "locale" : google_translate_locale .code }
285+ )
286+ assert len (m .request_history ) == 1
287+
288+ assert json .loads (response1 .content ) == json .loads (response2 .content )
289+
290+
291+ @pytest .mark .django_db
292+ def test_view_gpt_transform_cache (member , locale_a , openai_api_key ):
293+ url = reverse ("pontoon.gpt_transform" )
294+ cache .clear ()
295+
296+ mock_response = MagicMock ()
297+ mock_response .choices [0 ].message .content = " formal translation "
298+
299+ with patch ("pontoon.machinery.openai_service.OpenAI" ) as MockOpenAI :
300+ MockOpenAI .return_value .chat .completions .create .return_value = mock_response
301+
302+ params = {
303+ "english_text" : "Hello" ,
304+ "translated_text" : "Hola" ,
305+ "characteristic" : "formal" ,
306+ "locale" : locale_a .name ,
307+ }
308+
309+ response1 = member .client .get (url , params )
310+ assert MockOpenAI .return_value .chat .completions .create .call_count == 1
311+
312+ # Second identical request should be served from cache
313+ response2 = member .client .get (url , params )
314+ assert MockOpenAI .return_value .chat .completions .create .call_count == 1
315+
316+ assert json .loads (response1 .content ) == json .loads (response2 .content )
317+
318+
120319@pytest .mark .django_db
121320def test_view_caighdean (client , entity_a ):
122321 gd = Locale .objects .get (code = "gd" )
0 commit comments