"""Unit tests for marketing_intelligence.core.models.""" from typing import Any from marketing_intelligence.core.models import PostMetrics, SentimentRequest class TestSentimentRequest: def _make(self, **kwargs: Any) -> SentimentRequest: defaults: dict[str, Any] = { "prompt": "How is the track performing?", "artist_name": "Joy Crookes", "tracks": ["Feet Don't Fail Me Now"], "sample_size": 20, } return SentimentRequest(**(defaults | kwargs)) def test_artist_key_lowercased_and_slugified(self): req = self._make(artist_name="Joy Crookes") assert req.artist_key == "joy_crookes" def test_artist_key_strips_leading_trailing_underscores(self): req = self._make(artist_name=" The 1975 ") assert not req.artist_key.startswith("_") assert not req.artist_key.endswith("_") def test_track_to_tag_removes_non_alphanumeric(self): req = self._make() assert req.track_to_tag("Feet Don't Fail Me Now") == "feetdontfailmenow" def test_track_to_tag_lowercases(self): req = self._make() assert req.track_to_tag("Hello World") == "helloworld" def test_to_campaign_maps_tracks_to_tags(self): req = self._make(tracks=["Feet Don't Fail Me Now", "Wild Wind"]) campaign = req.to_campaign() assert campaign.tags == ["feetdontfailmenow", "wildwind"] assert campaign.track_names == ["Feet Don't Fail Me Now", "Wild Wind"] def test_to_campaign_sets_artist_fields(self): req = self._make(artist_name="Joy Crookes", tiktok_handle="joycrookesmusic") campaign = req.to_campaign() assert campaign.artist_name == "Joy Crookes" assert campaign.tiktok_handle == "joycrookesmusic" assert campaign.artist_key == "joy_crookes" def test_to_campaign_prompt_goes_to_questions(self): req = self._make(prompt="Is it trending?") campaign = req.to_campaign() assert "Is it trending?" in campaign.questions def test_to_campaign_sample_size_preserved(self): req = self._make(sample_size=50) campaign = req.to_campaign() assert campaign.sample_size == 50 class TestPostMetrics: def _base_kwargs(self, **overrides: Any) -> dict[str, Any]: return { "campaign_config_key": "test_artist", "post_id": "7123456789", "run_id": "run_001", "observed_at": "2026-06-01T12:00:00+00:00", "views": 1000, "likes": 50, "comments": 10, "shares": 5, "favorites": 3, } | overrides def test_from_observation_first_run_zero_deltas(self): metrics = PostMetrics.from_observation(**self._base_kwargs()) assert metrics.delta_views == 0 assert metrics.delta_likes == 0 assert metrics.delta_comments == 0 def test_from_observation_computes_deltas_against_prev(self): prev = {"views": 800, "likes": 40, "comments": 8, "shares": 3, "favorites": 2} metrics = PostMetrics.from_observation(**self._base_kwargs(), prev=prev) assert metrics.delta_views == 200 assert metrics.delta_likes == 10 assert metrics.delta_comments == 2 def test_from_observation_delta_zero_when_prev_missing_field(self): prev = {"views": 800} metrics = PostMetrics.from_observation(**self._base_kwargs(), prev=prev) assert metrics.delta_views == 200 assert metrics.delta_likes == 0 def test_from_observation_delta_zero_when_curr_none(self): prev = {"views": 800} metrics = PostMetrics.from_observation( **self._base_kwargs(views=None), prev=prev ) assert metrics.delta_views == 0 def test_from_observation_stores_all_fields(self): metrics = PostMetrics.from_observation(**self._base_kwargs()) assert metrics.views == 1000 assert metrics.likes == 50 assert metrics.run_id == "run_001" assert metrics.post_id == "7123456789"