• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rafalp / Misago / 17496798327

05 Sep 2025 02:59PM UTC coverage: 96.509% (-0.3%) from 96.769%
17496798327

Pull #1995

github

web-flow
Merge 17284d7d7 into 6bb938c90
Pull Request #1995: Move `Post` model from `misago.threads` to `misago.posts`

1884 of 1974 new or added lines in 149 files covered. (95.44%)

305 existing lines in 16 files now uncovered.

66716 of 69129 relevant lines covered (96.51%)

0.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.73
/misago/test/threads.py
1
from dataclasses import dataclass
1✔
2

3
import pytest
1✔
4
from django.utils.crypto import get_random_string
1✔
5

6
from ..attachments.models import Attachment
1✔
7
from ..categories.models import Category
1✔
8
from ..notifications.models import Notification, WatchedThread
1✔
9
from ..polls.models import Poll, PollVote
1✔
10
from ..posts.models import Post
1✔
11
from ..readtracker.models import ReadThread
1✔
12
from ..threads.models import Thread
1✔
13
from ..threadupdates.create import create_test_thread_update
1✔
14
from ..threadupdates.models import ThreadUpdate
1✔
15
from ..core.utils import slugify
1✔
16
from .utils import (
1✔
17
    FactoryTimestampArg,
18
    FactoryUserArg,
19
    factory_timestamp_arg,
20
    unpack_factory_user_arg,
21
)
22

23
__all__ = ["ThreadRelations", "thread_factory", "thread_relations_factory"]
1✔
24

25

26
@pytest.fixture
1✔
27
def thread_factory(post_factory):
1✔
28
    def _thread_factory(
1✔
29
        category: Category,
30
        *,
31
        title: str | None = None,
32
        replies: int = 0,
33
        has_events: bool = False,
34
        has_poll: bool = False,
35
        has_reported_posts: bool = False,
36
        has_open_reports: bool = False,
37
        has_unapproved_posts: bool = False,
38
        has_hidden_posts: bool = False,
39
        started_on: FactoryTimestampArg = True,
40
        last_post_on: FactoryTimestampArg = None,
41
        starter: FactoryUserArg = "Starter",
42
        last_poster: FactoryUserArg = None,
43
        weight: int = 0,
44
        is_unapproved: bool = False,
45
        is_hidden: bool = False,
46
        is_closed: bool = False,
47
    ):
48
        started_on = factory_timestamp_arg(started_on)
1✔
49
        if last_post_on is None:
1✔
50
            last_post_on = started_on
1✔
51
        else:
NEW
52
            last_post_on = factory_timestamp_arg(last_post_on)
×
53

54
        if not title:
1✔
55
            title = f"Thread {get_random_string(8)}"
1✔
56

57
        thread = Thread.objects.create(
1✔
58
            category=category,
59
            title=title,
60
            slug=slugify(title),
61
            replies=replies,
62
            has_events=has_events,
63
            has_poll=has_poll,
64
            has_reported_posts=has_reported_posts,
65
            has_open_reports=has_open_reports,
66
            has_unapproved_posts=has_unapproved_posts,
67
            has_hidden_posts=has_hidden_posts,
68
            started_on=started_on,
69
            last_post_on=last_post_on,
70
            weight=weight,
71
            is_unapproved=is_unapproved,
72
            is_hidden=is_hidden,
73
            is_closed=is_closed,
74
        )
75

76
        post = post_factory(
1✔
77
            thread,
78
            poster=starter,
79
            posted_at=started_on,
80
        )
81

82
        thread.first_post = post
1✔
83
        thread.last_post = post
1✔
84

85
        starter_obj, starter_name, starter_slug = unpack_factory_user_arg(starter)
1✔
86
        last_poster_obj, last_poster_name, last_poster_slug = unpack_factory_user_arg(
1✔
87
            last_poster or starter
88
        )
89

90
        thread.starter = starter_obj
1✔
91
        thread.starter_name = starter_name
1✔
92
        thread.starter_slug = starter_slug
1✔
93
        thread.last_poster = last_poster_obj
1✔
94
        thread.last_poster_name = last_poster_name
1✔
95
        thread.last_poster_slug = last_poster_slug
1✔
96

97
        thread.save()
1✔
98

99
        return thread
1✔
100

101
    return _thread_factory
1✔
102

103

104
@pytest.fixture
1✔
105
def thread_relations_factory(user, other_user):
1✔
106
    def _thread_relations_factory(thread: Thread) -> "ThreadRelations":
1✔
107
        post = thread.first_post
1✔
108

109
        attachment = Attachment.objects.create(
1✔
110
            category=thread.category,
111
            thread=thread,
112
            post=post,
113
            uploader_name="Anonymous",
114
            uploader_slug="anonymous",
115
            name="filename.txt",
116
            slug="filename-txt",
117
        )
118

119
        notification = Notification.objects.create(
1✔
120
            user=user,
121
            verb="TEST",
122
            actor=other_user,
123
            actor_name=other_user.username,
124
            category=thread.category,
125
            thread=thread,
126
            thread_title=thread.title,
127
            post=post,
128
        )
129

130
        poll = Poll.objects.create(
1✔
131
            category=thread.category,
132
            thread=thread,
133
            starter=user,
134
            starter_name=user.username,
135
            starter_slug=user.slug,
136
            question="...",
137
            choices=[],
138
        )
139

140
        poll_vote = PollVote.objects.create(
1✔
141
            category=thread.category,
142
            thread=thread,
143
            poll=poll,
144
            choice_id="aaaa",
145
            voter=user,
146
            voter_name=user.username,
147
            voter_slug=user.slug,
148
        )
149

150
        read_thread = ReadThread.objects.create(
1✔
151
            user=user,
152
            category=thread.category,
153
            thread=thread,
154
        )
155

156
        thread_update = create_test_thread_update(thread, other_user)
1✔
157

158
        watched_thread = WatchedThread.objects.create(
1✔
159
            user=user,
160
            category=thread.category,
161
            thread=thread,
162
        )
163

164
        return ThreadRelations(
1✔
165
            attachment=attachment,
166
            notification=notification,
167
            poll=poll,
168
            poll_vote=poll_vote,
169
            post=post,
170
            read_thread=read_thread,
171
            thread_update=thread_update,
172
            watched_thread=watched_thread,
173
        )
174

175
    return _thread_relations_factory
1✔
176

177

178
@dataclass(frozen=True)
1✔
179
class ThreadRelations:
1✔
180
    attachment: Attachment
1✔
181
    notification: Notification
1✔
182
    poll: Poll
1✔
183
    poll_vote: PollVote
1✔
184
    post: Post
1✔
185
    read_thread: ReadThread
1✔
186
    thread_update: ThreadUpdate
1✔
187
    watched_thread: WatchedThread
1✔
188

189
    def assert_category(self, category: Category):
1✔
190
        self.attachment.refresh_from_db()
1✔
191
        assert self.attachment.category_id == category.id
1✔
192

193
        self.notification.refresh_from_db()
1✔
194
        assert self.notification.category_id == category.id
1✔
195

196
        self.poll.refresh_from_db()
1✔
197
        assert self.poll.category_id == category.id
1✔
198

199
        self.poll_vote.refresh_from_db()
1✔
200
        assert self.poll_vote.category_id == category.id
1✔
201

202
        self.read_thread.refresh_from_db()
1✔
203
        assert self.read_thread.category_id == category.id
1✔
204

205
        self.post.refresh_from_db()
1✔
206
        assert self.post.category_id == category.id
1✔
207

208
        self.thread_update.refresh_from_db()
1✔
209
        assert self.thread_update.category_id == category.id
1✔
210

211
        self.watched_thread.refresh_from_db()
1✔
212
        assert self.watched_thread.category_id == category.id
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc