Add Docker-executed pytest suite with >90% coverage
This commit is contained in:
0
apps/authors/tests/__init__.py
Normal file
0
apps/authors/tests/__init__.py
Normal file
16
apps/authors/tests/test_models.py
Normal file
16
apps/authors/tests/test_models.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import pytest
|
||||
|
||||
from apps.authors.models import Author
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_author_create_and_social_links():
|
||||
author = Author.objects.create(name="Mark", slug="mark", twitter_url="https://x.com/mark")
|
||||
assert str(author) == "Mark"
|
||||
assert author.get_social_links() == {"twitter": "https://x.com/mark"}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_author_user_nullable():
|
||||
author = Author.objects.create(name="No User", slug="no-user")
|
||||
assert author.user is None
|
||||
0
apps/blog/tests/__init__.py
Normal file
0
apps/blog/tests/__init__.py
Normal file
64
apps/blog/tests/factories.py
Normal file
64
apps/blog/tests/factories.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import factory
|
||||
import wagtail_factories
|
||||
from django.utils import timezone
|
||||
from taggit.models import Tag
|
||||
from wagtail.models import Page
|
||||
|
||||
from apps.authors.models import Author
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage, HomePage, TagMetadata
|
||||
from apps.legal.models import LegalIndexPage, LegalPage
|
||||
|
||||
|
||||
class AuthorFactory(factory.django.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Author
|
||||
|
||||
name = factory.Sequence(lambda n: f"Author {n}")
|
||||
slug = factory.Sequence(lambda n: f"author-{n}")
|
||||
|
||||
|
||||
class HomePageFactory(wagtail_factories.PageFactory):
|
||||
class Meta:
|
||||
model = HomePage
|
||||
|
||||
|
||||
class ArticleIndexPageFactory(wagtail_factories.PageFactory):
|
||||
class Meta:
|
||||
model = ArticleIndexPage
|
||||
|
||||
|
||||
class ArticlePageFactory(wagtail_factories.PageFactory):
|
||||
class Meta:
|
||||
model = ArticlePage
|
||||
|
||||
title = factory.Sequence(lambda n: f"Article {n}")
|
||||
slug = factory.Sequence(lambda n: f"article-{n}")
|
||||
author = factory.SubFactory(AuthorFactory)
|
||||
summary = "Summary"
|
||||
body = [("rich_text", "<p>Hello world</p>")]
|
||||
first_published_at = factory.LazyFunction(timezone.now)
|
||||
|
||||
|
||||
class LegalIndexPageFactory(wagtail_factories.PageFactory):
|
||||
class Meta:
|
||||
model = LegalIndexPage
|
||||
|
||||
|
||||
class LegalPageFactory(wagtail_factories.PageFactory):
|
||||
class Meta:
|
||||
model = LegalPage
|
||||
|
||||
title = factory.Sequence(lambda n: f"Legal {n}")
|
||||
slug = factory.Sequence(lambda n: f"legal-{n}")
|
||||
body = "<p>Body</p>"
|
||||
last_updated = factory.Faker("date_object")
|
||||
|
||||
|
||||
def root_page():
|
||||
return Page.get_first_root_node()
|
||||
|
||||
|
||||
def create_tag_with_meta(name: str, colour: str = "neutral"):
|
||||
tag, _ = Tag.objects.get_or_create(name=name, slug=name)
|
||||
TagMetadata.objects.get_or_create(tag=tag, defaults={"colour": colour})
|
||||
return tag
|
||||
8
apps/blog/tests/test_feeds.py
Normal file
8
apps/blog/tests/test_feeds.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_feed_endpoint(client):
|
||||
resp = client.get("/feed/")
|
||||
assert resp.status_code == 200
|
||||
assert resp["Content-Type"].startswith("application/rss+xml")
|
||||
18
apps/blog/tests/test_feeds_more.py
Normal file
18
apps/blog/tests/test_feeds_more.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import pytest
|
||||
|
||||
from apps.blog.feeds import AllArticlesFeed
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_all_feed_methods(article_page):
|
||||
feed = AllArticlesFeed()
|
||||
assert feed.item_title(article_page) == article_page.title
|
||||
assert article_page.summary in feed.item_description(article_page)
|
||||
assert article_page.author.name == feed.item_author_name(article_page)
|
||||
assert feed.item_link(article_page).startswith("http")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tag_feed_not_found(client):
|
||||
resp = client.get("/feed/tag/does-not-exist/")
|
||||
assert resp.status_code == 404
|
||||
42
apps/blog/tests/test_models.py
Normal file
42
apps/blog/tests/test_models.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import pytest
|
||||
from django.db import IntegrityError
|
||||
from taggit.models import Tag
|
||||
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage, HomePage, TagMetadata
|
||||
from apps.blog.tests.factories import AuthorFactory
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_home_page_creation(home_page):
|
||||
assert HomePage.objects.count() == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_index_parent_restriction():
|
||||
assert ArticleIndexPage.parent_page_types == ["blog.HomePage"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_compute_read_time_excludes_code(home_page):
|
||||
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>one two three</p>"), ("code", {"language": "python", "raw_code": "x y z"})],
|
||||
)
|
||||
index.add_child(instance=article)
|
||||
article.save()
|
||||
assert article.read_time_mins == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_tag_metadata_css_and_uniqueness():
|
||||
tag = Tag.objects.create(name="llms", slug="llms")
|
||||
meta = TagMetadata.objects.create(tag=tag, colour="cyan")
|
||||
assert meta.get_css_classes()["bg"].startswith("bg-cyan")
|
||||
with pytest.raises(IntegrityError):
|
||||
TagMetadata.objects.create(tag=tag, colour="pink")
|
||||
27
apps/blog/tests/test_more_models.py
Normal file
27
apps/blog/tests/test_more_models.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import pytest
|
||||
|
||||
from apps.blog.models import TagMetadata
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_home_context_lists_articles(home_page, article_page):
|
||||
ctx = home_page.get_context(type("Req", (), {"GET": {}})())
|
||||
assert "latest_articles" in ctx
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_index_context_handles_page_values(article_index, article_page, rf):
|
||||
request = rf.get("/", {"page": "notanumber"})
|
||||
ctx = article_index.get_context(request)
|
||||
assert ctx["articles"].number == 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_related_articles_fallback(article_page, article_index):
|
||||
related = article_page.get_related_articles()
|
||||
assert isinstance(related, list)
|
||||
|
||||
|
||||
def test_tag_metadata_fallback_classes():
|
||||
css = TagMetadata.get_fallback_css()
|
||||
assert css["bg"].startswith("bg-")
|
||||
61
apps/blog/tests/test_views.py
Normal file
61
apps/blog/tests/test_views.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import pytest
|
||||
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage
|
||||
from apps.blog.tests.factories import AuthorFactory
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_homepage_render(client, home_page):
|
||||
resp = client.get("/")
|
||||
assert resp.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_index_pagination_and_tag_filter(client, home_page):
|
||||
index = ArticleIndexPage(title="Articles", slug="articles")
|
||||
home_page.add_child(instance=index)
|
||||
author = AuthorFactory()
|
||||
for n in range(14):
|
||||
article = ArticlePage(
|
||||
title=f"A{n}",
|
||||
slug=f"a{n}",
|
||||
author=author,
|
||||
summary="summary",
|
||||
body=[("rich_text", "<p>body</p>")],
|
||||
)
|
||||
index.add_child(instance=article)
|
||||
article.save_revision().publish()
|
||||
|
||||
resp = client.get("/articles/?page=2")
|
||||
assert resp.status_code == 200
|
||||
assert resp.context["articles"].number == 2
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_page_related_context(client, home_page):
|
||||
index = ArticleIndexPage(title="Articles", slug="articles")
|
||||
home_page.add_child(instance=index)
|
||||
author = AuthorFactory()
|
||||
main = ArticlePage(
|
||||
title="Main",
|
||||
slug="main",
|
||||
author=author,
|
||||
summary="summary",
|
||||
body=[("rich_text", "<p>body</p>")],
|
||||
)
|
||||
index.add_child(instance=main)
|
||||
main.save_revision().publish()
|
||||
|
||||
related = ArticlePage(
|
||||
title="Related",
|
||||
slug="related",
|
||||
author=author,
|
||||
summary="summary",
|
||||
body=[("rich_text", "<p>body</p>")],
|
||||
)
|
||||
index.add_child(instance=related)
|
||||
related.save_revision().publish()
|
||||
|
||||
resp = client.get("/articles/main/")
|
||||
assert resp.status_code == 200
|
||||
assert "related_articles" in resp.context
|
||||
0
apps/comments/tests/__init__.py
Normal file
0
apps/comments/tests/__init__.py
Normal file
40
apps/comments/tests/test_models.py
Normal file
40
apps/comments/tests/test_models.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import pytest
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
from apps.blog.models import ArticleIndexPage, ArticlePage
|
||||
from apps.blog.tests.factories import AuthorFactory
|
||||
from apps.comments.models import Comment
|
||||
|
||||
|
||||
def create_article(home):
|
||||
index = ArticleIndexPage(title="Articles", slug="articles")
|
||||
home.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()
|
||||
return article
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_comment_defaults_and_absolute_url(home_page):
|
||||
article = create_article(home_page)
|
||||
comment = Comment.objects.create(article=article, author_name="N", author_email="n@example.com", body="hello")
|
||||
assert comment.is_approved is False
|
||||
assert comment.get_absolute_url().endswith(f"#comment-{comment.id}")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reply_depth_validation(home_page):
|
||||
article = create_article(home_page)
|
||||
parent = Comment.objects.create(article=article, author_name="P", author_email="p@example.com", body="p")
|
||||
child = Comment.objects.create(
|
||||
article=article,
|
||||
author_name="C",
|
||||
author_email="c@example.com",
|
||||
body="c",
|
||||
parent=parent,
|
||||
)
|
||||
nested = Comment(article=article, author_name="X", author_email="x@example.com", body="x", parent=child)
|
||||
with pytest.raises(ValidationError):
|
||||
nested.clean()
|
||||
26
apps/comments/tests/test_more.py
Normal file
26
apps/comments/tests/test_more.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import pytest
|
||||
from django.core.cache import cache
|
||||
|
||||
from apps.comments.forms import CommentForm
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_comment_form_rejects_blank_body():
|
||||
form = CommentForm(data={"author_name": "A", "author_email": "a@a.com", "body": " ", "article_id": 1})
|
||||
assert not form.is_valid()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_comment_rate_limit(client, article_page):
|
||||
cache.clear()
|
||||
payload = {
|
||||
"article_id": article_page.id,
|
||||
"author_name": "T",
|
||||
"author_email": "t@example.com",
|
||||
"body": "Hi",
|
||||
"honeypot": "",
|
||||
}
|
||||
for _ in range(3):
|
||||
client.post("/comments/post/", payload)
|
||||
resp = client.post("/comments/post/", payload)
|
||||
assert resp.status_code == 429
|
||||
61
apps/comments/tests/test_views.py
Normal file
61
apps/comments/tests/test_views.py
Normal file
@@ -0,0 +1,61 @@
|
||||
import pytest
|
||||
from django.core.cache import cache
|
||||
|
||||
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", "<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": "Hello",
|
||||
"honeypot": "",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 302
|
||||
assert Comment.objects.count() == 1
|
||||
|
||||
|
||||
@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", "<p>body</p>")],
|
||||
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
|
||||
0
apps/core/tests/__init__.py
Normal file
0
apps/core/tests/__init__.py
Normal file
23
apps/core/tests/test_consent.py
Normal file
23
apps/core/tests/test_consent.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
||||
from apps.core.consent import CONSENT_COOKIE_NAME, ConsentService
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_consent_round_trip(rf):
|
||||
request = HttpRequest()
|
||||
response = HttpResponse()
|
||||
ConsentService.set_consent(response, analytics=True, advertising=False)
|
||||
cookie = response.cookies[CONSENT_COOKIE_NAME].value
|
||||
request.COOKIES[CONSENT_COOKIE_NAME] = cookie
|
||||
state = ConsentService.get_consent(request)
|
||||
assert state.analytics is True
|
||||
assert state.advertising is False
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_consent_post_view(client):
|
||||
resp = client.post("/consent/", {"accept_all": "1"}, follow=False)
|
||||
assert resp.status_code == 302
|
||||
assert CONSENT_COOKIE_NAME in resp.cookies
|
||||
50
apps/core/tests/test_more.py
Normal file
50
apps/core/tests/test_more.py
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
import pytest
|
||||
from django.template import Context
|
||||
from django.test import RequestFactory
|
||||
from taggit.models import Tag
|
||||
from wagtail.models import Site
|
||||
|
||||
from apps.core.context_processors import site_settings
|
||||
from apps.core.templatetags import core_tags
|
||||
from apps.core.templatetags.seo_tags import article_json_ld
|
||||
from apps.legal.models import LegalIndexPage, LegalPage
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_context_processor_returns_sitesettings(home_page):
|
||||
rf = RequestFactory()
|
||||
request = rf.get("/")
|
||||
request.site = Site.find_for_request(request)
|
||||
data = site_settings(request)
|
||||
assert "site_settings" in data
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_tag_css_fallback():
|
||||
tag = Tag.objects.create(name="x", slug="x")
|
||||
value = core_tags.get_tag_css(tag)
|
||||
assert "bg-zinc" in value
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_legal_pages_tag_callable(home_page):
|
||||
legal_index = LegalIndexPage(title="Legal", slug="legal")
|
||||
home_page.add_child(instance=legal_index)
|
||||
legal = LegalPage(title="Privacy", slug="privacy-policy", body="<p>x</p>", last_updated="2026-01-01")
|
||||
legal_index.add_child(instance=legal)
|
||||
legal.save_revision().publish()
|
||||
|
||||
rf = RequestFactory()
|
||||
request = rf.get("/")
|
||||
pages = core_tags.get_legal_pages({"request": request})
|
||||
assert pages.count() >= 1
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_article_json_ld_contains_headline(article_page, rf):
|
||||
request = rf.get("/")
|
||||
request.site = Site.objects.filter(is_default_site=True).first()
|
||||
result = article_json_ld(Context({"request": request}), article_page)
|
||||
assert "application/ld+json" in result
|
||||
assert article_page.title in result
|
||||
2
apps/core/tests/test_smoke.py
Normal file
2
apps/core/tests/test_smoke.py
Normal file
@@ -0,0 +1,2 @@
|
||||
def test_smoke():
|
||||
assert 1 == 1
|
||||
15
apps/core/tests/test_tags.py
Normal file
15
apps/core/tests/test_tags.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import pytest
|
||||
|
||||
from apps.legal.models import LegalIndexPage, LegalPage
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_legal_pages_tag(client, home_page):
|
||||
legal_index = LegalIndexPage(title="Legal", slug="legal")
|
||||
home_page.add_child(instance=legal_index)
|
||||
legal = LegalPage(title="Privacy", slug="privacy-policy", last_updated="2026-01-01", body="<p>x</p>")
|
||||
legal_index.add_child(instance=legal)
|
||||
legal.save_revision().publish()
|
||||
|
||||
resp = client.get("/")
|
||||
assert resp.status_code == 200
|
||||
0
apps/legal/tests/__init__.py
Normal file
0
apps/legal/tests/__init__.py
Normal file
23
apps/legal/tests/test_models.py
Normal file
23
apps/legal/tests/test_models.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import pytest
|
||||
|
||||
from apps.legal.models import LegalIndexPage, LegalPage
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_legal_index_redirects(client, home_page):
|
||||
legal_index = LegalIndexPage(title="Legal", slug="legal")
|
||||
home_page.add_child(instance=legal_index)
|
||||
legal_index.save_revision().publish()
|
||||
resp = client.get("/legal/")
|
||||
assert resp.status_code == 302
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_legal_page_render(client, home_page):
|
||||
legal_index = LegalIndexPage(title="Legal", slug="legal")
|
||||
home_page.add_child(instance=legal_index)
|
||||
legal = LegalPage(title="Privacy", slug="privacy-policy", last_updated="2026-01-01", body="<p>x</p>")
|
||||
legal_index.add_child(instance=legal)
|
||||
legal.save_revision().publish()
|
||||
resp = client.get("/legal/privacy-policy/")
|
||||
assert resp.status_code == 200
|
||||
8
apps/legal/tests/test_more.py
Normal file
8
apps/legal/tests/test_more.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import pytest
|
||||
|
||||
from apps.legal.models import LegalIndexPage
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_legal_index_sitemap_urls_empty():
|
||||
assert LegalIndexPage().get_sitemap_urls() == []
|
||||
0
apps/newsletter/tests/__init__.py
Normal file
0
apps/newsletter/tests/__init__.py
Normal file
14
apps/newsletter/tests/test_more.py
Normal file
14
apps/newsletter/tests/test_more.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import pytest
|
||||
|
||||
from apps.newsletter.services import ProviderSyncService
|
||||
from apps.newsletter.views import confirmation_token
|
||||
|
||||
|
||||
def test_confirmation_token_roundtrip():
|
||||
token = confirmation_token("x@example.com")
|
||||
assert token
|
||||
|
||||
|
||||
def test_provider_sync_not_implemented():
|
||||
with pytest.raises(NotImplementedError):
|
||||
ProviderSyncService().sync(None)
|
||||
37
apps/newsletter/tests/test_views.py
Normal file
37
apps/newsletter/tests/test_views.py
Normal file
@@ -0,0 +1,37 @@
|
||||
import pytest
|
||||
from django.core import signing
|
||||
|
||||
from apps.newsletter.models import NewsletterSubscription
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_subscribe_ok(client):
|
||||
resp = client.post("/newsletter/subscribe/", {"email": "a@example.com", "source": "nav"})
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["status"] == "ok"
|
||||
assert NewsletterSubscription.objects.filter(email="a@example.com").exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_subscribe_invalid(client):
|
||||
resp = client.post("/newsletter/subscribe/", {"email": "bad"})
|
||||
assert resp.status_code == 400
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_confirm_endpoint(client):
|
||||
sub = NewsletterSubscription.objects.create(email="b@example.com")
|
||||
token = signing.dumps(sub.email, salt="newsletter-confirm")
|
||||
resp = client.get(f"/newsletter/confirm/{token}/")
|
||||
assert resp.status_code == 302
|
||||
sub.refresh_from_db()
|
||||
assert sub.confirmed is True
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_confirm_endpoint_with_expired_token(client, monkeypatch):
|
||||
sub = NewsletterSubscription.objects.create(email="c@example.com")
|
||||
token = signing.dumps(sub.email, salt="newsletter-confirm")
|
||||
monkeypatch.setattr("apps.newsletter.views.CONFIRMATION_TOKEN_MAX_AGE_SECONDS", -1)
|
||||
resp = client.get(f"/newsletter/confirm/{token}/")
|
||||
assert resp.status_code == 404
|
||||
Reference in New Issue
Block a user