import pytest from apps.gamification.services import summary_for_user, sync_source_effects from apps.gamification.models import ContributionLedger pytestmark = [pytest.mark.integration, pytest.mark.django_db] def test_source_award_is_idempotent_and_reversal_then_restore_tracks_current_state(user_factory): member = user_factory() award = [{"user": member, "activity": "post", "role": "author", "points": 20}] sync_source_effects("blog_post", "post-1", award) sync_source_effects("blog_post", "post-1", award) assert summary_for_user(member)["points_total"] == 20 assert ContributionLedger.objects.filter(user=member).count() == 1 sync_source_effects("blog_post", "post-1", []) sync_source_effects("blog_post", "post-1", []) assert summary_for_user(member)["points_total"] == 0 assert ContributionLedger.objects.filter(user=member).count() == 2 sync_source_effects("blog_post", "post-1", award) assert summary_for_user(member)["points_total"] == 20 assert summary_for_user(member)["activity_counts"]["posts"] == 1 assert ContributionLedger.objects.filter(user=member).count() == 3 def test_summary_does_not_count_saves_as_activity_badges(user_factory): member = user_factory() sync_source_effects( "blog_save", "save-1", [{"user": member, "activity": "save", "role": "actor", "points": 1}], ) summary = summary_for_user(member) assert summary["points_total"] == 1 assert summary["points_blog"] == 1 assert summary["activity_counts"]["likes"] == 0 assert not any(badge["category"] == "save" for badge in summary["badges"])