import pytest from django.contrib import admin from types import SimpleNamespace from apps.blog.admin import CommentAdmin, PostAdmin from apps.blog.models import Comment, Like, Post, SavedPost from apps.gamification.models import ContributionLedger from apps.gamification.services import summary_for_user pytestmark = [pytest.mark.integration, pytest.mark.django_db] def test_publish_coauthor_like_and_save_award_expected_contributions(user_factory): author = user_factory() coauthor = user_factory() actor = user_factory() post = Post.objects.create( title="Community story", content="A real story", author=author, status=Post.StatusChoices.PUBLISHED, ) post.writers.add(coauthor) Like.objects.create(post=post, user=actor) SavedPost.objects.create(post=post, user=actor) assert summary_for_user(author)["points_total"] == 22 assert summary_for_user(coauthor)["points_total"] == 22 assert summary_for_user(actor)["points_total"] == 2 assert summary_for_user(actor)["activity_counts"]["likes"] == 1 assert summary_for_user(coauthor)["activity_counts"]["likes"] == 1 post.writers.remove(coauthor) post.writers.remove(author) assert summary_for_user(author)["points_total"] == 22 assert summary_for_user(coauthor)["points_total"] == 0 assert summary_for_user(actor)["points_total"] == 2 def test_self_like_and_save_count_once_each_and_only_like_advances_badge_ladder(user_factory): member = user_factory() post = Post.objects.create( title="My post", content="My content", author=member, status=Post.StatusChoices.PUBLISHED, ) Like.objects.create(post=post, user=member) SavedPost.objects.create(post=post, user=member) summary = summary_for_user(member) assert summary["points_total"] == 22 assert summary["activity_counts"]["likes"] == 1 assert not any(badge["category"] == "save" for badge in summary["badges"]) def test_approved_comments_and_replies_score_and_hiding_reverses_them(user_factory): author = user_factory() commenter = user_factory() post = Post.objects.create( title="Discussion", content="Discuss this", author=author, status=Post.StatusChoices.PUBLISHED, ) comment = Comment.objects.create(post=post, author=commenter, content="A comment") reply = Comment.objects.create(post=post, author=author, content="A reply", parent=comment) assert summary_for_user(commenter)["points_total"] == 5 assert summary_for_user(author)["points_total"] == 25 comment.hide(user=author, note="moderated") assert summary_for_user(commenter)["points_total"] == 0 assert summary_for_user(author)["points_total"] == 20 assert summary_for_user(commenter)["activity_counts"]["comments"] == 0 assert ContributionLedger.objects.filter(user=commenter).count() == 2 assert ContributionLedger.objects.filter(user=author, source_type="blog_comment", source_id=str(reply.pk)).count() == 2 def test_removing_like_and_unpublishing_post_reverse_only_valid_points(user_factory): author = user_factory() actor = user_factory() post = Post.objects.create( title="Published then withdrawn", content="Content", author=author, status=Post.StatusChoices.PUBLISHED, ) like = Like.objects.create(post=post, user=actor) SavedPost.objects.create(post=post, user=actor) assert summary_for_user(actor)["points_total"] == 2 like.delete() assert summary_for_user(actor)["points_total"] == 1 assert summary_for_user(author)["points_total"] == 21 post.status = Post.StatusChoices.ARCHIVED post.save(update_fields=["status", "updated_at"]) assert summary_for_user(author)["points_total"] == 0 assert summary_for_user(actor)["points_total"] == 0 def test_blog_admin_publish_and_approve_actions_flow_through_scoring(user_factory, mocker): editor = user_factory(is_staff=True) author = user_factory() commenter = user_factory() draft = Post.objects.create(title="Draft", content="Content", author=author) post_admin = PostAdmin(Post, admin.site) comment_admin = CommentAdmin(Comment, admin.site) mocker.patch.object(PostAdmin, "message_user") mocker.patch.object(CommentAdmin, "message_user") post_admin.make_published(SimpleNamespace(user=editor), Post.objects.filter(pk=draft.pk)) draft.refresh_from_db() pending_comment = Comment.objects.create( post=draft, author=commenter, content="Awaiting approval", is_approved=False, ) assert summary_for_user(author)["points_total"] == 20 assert summary_for_user(commenter)["points_total"] == 0 comment_admin.approve_comments(SimpleNamespace(user=editor), Comment.objects.filter(pk=pending_comment.pk)) assert summary_for_user(commenter)["points_total"] == 5