from unittest.mock import patch import pytest from django.core.cache import cache from django.test import override_settings from apps.blog.models import ArticleIndexPage, ArticlePage from apps.blog.tests.factories import AuthorFactory from apps.comments.models import Comment @pytest.mark.django_db def test_comment_post_flow(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "
body
")]) index.add_child(instance=article) article.save_revision().publish() resp = client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": "Hello", "honeypot": "", }, ) assert resp.status_code == 302 assert resp["Location"].endswith("?commented=pending") assert Comment.objects.count() == 1 @pytest.mark.django_db def test_comment_post_redirect_banner_renders_on_article_page(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")]) index.add_child(instance=article) article.save_revision().publish() resp = client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": "Hello", "honeypot": "", }, follow=True, ) assert resp.status_code == 200 assert b"Your comment has been posted and is awaiting moderation." in resp.content @pytest.mark.django_db @override_settings(TURNSTILE_SECRET_KEY="test-secret") def test_comment_post_redirect_banner_renders_approved_state(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")]) index.add_child(instance=article) article.save_revision().publish() with patch("apps.comments.views._verify_turnstile", return_value=True): resp = client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": "Hello", "honeypot": "", "cf-turnstile-response": "tok", }, follow=True, ) assert resp.status_code == 200 assert b"Comment posted!" in resp.content @pytest.mark.django_db def test_comment_post_rejected_when_comments_disabled(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage( title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")], comments_enabled=False, ) index.add_child(instance=article) article.save_revision().publish() resp = client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": "Hello", "honeypot": "", }, ) assert resp.status_code == 404 assert Comment.objects.count() == 0 @pytest.mark.django_db def test_invalid_comment_post_rerenders_form_with_errors(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")]) index.add_child(instance=article) article.save_revision().publish() resp = client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": " ", "honeypot": "", }, ) assert resp.status_code == 200 assert b'aria-label="Comment form errors"' in resp.content assert b'value="Test"' in resp.content assert b"test@example.com" in resp.content assert Comment.objects.count() == 0 @pytest.mark.django_db def test_comment_reply_depth_is_enforced(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")]) index.add_child(instance=article) article.save_revision().publish() parent = Comment.objects.create( article=article, author_name="Parent", author_email="p@example.com", body="Parent", is_approved=True, ) child = Comment.objects.create( article=article, parent=parent, author_name="Child", author_email="c@example.com", body="Child", is_approved=True, ) resp = client.post( "/comments/post/", { "article_id": article.id, "parent_id": child.id, "author_name": "TooDeep", "author_email": "deep@example.com", "body": "Nope", "honeypot": "", }, ) assert resp.status_code == 200 assert b"Reply depth exceeds the allowed limit" in resp.content assert Comment.objects.count() == 2 @pytest.mark.django_db @override_settings(TRUSTED_PROXY_IPS=[]) def test_comment_uses_remote_addr_when_proxy_untrusted(client, home_page): cache.clear() index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() article = ArticlePage(title="A", slug="a", author=author, summary="s", body=[("rich_text", "body
")]) index.add_child(instance=article) article.save_revision().publish() client.post( "/comments/post/", { "article_id": article.id, "author_name": "Test", "author_email": "test@example.com", "body": "Hello", "honeypot": "", }, REMOTE_ADDR="10.0.0.1", HTTP_X_FORWARDED_FOR="203.0.113.7", ) comment = Comment.objects.get() assert comment.ip_address == "10.0.0.1"