import pytest from src.app import _find_new_stores, handler def _make_row( shopify_shop=None, shopify_store_url=None, public_domain=None, alternate_domain=None, selling_country=None, rep_owner=None, merch_company=None, active_=True, ) -> dict: return { "shopify_shop": shopify_shop, "shopify_store_url": shopify_store_url, "public_domain": public_domain, "alternate_domain": alternate_domain, "selling_country": selling_country, "rep_owner": rep_owner, "merch_company": merch_company, "active_": active_, } @pytest.fixture(autouse=True) def patch_snowflake(mocker, mock_sf): mocker.patch("src.app.get_connection", return_value=mock_sf) mocker.patch("src.app.get_onboarding_domains", return_value=frozenset()) mocker.patch("src.app.get_registry_domains", return_value=frozenset()) mocker.patch("src.app.get_crm_domains", return_value=frozenset()) mocker.patch("src.app.get_fansifter_domains", return_value=frozenset()) mocker.patch("src.app.get_artist_stores_metadata", return_value=[]) mocker.patch("src.app.upsert_registry") @pytest.fixture(autouse=True) def patch_metrics(mocker): mocker.patch("src.app.lambda_metric") class TestFindNewStores: def test_empty_artist_stores_returns_empty(self, mock_sf): assert _find_new_stores(mock_sf) == [] def test_store_in_onboarding_state_is_excluded(self, mocker, mock_sf): mocker.patch("src.app.get_onboarding_domains", return_value=frozenset({"existing.myshopify.com"})) mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="existing.myshopify.com")] ) assert _find_new_stores(mock_sf) == [] def test_store_in_registry_is_excluded(self, mocker, mock_sf): mocker.patch("src.app.get_registry_domains", return_value=frozenset({"existing.myshopify.com"})) mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="existing.myshopify.com")] ) assert _find_new_stores(mock_sf) == [] def test_crm_store_is_excluded(self, mocker, mock_sf): mocker.patch("src.app.get_crm_domains", return_value=frozenset({"crm-store.myshopify.com"})) mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="crm-store.myshopify.com")] ) assert _find_new_stores(mock_sf) == [] def test_fansifter_store_is_excluded(self, mocker, mock_sf): mocker.patch("src.app.get_fansifter_domains", return_value=frozenset({"fan.myshopify.com"})) mocker.patch("src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="fan.myshopify.com")]) assert _find_new_stores(mock_sf) == [] def test_already_tracked_store_is_excluded(self, mocker, mock_sf): mocker.patch("src.app.get_registry_domains", return_value=frozenset({"already-tracked.myshopify.com"})) mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="already-tracked.myshopify.com")] ) assert _find_new_stores(mock_sf) == [] def test_alt_domain_on_row_suppressed_when_primary_is_excluded(self, mocker, mock_sf): # Both myshopify domains on the same row must be skipped if the primary is tracked. mocker.patch("src.app.get_onboarding_domains", return_value=frozenset({"primary.myshopify.com"})) mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="primary.myshopify.com", shopify_store_url="alt.myshopify.com")], ) assert _find_new_stores(mock_sf) == [] def test_new_store_has_correct_structure(self, mocker, mock_sf): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[ _make_row(shopify_shop="new-store.myshopify.com", selling_country="US", rep_owner="Ceremony of Roses") ], ) result = _find_new_stores(mock_sf) assert len(result) == 1 store = result[0] assert store["shop_domain"] == "new-store.myshopify.com" assert store["schema_name"] == "shopify_cor_us_new_store" assert store["selling_country"] == "US" assert store["merch_company"] is None def test_custom_domain_extracted_from_public_domain(self, mocker, mock_sf): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="new-store.myshopify.com", public_domain="www.myband.com")], ) result = _find_new_stores(mock_sf) assert result[0]["custom_domain"] == "myband.com" def test_country_starting_with_hash_is_treated_as_empty(self, mocker, mock_sf): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="new-store.myshopify.com", selling_country="#N/A")], ) result = _find_new_stores(mock_sf) assert result[0]["selling_country"] is None def test_alt_myshopify_domain_captured_from_second_myshopify_url(self, mocker, mock_sf): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="primary.myshopify.com", shopify_store_url="alt.myshopify.com")], ) result = _find_new_stores(mock_sf) assert result[0]["shop_domain"] == "primary.myshopify.com" assert result[0]["alt_myshopify_domain"] == "alt.myshopify.com" def test_non_myshopify_public_domain_not_added_as_alt_myshopify(self, mocker, mock_sf): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="new-store.myshopify.com", public_domain="www.myband.com")], ) result = _find_new_stores(mock_sf) assert result[0]["alt_myshopify_domain"] is None class TestHandler: def test_no_new_stores_returns_discovered_zero(self): result = handler({}, None) assert result == {"discovered": 0} def test_sqs_failure_skips_registry_write(self, mocker): mocker.patch("src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="new.myshopify.com")]) mock_registry = mocker.patch("src.app.upsert_registry") mocker.patch("src.app._sqs.send_message", side_effect=Exception("SQS unavailable")) result = handler({}, None) assert result["send_failed"] == 1 assert result["queued"] == 0 mock_registry.assert_not_called() def test_registry_write_failure_still_counts_store_as_queued(self, mocker): mocker.patch("src.app.get_artist_stores_metadata", return_value=[_make_row(shopify_shop="new.myshopify.com")]) mocker.patch("src.app._sqs.send_message") mocker.patch("src.app.upsert_registry", side_effect=Exception("DB error")) result = handler({}, None) assert result["queued"] == 1 assert result["registry_write_failed"] == 1 def test_happy_path_counts_are_correct(self, mocker): mocker.patch( "src.app.get_artist_stores_metadata", return_value=[ _make_row(shopify_shop="store-a.myshopify.com"), _make_row(shopify_shop="store-b.myshopify.com"), ], ) mocker.patch("src.app._sqs.send_message") mocker.patch("src.app.upsert_registry") result = handler({}, None) assert result == {"discovered": 2, "queued": 2, "send_failed": 0, "registry_write_failed": 0}