import pytest from dirty_equals import IsStr from starlette.testclient import TestClient from email_campaigns.campaigns.models import EmailCampaign from email_campaigns.emails.models import ( EmailAnalyticsBenchmarkByLinktV3Dbt, EmailAnalyticsV3Dbt, ShopifyEmailOrdersDbt, ShopifyEmailProductsDbt, ShopifyProductTypesDbt, ) from email_campaigns.reporting.models import EmailReportingReport from tests.unit.equals import IsISODatetime from tests.unit.types import CreateModel @pytest.mark.db def test_list_email_reporting_reports( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report", ) create_model( EmailReportingReport, identity_id="other-identity", name="Other Report", ) response = client.get("/email-reporting/reports") assert response.status_code == 200 assert response.json() == { "total": 1, "items": [ { "id": report.id, "name": "My Report", "emailsCount": 0, "createdBy": identity_id, "createdAt": IsISODatetime(report.created_at), "updatedAt": IsISODatetime(report.updated_at), } ], "limit": 50, "offset": 0, } @pytest.mark.db def test_get_email_reporting_report( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report", ) response = client.get(f"/email-reporting/reports/{report.id}") assert response.status_code == 200 assert response.json() == { "id": report.id, "name": "My Report", "emailIds": [], "emailsCount": 0, "createdBy": identity_id, "createdAt": IsISODatetime(report.created_at), "updatedAt": IsISODatetime(report.updated_at), } @pytest.mark.db def test_get_email_reporting_report_not_found( client: TestClient, ) -> None: response = client.get("/email-reporting/reports/non-existent-id") assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } @pytest.mark.db def test_create_email_reporting_report( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign", vendor_id=1) response = client.post( "/email-reporting/reports", json={"name": "My Report", "emailIds": [campaign.id]}, ) assert response.status_code == 201 assert response.json() == { "id": IsStr(), "name": "My Report", "emailIds": [campaign.id], "emailsCount": 1, "createdBy": identity_id, "createdAt": IsStr(), "updatedAt": IsStr(), } @pytest.mark.db def test_update_email_reporting_report( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign", vendor_id=1) report = create_model( EmailReportingReport, identity_id=identity_id, name="Old Name", ) response = client.patch( f"/email-reporting/reports/{report.id}", json={"name": "New Name", "emailIds": [campaign.id]}, ) assert response.status_code == 200 assert response.json() == { "id": report.id, "name": "New Name", "emailIds": [campaign.id], "emailsCount": 1, "createdBy": identity_id, "createdAt": IsISODatetime(report.created_at), "updatedAt": IsStr(), } @pytest.mark.db def test_update_email_reporting_report_not_found( client: TestClient, ) -> None: response = client.patch( "/email-reporting/reports/non-existent-id", json={"name": "New Name", "emailIds": []}, ) assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } @pytest.mark.db def test_delete_email_reporting_report( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: report = create_model( EmailReportingReport, identity_id=identity_id, name="Test Report", ) response = client.delete(f"/email-reporting/reports/{report.id}") assert response.status_code == 204 @pytest.mark.db def test_delete_email_reporting_report_not_found( client: TestClient, ) -> None: response = client.delete("/email-reporting/reports/non-existent-id") assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } # Analytics endpoints @pytest.mark.db def test_get_email_reporting_report_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( EmailAnalyticsV3Dbt, email_id=campaign.id, email_type=campaign.email_type, sends=10, drops=0, delivered=10, opens=6, clicks=3, unique_opens=6, unique_clicks=3, unique_bounces=0, unique_spam_complaints=1, unsubscribes=0, open_rate=0.6, delivered_rate=1.0, click_through_rate=0.3, click_to_open_rate=0.5, bounce_rate=0.0, spam_complaints_rate=0.1, unsubscribe_rate=0.0, unique_opens_dropoff=0.6, unique_clicks_dropoff=0.5, ) response = client.get(f"/email-reporting/reports/{report.id}/analytics") assert response.status_code == 200 assert response.json() == { "data": { "sends": 10, "delivered": 10, "opens": 6, "clicks": 3, "bounces": 0, "spamComplaints": 1, "unsubscribes": 0, "openRate": 0.6, "deliveredRate": 1.0, "clickThroughRate": 0.3, "clickToOpenRate": 0.5, "bounceRate": 0.0, "spamComplaintsRate": 0.1, "unsubscribeRate": 0.0, } } @pytest.mark.db def test_get_email_reporting_report_analytics_not_found( client: TestClient, ) -> None: response = client.get("/email-reporting/reports/non-existent-id/analytics") assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } @pytest.mark.db def test_get_email_reporting_report_link_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( EmailAnalyticsBenchmarkByLinktV3Dbt, email_id=campaign.id, email_type=campaign.email_type, url="http://www.awesome-artist.com", total_clicks=100, unique_clicks=50, percentage_of_total_clicks=1.0, ) create_model( EmailAnalyticsBenchmarkByLinktV3Dbt, email_id=campaign.id, email_type=campaign.email_type, url="http://facebook.com/tickets", total_clicks=150, unique_clicks=100, percentage_of_total_clicks=1.0, ) response = client.get(f"/email-reporting/reports/{report.id}/analytics/links") assert response.status_code == 200 assert response.json() == { "data": [ { "url": "http://facebook.com/tickets", "totalClicks": 150, "percentageOfTotalClicks": 0.6, }, { "url": "http://www.awesome-artist.com", "totalClicks": 100, "percentageOfTotalClicks": 0.4, }, ] } @pytest.mark.db def test_get_email_reporting_report_link_analytics_not_found( client: TestClient, ) -> None: response = client.get("/email-reporting/reports/non-existent-id/analytics/links") assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } @pytest.mark.db def test_get_email_reporting_report_sales_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( ShopifyEmailOrdersDbt, email_id=campaign.id, shop_id="shop1", currency="USD", country_code="US", shop_name="Shop One", total_orders=5, items_sold=10, total_sales_usd=120.0, total_discounts_usd=10.0, total_taxes_usd=8.0, total_delivery_usd=5.0, total_net_sales_usd=100.0, total_refunds_usd=10.0, first_time_buyer_rate=0.4, ) response = client.get(f"/email-reporting/reports/{report.id}/analytics/sales") assert response.status_code == 200 assert response.json() == { "data": { "ordersCount": 5, "itemsSold": 10, "netSalesUsd": 100.0, "grossSalesUsd": 120.0, "discountsUsd": 10.0, "taxesUsd": 8.0, "shippingUsd": 5.0, "refundsUsd": 10.0, "averageUnitsPerOrder": 2.0, "averageOrderAmount": 20.0, "firstTimeBuyerRate": 0.4, } } @pytest.mark.db def test_get_email_reporting_report_sales_analytics_not_found( client: TestClient, ) -> None: response = client.get("/email-reporting/reports/non-existent-id/analytics/sales") assert response.status_code == 404 assert response.json() == { "code": "email_reporting_report_not_found", "message": "Email reporting report not found", } @pytest.mark.db def test_get_email_reporting_report_sales_products_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( ShopifyEmailProductsDbt, email_id=campaign.id, product_title="Vinyl Record", product_type="Music", currency="USD", total_quantity_sold=5, total_net_sales_usd=200.0, ) create_model(ShopifyProductTypesDbt, product_type="Music", chart_eligible=True) response = client.get( f"/email-reporting/reports/{report.id}/analytics/sales/products" ) assert response.status_code == 200 assert response.json() == { "data": [ { "productTitle": "Vinyl Record", "productType": "Music", "itemsSold": 5, "netSalesUsd": 200.0, "chartEligible": True, } ] } @pytest.mark.db def test_get_email_reporting_report_sales_products_analytics_not_found( client: TestClient, ) -> None: response = client.get( "/email-reporting/reports/non-existent-id/analytics/sales/products" ) assert response.status_code == 404 @pytest.mark.db def test_get_email_reporting_report_sales_locations_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( ShopifyEmailOrdersDbt, email_id=campaign.id, shop_id="shop1", currency="USD", country_code="US", shop_name="Shop One", total_orders=5, items_sold=10, total_sales_usd=220.0, total_discounts_usd=10.0, total_taxes_usd=8.0, total_delivery_usd=5.0, total_net_sales_usd=200.0, ) response = client.get( f"/email-reporting/reports/{report.id}/analytics/sales/locations" ) assert response.status_code == 200 assert response.json() == { "data": [ { "countryCode": "US", "itemsSold": 10, "netSalesUsd": 200.0, "salesRate": pytest.approx(1.0), } ] } @pytest.mark.db def test_get_email_reporting_report_sales_locations_analytics_not_found( client: TestClient, ) -> None: response = client.get( "/email-reporting/reports/non-existent-id/analytics/sales/locations" ) assert response.status_code == 404 @pytest.mark.db def test_get_email_reporting_report_sales_shops_analytics( client: TestClient, create_model: CreateModel, identity_id: str, ) -> None: campaign = create_model(EmailCampaign, name="Test Campaign") report = create_model( EmailReportingReport, identity_id=identity_id, name="My Report" ) report.email_ids = [campaign.id] create_model( ShopifyEmailOrdersDbt, email_id=campaign.id, shop_id="shop1", currency="USD", country_code="US", shop_name="Big Shop", total_orders=10, items_sold=20, total_sales_usd=320.0, total_discounts_usd=10.0, total_taxes_usd=8.0, total_delivery_usd=5.0, total_net_sales_usd=300.0, ) response = client.get(f"/email-reporting/reports/{report.id}/analytics/sales/shops") assert response.status_code == 200 assert response.json() == { "data": [{"shopName": "Big Shop", "itemsSold": 20, "netSalesUsd": 300.0}] } @pytest.mark.db def test_get_email_reporting_report_sales_shops_analytics_not_found( client: TestClient, ) -> None: response = client.get( "/email-reporting/reports/non-existent-id/analytics/sales/shops" ) assert response.status_code == 404