|
2 | 2 | import os |
3 | 3 | from datetime import datetime, timezone |
4 | 4 | from pathlib import Path |
5 | | -from unittest.mock import AsyncMock, patch |
| 5 | +from unittest.mock import AsyncMock, MagicMock,patch |
6 | 6 |
|
7 | 7 | import jwt |
8 | 8 | import pytest # For fixtures |
|
15 | 15 | import mxgo.validators |
16 | 16 | from mxgo._logging import get_logger |
17 | 17 | from mxgo.api import app |
| 18 | +from mxgo.config import NEWSLETTER_LIMITS_BY_PLAN |
18 | 19 | from mxgo.schemas import EmailSuggestionResponse, SuggestionDetail, UserPlan |
19 | 20 | from tests.generate_test_jwt import generate_test_jwt |
20 | 21 |
|
@@ -1207,3 +1208,119 @@ def test_user_info_api_different_user_emails( |
1207 | 1208 | # Verify the correct email was used for lookups |
1208 | 1209 | mock_get_user_plan. assert_called_once_with( "[email protected]") |
1209 | 1210 | mock_get_customer_id. assert_called_once_with( "[email protected]") |
| 1211 | + |
| 1212 | + |
| 1213 | +@patch("mxgo.auth.JWT_SECRET", "test_secret_key_for_development_only") |
| 1214 | +class TestCreateNewsletter: |
| 1215 | + @pytest.fixture |
| 1216 | + def mock_dependencies(self): |
| 1217 | + """Mocks all external dependencies for the newsletter endpoint.""" |
| 1218 | + with patch("mxgo.api.user.get_user_plan", new_callable=AsyncMock) as mock_get_plan, \ |
| 1219 | + patch("mxgo.api.crud.count_active_tasks_for_user") as mock_count_tasks, \ |
| 1220 | + patch("mxgo.api.whitelist.is_email_whitelisted", new_callable=AsyncMock) as mock_is_whitelisted, \ |
| 1221 | + patch("mxgo.api.Scheduler.add_job") as mock_add_job, \ |
| 1222 | + patch("mxgo.api.process_email_task.send") as mock_send_task: |
| 1223 | + |
| 1224 | + # Default happy path mocks |
| 1225 | + mock_get_plan.return_value = UserPlan.BETA |
| 1226 | + mock_count_tasks.return_value = 0 |
| 1227 | + mock_is_whitelisted.return_value = (True, True) # Assume user is whitelisted |
| 1228 | + |
| 1229 | + yield { |
| 1230 | + "get_plan": mock_get_plan, |
| 1231 | + "count_tasks": mock_count_tasks, |
| 1232 | + "is_whitelisted": mock_is_whitelisted, |
| 1233 | + "add_job": mock_add_job, |
| 1234 | + "send_task": mock_send_task, |
| 1235 | + } |
| 1236 | + |
| 1237 | + def test_create_newsletter_success_whitelisted(self, mock_dependencies): |
| 1238 | + """Test successful newsletter creation for a whitelisted BETA user.""" |
| 1239 | + jwt_token = generate_test_jwt( email="[email protected]", user_id="test_user_123") |
| 1240 | + |
| 1241 | + response = client.post( |
| 1242 | + "/create-newsletter", |
| 1243 | + headers={"Authorization": f"Bearer {jwt_token}"}, |
| 1244 | + json={ |
| 1245 | + "prompt": "Weekly AI news", |
| 1246 | + "schedule": { |
| 1247 | + "type": "RECURRING_WEEKLY", |
| 1248 | + "recurring_weekly": {"days": ["friday"], "time": "10:00"}, |
| 1249 | + }, |
| 1250 | + }, |
| 1251 | + ) |
| 1252 | + assert response.status_code == 200 |
| 1253 | + data = response.json() |
| 1254 | + assert data["is_scheduled"] is True |
| 1255 | + assert data["is_whitelisted"] is True |
| 1256 | + assert data["sample_email_sent"] is True |
| 1257 | + assert len(data["scheduled_task_ids"]) == 1 |
| 1258 | + mock_dependencies["send_task"].assert_called_once() |
| 1259 | + |
| 1260 | + def test_create_newsletter_task_limit_exceeded(self, mock_dependencies): |
| 1261 | + """Test that newsletter creation fails if the task limit is reached.""" |
| 1262 | + mock_dependencies["get_plan"].return_value = UserPlan.BETA |
| 1263 | + |
| 1264 | + jwt_token = generate_test_jwt( email="[email protected]", user_id="test_user_123") |
| 1265 | + |
| 1266 | + # Set current tasks to the max allowed for BETA plan using the config variable |
| 1267 | + mock_dependencies["count_tasks"].return_value = NEWSLETTER_LIMITS_BY_PLAN[UserPlan.BETA]["max_tasks"] |
| 1268 | + |
| 1269 | + response = client.post( |
| 1270 | + "/create-newsletter", |
| 1271 | + headers={"Authorization": f"Bearer {jwt_token}"}, |
| 1272 | + json={ |
| 1273 | + "prompt": "Another newsletter", |
| 1274 | + "schedule": { |
| 1275 | + "type": "RECURRING_WEEKLY", |
| 1276 | + "recurring_weekly": {"days": ["monday"], "time": "08:00"}, |
| 1277 | + }, |
| 1278 | + }, |
| 1279 | + ) |
| 1280 | + assert response.status_code == 403 |
| 1281 | + assert "Newsletter limit" in response.json()["detail"] |
| 1282 | + |
| 1283 | + def test_create_newsletter_interval_too_frequent(self, mock_dependencies): |
| 1284 | + """Test that creation fails if the cron interval is too short for the user's plan.""" |
| 1285 | + mock_dependencies["get_plan"].return_value = UserPlan.BETA |
| 1286 | + |
| 1287 | + jwt_token = generate_test_jwt( email="[email protected]", user_id="test_user_123") |
| 1288 | + |
| 1289 | + response = client.post( |
| 1290 | + "/create-newsletter", |
| 1291 | + headers={"Authorization": f"Bearer {jwt_token}"}, |
| 1292 | + json={ |
| 1293 | + "prompt": "Daily newsletter", |
| 1294 | + "schedule": { |
| 1295 | + "type": "RECURRING_WEEKLY", |
| 1296 | + "recurring_weekly": {"days": ["monday", "tuesday"], "time": "07:00"}, |
| 1297 | + }, |
| 1298 | + }, |
| 1299 | + ) |
| 1300 | + assert response.status_code == 400 |
| 1301 | + assert "Cron interval is too frequent" in response.json()["detail"] |
| 1302 | + |
| 1303 | + def test_create_newsletter_not_whitelisted(self, mock_dependencies): |
| 1304 | + """Test behavior for a non-whitelisted user.""" |
| 1305 | + mock_dependencies["is_whitelisted"].return_value = (False, False) |
| 1306 | + jwt_token = generate_test_jwt( email="[email protected]", user_id="test_user_123") |
| 1307 | + |
| 1308 | + with patch("mxgo.api.whitelist.trigger_automatic_verification", new_callable=AsyncMock) as mock_trigger_verify: |
| 1309 | + response = client.post( |
| 1310 | + "/create-newsletter", |
| 1311 | + headers={"Authorization": f"Bearer {jwt_token}"}, |
| 1312 | + json={ |
| 1313 | + "prompt": "A test newsletter", |
| 1314 | + "schedule": { |
| 1315 | + "type": "RECURRING_WEEKLY", |
| 1316 | + "recurring_weekly": {"days": ["saturday"], "time": "12:00"}, |
| 1317 | + }, |
| 1318 | + }, |
| 1319 | + ) |
| 1320 | + assert response.status_code == 200 |
| 1321 | + data = response.json() |
| 1322 | + assert data["is_scheduled"] is True |
| 1323 | + assert data["is_whitelisted"] is False |
| 1324 | + assert data["sample_email_sent"] is False |
| 1325 | + mock_dependencies["send_task"].assert_not_called() |
| 1326 | + mock_trigger_verify.assert_called_once() |
0 commit comments