fix(spec): enforce read-time budget and re-render invalid comment submissions
This commit is contained in:
@@ -63,6 +63,34 @@ def test_comment_post_rejected_when_comments_disabled(client, home_page):
|
|||||||
assert Comment.objects.count() == 0
|
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", "<p>body</p>")])
|
||||||
|
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
|
@pytest.mark.django_db
|
||||||
def test_comment_reply_depth_is_enforced(client, home_page):
|
def test_comment_reply_depth_is_enforced(client, home_page):
|
||||||
cache.clear()
|
cache.clear()
|
||||||
@@ -100,7 +128,8 @@ def test_comment_reply_depth_is_enforced(client, home_page):
|
|||||||
"honeypot": "",
|
"honeypot": "",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert resp.status_code == 302
|
assert resp.status_code == 200
|
||||||
|
assert b"Reply depth exceeds the allowed limit" in resp.content
|
||||||
assert Comment.objects.count() == 2
|
assert Comment.objects.count() == 2
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from django.contrib import messages
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
from django.shortcuts import get_object_or_404, redirect, render
|
||||||
from django.views import View
|
from django.views import View
|
||||||
|
|
||||||
from apps.blog.models import ArticlePage
|
from apps.blog.models import ArticlePage
|
||||||
@@ -23,6 +23,12 @@ def client_ip_from_request(request) -> str:
|
|||||||
|
|
||||||
|
|
||||||
class CommentCreateView(View):
|
class CommentCreateView(View):
|
||||||
|
def _render_article_with_errors(self, request, article, form):
|
||||||
|
context = article.get_context(request)
|
||||||
|
context["page"] = article
|
||||||
|
context["comment_form"] = form
|
||||||
|
return render(request, "blog/article_page.html", context, status=200)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
ip = client_ip_from_request(request)
|
ip = client_ip_from_request(request)
|
||||||
key = f"comment-rate:{ip}"
|
key = f"comment-rate:{ip}"
|
||||||
@@ -48,11 +54,10 @@ class CommentCreateView(View):
|
|||||||
try:
|
try:
|
||||||
comment.full_clean()
|
comment.full_clean()
|
||||||
except ValidationError:
|
except ValidationError:
|
||||||
messages.error(request, "Reply depth exceeds the allowed limit")
|
form.add_error(None, "Reply depth exceeds the allowed limit")
|
||||||
return redirect(article.url)
|
return self._render_article_with_errors(request, article, form)
|
||||||
comment.save()
|
comment.save()
|
||||||
messages.success(request, "Your comment is awaiting moderation")
|
messages.success(request, "Your comment is awaiting moderation")
|
||||||
return redirect(f"{article.url}?commented=1")
|
return redirect(f"{article.url}?commented=1")
|
||||||
|
|
||||||
messages.error(request, "Please correct the form errors")
|
return self._render_article_with_errors(request, article, form)
|
||||||
return redirect(article.url)
|
|
||||||
|
|||||||
@@ -72,3 +72,4 @@ def test_read_time_benchmark(benchmark):
|
|||||||
|
|
||||||
result = benchmark(article._compute_read_time)
|
result = benchmark(article._compute_read_time)
|
||||||
assert result >= 1
|
assert result >= 1
|
||||||
|
assert benchmark.stats.stats.mean < 0.05
|
||||||
|
|||||||
@@ -69,12 +69,20 @@
|
|||||||
{% empty %}
|
{% empty %}
|
||||||
<p>No comments yet.</p>
|
<p>No comments yet.</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% if comment_form and comment_form.errors %}
|
||||||
|
<div aria-label="Comment form errors">
|
||||||
|
{{ comment_form.non_field_errors }}
|
||||||
|
{% for field in comment_form %}
|
||||||
|
{{ field.errors }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
<form method="post" action="{% url 'comment_post' %}">
|
<form method="post" action="{% url 'comment_post' %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<input type="hidden" name="article_id" value="{{ page.id }}" />
|
<input type="hidden" name="article_id" value="{{ page.id }}" />
|
||||||
<input type="text" name="author_name" required />
|
<input type="text" name="author_name" value="{% if comment_form %}{{ comment_form.author_name.value|default:'' }}{% endif %}" required />
|
||||||
<input type="email" name="author_email" required />
|
<input type="email" name="author_email" value="{% if comment_form %}{{ comment_form.author_email.value|default:'' }}{% endif %}" required />
|
||||||
<textarea name="body" required></textarea>
|
<textarea name="body" required>{% if comment_form %}{{ comment_form.body.value|default:'' }}{% endif %}</textarea>
|
||||||
<input type="text" name="honeypot" style="display:none" />
|
<input type="text" name="honeypot" style="display:none" />
|
||||||
<button type="submit">Post comment</button>
|
<button type="submit">Post comment</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user