Coverage for website/tests/test_author_view.py: 100%
76 statements
« prev ^ index » next coverage.py v7.5.0, created at 2025-09-13 15:29 -0300
« prev ^ index » next coverage.py v7.5.0, created at 2025-09-13 15:29 -0300
1from unittest.mock import MagicMock, patch
3from django.contrib.auth import get_user_model
4from django.core.files.uploadedfile import SimpleUploadedFile
5from django.http import HttpResponse
6from django.test import RequestFactory, TestCase
8import website.views.AuthorView as av
9from website.models.AuthorModel import Author
10from website.models.AuthorSocialMediaModel import SocialMedia
12User = get_user_model()
15class AuthorViewTests(TestCase):
16 def setUp(self):
17 self.factory = RequestFactory()
18 self.owner = User.objects.create_user(
19 email="owner@example.com", password="pw", username="owner"
20 )
21 self.other = User.objects.create_user(
22 email="other@example.com", password="pw", username="other"
23 )
24 self.author = Author.objects.create(
25 user=self.owner, author_name="Owner", author_url_slug="owner"
26 )
28 def test_view_author_page_owner_and_non_owner(self):
29 # Patch render to capture context without rendering templates
30 captured = {}
32 def fake_render(request, template_name=None, context=None, status=200):
33 resp = HttpResponse(status=status)
34 resp.context = context or {}
35 captured["ctx"] = context or {}
36 return resp
38 with patch.object(av, "render", side_effect=fake_render):
39 # non-owner
40 req = self.factory.get("/")
41 req.user = self.other
42 resp = av.view_author_page(req, slug=self.author.author_url_slug)
43 assert resp.status_code == 200
44 assert captured["ctx"]["author_connected"] is False
46 # owner
47 req2 = self.factory.get("/")
48 req2.user = self.owner
49 resp2 = av.view_author_page(req2, slug=self.author.author_url_slug)
50 assert resp2.status_code == 200
51 assert captured["ctx"]["author_connected"] is True
53 def test_check_request_post_parses_post_and_files(self):
54 # create a dummy social media entry so counts compare
55 SocialMedia.objects.create(
56 user_social_media=self.author, social_media=1, social_media_profile="p1"
57 )
59 data = {
60 "username": "owner",
61 "author_name": "New Owner",
62 "social_media": ["1", "2"],
63 "social_media_profile": ["p1", "p2"],
64 "exclude-social": ["", ""],
65 }
66 img = SimpleUploadedFile("pic.jpg", b"filecontent", content_type="image/jpeg")
67 req = self.factory.post("/", data, FILES={"image": img})
68 req.user = self.owner
69 parsed = av.check_request_post(req)
70 assert parsed["username"] == "owner"
71 assert parsed["name"] == "New Owner"
72 assert "image" in parsed
73 assert isinstance(parsed["exclude_social_media"], list)
75 def test_edit_author_profile_post_success_path(self):
76 # Prepare a POST request and patch internal helpers to simulate success path
77 data = {"username": "owner", "author_name": "Owner New"}
78 req = self.factory.post("/", data)
79 req.user = self.owner
81 # Patch helpers: check_request_post, check_author_form, update_social_media, create_social_media, exclude_social_media
82 fake_check = {
83 "username": "owner",
84 "name": "Owner New",
85 "check_username_request": None,
86 "image": None,
87 "new_social_addition": False,
88 "exclude_social_media": [],
89 }
91 # Patch form classes and formset factory so validations pass and redirect occurs
93 class FakeFormSet:
94 def __init__(self, *a, **k):
95 self.instance = None
97 def is_valid(self):
98 return True
100 def save(self):
101 return None
103 user_form_mock = MagicMock()
104 user_form_mock.is_valid.return_value = True
105 user_form_mock.save.return_value = req.user
106 user_form_mock.cleaned_data = {"password": None}
108 author_form_mock = MagicMock()
109 author_form_mock.is_valid.return_value = True
110 author_form_mock.save.return_value = self.author
112 with (
113 patch.object(av, "check_request_post", return_value=fake_check),
114 patch.object(av, "check_author_form", return_value=True),
115 patch.object(av, "update_social_media", return_value=False),
116 patch.object(av, "create_social_media") as mock_create,
117 patch.object(av, "exclude_social_media") as mock_exclude,
118 patch.object(av, "messages"),
119 patch.object(av, "redirect", return_value=HttpResponse(status=302)),
120 patch.object(av, "UserChangeForm", return_value=user_form_mock),
121 patch.object(av, "EditAuthorForm", return_value=author_form_mock),
122 patch.object(av, "inlineformset_factory", return_value=FakeFormSet),
123 ):
124 resp = av.edit_author_profile(req, slug=self.author.author_url_slug)
125 assert resp.status_code in (301, 302)
126 # ensure create/exclude not called in this path
127 mock_create.assert_not_called()
128 mock_exclude.assert_not_called()
130 def test_edit_author_profile_post_username_conflict_shows_error(self):
131 # Simulate a username conflict path where check_author_form is False due to username taken
132 data = {"username": "taken", "author_name": "Name"}
133 req = self.factory.post("/", data)
134 req.user = self.owner
135 fake_check = {
136 "username": "taken",
137 "name": "Name",
138 "check_username_request": True, # indicates another exists -> conflict
139 "image": None,
140 "new_social_addition": False,
141 "exclude_social_media": [],
142 }
144 with (
145 patch.object(av, "check_request_post", return_value=fake_check),
146 patch.object(av, "check_author_form", return_value=False),
147 patch.object(av, "messages"),
148 patch.object(av, "render", return_value=HttpResponse(status=200)),
149 ):
150 resp = av.edit_author_profile(req, slug=self.author.author_url_slug)
151 # In conflict case the view renders the edit page (status 200)
152 assert resp.status_code == 200
153 # messages.error should have been called by the view; ensure messages exists
154 # We patched messages module with a dummy object; here we assert render returned 200