1
|
|
|
from datetime import timedelta |
2
|
|
|
|
3
|
|
|
from django.conf import settings |
4
|
|
|
from django.contrib.auth import get_user_model |
5
|
|
|
from django.core.urlresolvers import reverse |
6
|
|
|
from django.test import TestCase |
7
|
|
|
from django.utils import timezone |
8
|
|
|
|
9
|
|
|
from simple_forums import models |
10
|
|
|
from simple_forums.tests.testing_utils import ( |
11
|
|
|
create_message, |
12
|
|
|
create_thread, |
13
|
|
|
create_topic,) |
14
|
|
|
from simple_forums.utils import thread_detail_url, thread_list_url |
15
|
|
|
|
16
|
|
|
|
17
|
|
|
class AuthenticationTestCase(TestCase): |
18
|
|
|
""" Allows for easy authentication. """ |
19
|
|
|
USERNAME = 'test' |
20
|
|
|
PASSWORD = 'test' |
21
|
|
|
|
22
|
|
|
def setUp(self, *args, **kwargs): |
23
|
|
|
""" Create a user for authentication. """ |
24
|
|
|
self.user = get_user_model().objects.create_user( |
25
|
|
|
username=self.USERNAME, |
26
|
|
|
password=self.PASSWORD) |
27
|
|
|
|
28
|
|
|
return super(AuthenticationTestCase, self).setUp(*args, **kwargs) |
29
|
|
|
|
30
|
|
|
def assertLoginRedirect(self, response, next_url): |
31
|
|
|
""" Assert that the response redirects to the login url """ |
32
|
|
|
login_url = '%s?next=%s' % (settings.LOGIN_URL, next_url) |
33
|
|
|
self.assertRedirects(response, login_url) |
34
|
|
|
|
35
|
|
|
def login(self): |
36
|
|
|
""" Log in the test client """ |
37
|
|
|
self.client.login( |
38
|
|
|
username=self.USERNAME, |
39
|
|
|
password=self.PASSWORD) |
40
|
|
|
|
41
|
|
|
|
42
|
|
|
class TestThreadCreateView(AuthenticationTestCase): |
43
|
|
|
""" Tests for ThreadCreateView """ |
44
|
|
|
|
45
|
|
|
URL = reverse('thread-create') |
46
|
|
|
|
47
|
|
|
def test_empty_post(self): |
48
|
|
|
""" Test submitting empty form. |
49
|
|
|
|
50
|
|
|
If an empty form is submitted, the user should be redirected |
51
|
|
|
back to the thread creation form and errors should be shown. |
52
|
|
|
""" |
53
|
|
|
self.login() |
54
|
|
|
|
55
|
|
|
response = self.client.post(self.URL, {}) |
56
|
|
|
|
57
|
|
|
form = response.context['form'] |
58
|
|
|
|
59
|
|
|
self.assertEqual(200, response.status_code) |
60
|
|
|
self.assertTrue(form.is_bound) |
61
|
|
|
self.assertFalse(form.is_valid()) |
62
|
|
|
|
63
|
|
|
def test_get(self): |
64
|
|
|
""" Test submitting a GET request to the view. |
65
|
|
|
|
66
|
|
|
A GET request to this view should display a form for creating a |
67
|
|
|
new thread. |
68
|
|
|
""" |
69
|
|
|
self.login() |
70
|
|
|
|
71
|
|
|
response = self.client.get(self.URL) |
72
|
|
|
|
73
|
|
|
form = response.context['form'] |
74
|
|
|
|
75
|
|
|
self.assertEqual(200, response.status_code) |
76
|
|
|
self.assertFalse(form.is_bound) |
77
|
|
|
|
78
|
|
|
def test_not_authenticated_get(self): |
79
|
|
|
""" Test unauthenticated user submitting a GET request. |
80
|
|
|
|
81
|
|
|
If a GET request is made by a user who is not authenticated, |
82
|
|
|
then they should be redirected to the login page. |
83
|
|
|
""" |
84
|
|
|
response = self.client.get(self.URL) |
85
|
|
|
|
86
|
|
|
self.assertLoginRedirect(response, self.URL) |
87
|
|
|
|
88
|
|
|
def test_not_authenticated_post(self): |
89
|
|
|
""" Test unauthenticated user submitting a POST request. |
90
|
|
|
|
91
|
|
|
If a POST request is made by a user who is not authenticated, |
92
|
|
|
then they should be redirected to the login page. |
93
|
|
|
""" |
94
|
|
|
response = self.client.post(self.URL, {}) |
95
|
|
|
|
96
|
|
|
self.assertLoginRedirect(response, self.URL) |
97
|
|
|
|
98
|
|
|
def test_valid_form(self): |
99
|
|
|
""" Test authenticated user submitting a valid form. |
100
|
|
|
|
101
|
|
|
If an authenticated user submits a valid form, then a new |
102
|
|
|
thread should be created. |
103
|
|
|
""" |
104
|
|
|
self.login() |
105
|
|
|
|
106
|
|
|
topic = create_topic() |
107
|
|
|
data = { |
108
|
|
|
'topic': '%d' % topic.pk, |
109
|
|
|
'title': 'Test Thread Title', |
110
|
|
|
'body': 'Test thread body', |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
response = self.client.post(self.URL, data) |
114
|
|
|
|
115
|
|
|
thread = models.Thread.objects.get() |
116
|
|
|
message = thread.message_set.get() |
117
|
|
|
|
118
|
|
|
self.assertRedirects(response, thread_detail_url(thread=thread)) |
119
|
|
|
self.assertEqual(data['title'], thread.title) |
120
|
|
|
self.assertEqual(data['body'], message.body) |
121
|
|
|
|
122
|
|
|
|
123
|
|
|
class TestThreadDetailView(AuthenticationTestCase): |
124
|
|
|
""" Tests for ThreadDetailView """ |
125
|
|
|
|
126
|
|
|
def test_no_messages(self): |
127
|
|
|
""" Test the view when the given thread has no messages. |
128
|
|
|
|
129
|
|
|
If the thread has no messages, the response should contain a |
130
|
|
|
note to the user that there are no messages for the current |
131
|
|
|
thread. |
132
|
|
|
|
133
|
|
|
In practice, this should never occur because when threads are |
134
|
|
|
created, there should always be an initial message. |
135
|
|
|
""" |
136
|
|
|
thread = create_thread() |
137
|
|
|
|
138
|
|
|
url = thread_detail_url(thread=thread) |
139
|
|
|
response = self.client.get(url) |
140
|
|
|
|
141
|
|
|
no_replies_message = "There are no replies to this thread" |
142
|
|
|
|
143
|
|
|
self.assertEqual(200, response.status_code) |
144
|
|
|
self.assertEqual(thread, response.context['thread']) |
145
|
|
|
self.assertContains(response, no_replies_message) |
146
|
|
|
|
147
|
|
|
def test_reply(self): |
148
|
|
|
""" Test submitting a valid reply. |
149
|
|
|
|
150
|
|
|
If a valid form is submitted, a new message should be created |
151
|
|
|
on the current thread. |
152
|
|
|
""" |
153
|
|
|
self.login() |
154
|
|
|
|
155
|
|
|
thread = create_thread() |
156
|
|
|
data = { |
157
|
|
|
'body': 'Test body text.', |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
url = thread_detail_url(thread=thread) |
161
|
|
|
response = self.client.post(url, data) |
162
|
|
|
|
163
|
|
|
message = thread.message_set.get() |
164
|
|
|
|
165
|
|
|
self.assertRedirects(response, url) |
166
|
|
|
self.assertEqual(1, models.Message.objects.count()) |
167
|
|
|
self.assertEqual(self.user, message.user) |
168
|
|
|
self.assertEqual(data['body'], message.body) |
169
|
|
|
|
170
|
|
|
def test_reply_unauthenticated(self): |
171
|
|
|
""" Test replying while unauthenticated. |
172
|
|
|
|
173
|
|
|
If an unauthenticated user tries to reply to a thread, an error |
174
|
|
|
should be shown. |
175
|
|
|
""" |
176
|
|
|
thread = create_thread() |
177
|
|
|
data = { |
178
|
|
|
'body': 'Test body text.', |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
url = thread_detail_url(thread=thread) |
182
|
|
|
response = self.client.post(url, data) |
183
|
|
|
|
184
|
|
|
self.assertEqual(403, response.status_code) |
185
|
|
|
self.assertEqual(0, models.Message.objects.count()) |
186
|
|
|
|
187
|
|
|
def test_reply_errors(self): |
188
|
|
|
""" Test submitting an invalid reply form. |
189
|
|
|
|
190
|
|
|
If an invalid reply is submitted, the reply form should be |
191
|
|
|
displayed with errors. |
192
|
|
|
""" |
193
|
|
|
self.login() |
194
|
|
|
|
195
|
|
|
thread = create_thread() |
196
|
|
|
|
197
|
|
|
url = thread_detail_url(thread=thread) |
198
|
|
|
response = self.client.post(url, {}) |
199
|
|
|
|
200
|
|
|
expected_errors = { |
201
|
|
|
'body': ['This field is required.'], |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
self.assertEqual(200, response.status_code) |
205
|
|
|
self.assertTrue(response.context['reply_form'].is_bound) |
206
|
|
|
self.assertEqual( |
207
|
|
|
expected_errors, |
208
|
|
|
response.context['reply_form'].errors) |
209
|
|
|
|
210
|
|
|
def test_reply_form(self): |
211
|
|
|
""" Test presence of a reply form. |
212
|
|
|
|
213
|
|
|
If a user is logged in, there should be a reply form. |
214
|
|
|
""" |
215
|
|
|
self.login() |
216
|
|
|
|
217
|
|
|
thread = create_thread() |
218
|
|
|
|
219
|
|
|
url = thread_detail_url(thread=thread) |
220
|
|
|
response = self.client.get(url) |
221
|
|
|
|
222
|
|
|
self.assertEqual(200, response.status_code) |
223
|
|
|
self.assertFalse(response.context['reply_form'].is_bound) |
224
|
|
|
|
225
|
|
|
def test_reply_form_unauthenticated(self): |
226
|
|
|
""" Test presence of reply form for unauthenticated users. |
227
|
|
|
|
228
|
|
|
If a user isn't logged in, there should be no reply form. |
229
|
|
|
""" |
230
|
|
|
thread = create_thread() |
231
|
|
|
|
232
|
|
|
url = thread_detail_url(thread=thread) |
233
|
|
|
response = self.client.get(url) |
234
|
|
|
|
235
|
|
|
self.assertEqual(200, response.status_code) |
236
|
|
|
self.assertTrue('reply_form' not in response.context) |
237
|
|
|
|
238
|
|
|
def test_with_message(self): |
239
|
|
|
""" Test the view when the given thread has a message. |
240
|
|
|
|
241
|
|
|
If the thread has a message, it should be displayed following |
242
|
|
|
the thread's title. |
243
|
|
|
""" |
244
|
|
|
thread = create_thread() |
245
|
|
|
message = create_message(user=self.user, thread=thread) |
246
|
|
|
|
247
|
|
|
url = thread_detail_url(thread=thread) |
248
|
|
|
response = self.client.get(url) |
249
|
|
|
|
250
|
|
|
self.assertEqual(200, response.status_code) |
251
|
|
|
self.assertContains(response, message.body) |
252
|
|
|
|
253
|
|
|
|
254
|
|
|
class TestThreadListView(TestCase): |
255
|
|
|
""" Tests for ThreadListView """ |
256
|
|
|
|
257
|
|
|
def setUp(self): |
258
|
|
|
""" Create topic for testing """ |
259
|
|
|
self.topic = create_topic() |
260
|
|
|
|
261
|
|
|
def test_no_threads(self): |
262
|
|
|
""" Test the view when there are no threads. |
263
|
|
|
|
264
|
|
|
If there are no threads, then the view should display a message |
265
|
|
|
that there are no threads. |
266
|
|
|
""" |
267
|
|
|
url = thread_list_url(topic=self.topic) |
268
|
|
|
response = self.client.get(url) |
269
|
|
|
|
270
|
|
|
empty_message = "No threads found" |
271
|
|
|
|
272
|
|
|
self.assertEqual(200, response.status_code) |
273
|
|
|
self.assertQuerysetEqual( |
274
|
|
|
response.context['thread_list'], |
275
|
|
|
[]) |
276
|
|
|
self.assertContains(response, empty_message) |
277
|
|
|
|
278
|
|
|
def test_thread(self): |
279
|
|
|
""" Test view when there is a thread. |
280
|
|
|
|
281
|
|
|
If there is a thread, then the view should show the thread's |
282
|
|
|
name. |
283
|
|
|
""" |
284
|
|
|
thread = create_thread(topic=self.topic) |
285
|
|
|
|
286
|
|
|
url = thread_list_url(topic=self.topic) |
287
|
|
|
response = self.client.get(url) |
288
|
|
|
|
289
|
|
|
detail_url = thread_detail_url(thread=thread) |
290
|
|
|
href_text = 'href="%s"' % detail_url |
291
|
|
|
|
292
|
|
|
self.assertEqual(200, response.status_code) |
293
|
|
|
self.assertQuerysetEqual( |
294
|
|
|
response.context['thread_list'], |
295
|
|
|
['<Thread: %s>' % thread.title]) |
296
|
|
|
self.assertContains(response, thread.title) |
297
|
|
|
self.assertContains(response, href_text) |
298
|
|
|
|
299
|
|
|
def test_thread_for_different_topic(self): |
300
|
|
|
""" Test view when there is a thread for a different topic. |
301
|
|
|
|
302
|
|
|
If a thread is associated with a different topic, it should not |
303
|
|
|
be displayed. |
304
|
|
|
""" |
305
|
|
|
thread = create_thread(topic=self.topic) |
306
|
|
|
create_thread(title="I shouldn't be included") |
307
|
|
|
|
308
|
|
|
url = thread_list_url(topic=self.topic) |
309
|
|
|
response = self.client.get(url) |
310
|
|
|
|
311
|
|
|
self.assertEqual(200, response.status_code) |
312
|
|
|
self.assertQuerysetEqual( |
313
|
|
|
response.context['thread_list'], |
314
|
|
|
['<Thread: %s>' % thread]) |
315
|
|
|
|
316
|
|
|
def test_threads(self): |
317
|
|
|
""" Test view with multiple threads. |
318
|
|
|
|
319
|
|
|
If there are multiple threads, they should be ordered by the |
320
|
|
|
date of their last activity. |
321
|
|
|
""" |
322
|
|
|
past = timezone.now() - timedelta(days=1) |
323
|
|
|
thread1 = create_thread( |
324
|
|
|
topic=self.topic, |
325
|
|
|
title='Test Thread 1', |
326
|
|
|
time_created=past) |
327
|
|
|
thread2 = create_thread( |
328
|
|
|
topic=self.topic, |
329
|
|
|
title='Test Thread 2') |
330
|
|
|
|
331
|
|
|
url = thread_list_url(topic=self.topic) |
332
|
|
|
response = self.client.get(url) |
333
|
|
|
|
334
|
|
|
self.assertEqual(200, response.status_code) |
335
|
|
|
self.assertQuerysetEqual( |
336
|
|
|
response.context['thread_list'], |
337
|
|
|
['<Thread: %s>' % thread2, '<Thread: %s>' % thread1]) |
338
|
|
|
|
339
|
|
|
def test_topic_context(self): |
340
|
|
|
""" Test passing the topic as a context variable. |
341
|
|
|
|
342
|
|
|
This view should have the parent topic as a context variable. |
343
|
|
|
""" |
344
|
|
|
url = thread_list_url(topic=self.topic) |
345
|
|
|
response = self.client.get(url) |
346
|
|
|
|
347
|
|
|
self.assertEqual(200, response.status_code) |
348
|
|
|
self.assertEqual(self.topic, response.context['topic']) |
349
|
|
|
|
350
|
|
|
def test_sticky_thread(self): |
351
|
|
|
""" Test view when there is a sticky thread. |
352
|
|
|
|
353
|
|
|
If there is a sticky thread, it should be in a context variable |
354
|
|
|
for the sticky threads, and not in the one for normal threads. |
355
|
|
|
""" |
356
|
|
|
thread = create_thread(topic=self.topic) |
357
|
|
|
sticky_thread = create_thread( |
358
|
|
|
topic=self.topic, |
359
|
|
|
title='Sticky Thread', |
360
|
|
|
sticky=True) |
361
|
|
|
|
362
|
|
|
url = thread_list_url(topic=self.topic) |
363
|
|
|
response = self.client.get(url) |
364
|
|
|
|
365
|
|
|
self.assertEqual(200, response.status_code) |
366
|
|
|
self.assertQuerysetEqual( |
367
|
|
|
response.context['thread_list'], |
368
|
|
|
['<Thread: %s>' % thread]) |
369
|
|
|
self.assertQuerysetEqual( |
370
|
|
|
response.context['sticky_thread_list'], |
371
|
|
|
['<Thread: %s>' % sticky_thread]) |
372
|
|
|
|
373
|
|
|
|
374
|
|
|
class TestTopicListView(TestCase): |
375
|
|
|
""" Tests for TopicListView """ |
376
|
|
|
|
377
|
|
|
URL = reverse('topic-list') |
378
|
|
|
|
379
|
|
|
def test_no_topics(self): |
380
|
|
|
""" Test the view when there are no topics. |
381
|
|
|
|
382
|
|
|
If there are no topics, a message should be displayed to |
383
|
|
|
inform the user that there are no topics. |
384
|
|
|
""" |
385
|
|
|
response = self.client.get(self.URL) |
386
|
|
|
|
387
|
|
|
message = 'No topics found' |
388
|
|
|
|
389
|
|
|
self.assertEqual(200, response.status_code) |
390
|
|
|
self.assertQuerysetEqual( |
391
|
|
|
response.context['topic_list'], |
392
|
|
|
[]) |
393
|
|
|
self.assertContains(response, message) |
394
|
|
|
|
395
|
|
|
def test_topic(self): |
396
|
|
|
""" Test the view when there is a topic. |
397
|
|
|
|
398
|
|
|
If there is a topic, it should be listed with its title and |
399
|
|
|
description. |
400
|
|
|
""" |
401
|
|
|
topic = create_topic() |
402
|
|
|
|
403
|
|
|
response = self.client.get(self.URL) |
404
|
|
|
|
405
|
|
|
topic_url = thread_list_url(topic=topic) |
406
|
|
|
link_href = 'href="%s"' % topic_url |
407
|
|
|
|
408
|
|
|
self.assertEqual(200, response.status_code) |
409
|
|
|
self.assertQuerysetEqual( |
410
|
|
|
response.context['topic_list'], |
411
|
|
|
['<Topic: %s>' % topic]) |
412
|
|
|
self.assertContains(response, topic.title) |
413
|
|
|
self.assertContains(response, topic.description) |
414
|
|
|
self.assertContains(response, link_href) |
415
|
|
|
|