##// END OF EJS Templates
Defined encoding in the HTML. Fixed error 404 page on opening reply as thread. This fixes #46, #47
neko259 -
r86:938852d9 default
parent child Browse files
Show More
@@ -1,288 +1,288 b''
1 import os
1 import os
2 from random import random
2 from random import random
3 import re
3 import re
4 import time
4 import time
5 import math
5 import math
6
6
7 from django.db import models
7 from django.db import models
8 from django.http import Http404
8 from django.http import Http404
9 from django.utils import timezone
9 from django.utils import timezone
10 from markupfield.fields import MarkupField
10 from markupfield.fields import MarkupField
11
11
12 from neboard import settings
12 from neboard import settings
13 import thumbs
13 import thumbs
14
14
15 IMAGE_THUMB_SIZE = (200, 150)
15 IMAGE_THUMB_SIZE = (200, 150)
16
16
17 TITLE_MAX_LENGTH = 50
17 TITLE_MAX_LENGTH = 50
18
18
19 DEFAULT_MARKUP_TYPE = 'markdown'
19 DEFAULT_MARKUP_TYPE = 'markdown'
20
20
21 NO_PARENT = -1
21 NO_PARENT = -1
22 NO_IP = '0.0.0.0'
22 NO_IP = '0.0.0.0'
23 UNKNOWN_UA = ''
23 UNKNOWN_UA = ''
24 ALL_PAGES = -1
24 ALL_PAGES = -1
25 OPENING_POST_POPULARITY_WEIGHT = 2
25 OPENING_POST_POPULARITY_WEIGHT = 2
26 IMAGES_DIRECTORY = 'images/'
26 IMAGES_DIRECTORY = 'images/'
27 FILE_EXTENSION_DELIMITER = '.'
27 FILE_EXTENSION_DELIMITER = '.'
28
28
29 REGEX_PRETTY = re.compile(r'^\d(0)+$')
29 REGEX_PRETTY = re.compile(r'^\d(0)+$')
30 REGEX_SAME = re.compile(r'^(.)\1+$')
30 REGEX_SAME = re.compile(r'^(.)\1+$')
31
31
32
32
33 class PostManager(models.Manager):
33 class PostManager(models.Manager):
34 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
34 def create_post(self, title, text, image=None, parent_id=NO_PARENT,
35 ip=NO_IP, tags=None):
35 ip=NO_IP, tags=None):
36 post = self.create(title=title,
36 post = self.create(title=title,
37 text=text,
37 text=text,
38 pub_time=timezone.now(),
38 pub_time=timezone.now(),
39 parent=parent_id,
39 parent=parent_id,
40 image=image,
40 image=image,
41 poster_ip=ip,
41 poster_ip=ip,
42 poster_user_agent=UNKNOWN_UA,
42 poster_user_agent=UNKNOWN_UA,
43 last_edit_time=timezone.now())
43 last_edit_time=timezone.now())
44
44
45 if tags:
45 if tags:
46 map(post.tags.add, tags)
46 map(post.tags.add, tags)
47
47
48 if parent_id != NO_PARENT:
48 if parent_id != NO_PARENT:
49 self._bump_thread(parent_id)
49 self._bump_thread(parent_id)
50 else:
50 else:
51 self._delete_old_threads()
51 self._delete_old_threads()
52
52
53 return post
53 return post
54
54
55 def delete_post(self, post):
55 def delete_post(self, post):
56 children = self.filter(parent=post.id)
56 children = self.filter(parent=post.id)
57 for child in children:
57 for child in children:
58 self.delete_post(child)
58 self.delete_post(child)
59 post.delete()
59 post.delete()
60
60
61 def delete_posts_by_ip(self, ip):
61 def delete_posts_by_ip(self, ip):
62 posts = self.filter(poster_ip=ip)
62 posts = self.filter(poster_ip=ip)
63 for post in posts:
63 for post in posts:
64 self.delete_post(post)
64 self.delete_post(post)
65
65
66 def get_threads(self, tag=None, page=ALL_PAGES):
66 def get_threads(self, tag=None, page=ALL_PAGES):
67 if tag:
67 if tag:
68 threads = self.filter(parent=NO_PARENT, tags=tag)
68 threads = self.filter(parent=NO_PARENT, tags=tag)
69 else:
69 else:
70 threads = self.filter(parent=NO_PARENT)
70 threads = self.filter(parent=NO_PARENT)
71
71
72 if not threads:
72 if not threads:
73 raise Http404
73 raise Http404
74
74
75 threads = threads.order_by('-last_edit_time')
75 threads = threads.order_by('-last_edit_time')
76
76
77 if page != ALL_PAGES:
77 if page != ALL_PAGES:
78 thread_count = len(threads)
78 thread_count = len(threads)
79
79
80 if page < self.get_thread_page_count(tag=tag):
80 if page < self.get_thread_page_count(tag=tag):
81 start_thread = page * settings.THREADS_PER_PAGE
81 start_thread = page * settings.THREADS_PER_PAGE
82 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
82 end_thread = min(start_thread + settings.THREADS_PER_PAGE,
83 thread_count)
83 thread_count)
84 threads = threads[start_thread:end_thread]
84 threads = threads[start_thread:end_thread]
85
85
86 return threads
86 return threads
87
87
88 def get_thread(self, opening_post_id):
88 def get_thread(self, opening_post_id):
89 try:
89 try:
90 opening_post = self.get(id=opening_post_id)
90 opening_post = self.get(id=opening_post_id, parent=NO_PARENT)
91 except Post.DoesNotExist:
91 except Post.DoesNotExist:
92 raise Http404
92 raise Http404
93
93
94 if opening_post.parent == NO_PARENT:
94 if opening_post.parent == NO_PARENT:
95 replies = self.filter(parent=opening_post_id)
95 replies = self.filter(parent=opening_post_id)
96
96
97 thread = [opening_post]
97 thread = [opening_post]
98 thread.extend(replies)
98 thread.extend(replies)
99
99
100 return thread
100 return thread
101
101
102 def exists(self, post_id):
102 def exists(self, post_id):
103 posts = self.filter(id=post_id)
103 posts = self.filter(id=post_id)
104
104
105 return posts.count() > 0
105 return posts.count() > 0
106
106
107 def get_thread_page_count(self, tag=None):
107 def get_thread_page_count(self, tag=None):
108 if tag:
108 if tag:
109 threads = self.filter(parent=NO_PARENT, tags=tag)
109 threads = self.filter(parent=NO_PARENT, tags=tag)
110 else:
110 else:
111 threads = self.filter(parent=NO_PARENT)
111 threads = self.filter(parent=NO_PARENT)
112
112
113 return int(math.ceil(threads.count() / float(
113 return int(math.ceil(threads.count() / float(
114 settings.THREADS_PER_PAGE)))
114 settings.THREADS_PER_PAGE)))
115
115
116 def _delete_old_threads(self):
116 def _delete_old_threads(self):
117 """
117 """
118 Preserves maximum thread count. If there are too many threads,
118 Preserves maximum thread count. If there are too many threads,
119 delete the old ones.
119 delete the old ones.
120 """
120 """
121
121
122 # TODO Move old threads to the archive instead of deleting them.
122 # TODO Move old threads to the archive instead of deleting them.
123 # Maybe make some 'old' field in the model to indicate the thread
123 # Maybe make some 'old' field in the model to indicate the thread
124 # must not be shown and be able for replying.
124 # must not be shown and be able for replying.
125
125
126 threads = self.get_threads()
126 threads = self.get_threads()
127 thread_count = len(threads)
127 thread_count = len(threads)
128
128
129 if thread_count > settings.MAX_THREAD_COUNT:
129 if thread_count > settings.MAX_THREAD_COUNT:
130 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
130 num_threads_to_delete = thread_count - settings.MAX_THREAD_COUNT
131 old_threads = threads[thread_count - num_threads_to_delete:]
131 old_threads = threads[thread_count - num_threads_to_delete:]
132
132
133 for thread in old_threads:
133 for thread in old_threads:
134 self.delete_post(thread)
134 self.delete_post(thread)
135
135
136 def _bump_thread(self, thread_id):
136 def _bump_thread(self, thread_id):
137 thread = self.get(id=thread_id)
137 thread = self.get(id=thread_id)
138
138
139 if thread.can_bump():
139 if thread.can_bump():
140 thread.last_edit_time = timezone.now()
140 thread.last_edit_time = timezone.now()
141 thread.save()
141 thread.save()
142
142
143
143
144 class TagManager(models.Manager):
144 class TagManager(models.Manager):
145 def get_not_empty_tags(self):
145 def get_not_empty_tags(self):
146 all_tags = self.all().order_by('name')
146 all_tags = self.all().order_by('name')
147 tags = []
147 tags = []
148 for tag in all_tags:
148 for tag in all_tags:
149 if not tag.is_empty():
149 if not tag.is_empty():
150 tags.append(tag)
150 tags.append(tag)
151
151
152 return tags
152 return tags
153
153
154 def get_popular_tags(self):
154 def get_popular_tags(self):
155 all_tags = self.get_not_empty_tags()
155 all_tags = self.get_not_empty_tags()
156
156
157 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
157 sorted_tags = sorted(all_tags, key=lambda tag: tag.get_popularity(),
158 reverse=True)
158 reverse=True)
159
159
160 return sorted_tags[:settings.POPULAR_TAGS]
160 return sorted_tags[:settings.POPULAR_TAGS]
161
161
162
162
163 class Tag(models.Model):
163 class Tag(models.Model):
164 """
164 """
165 A tag is a text node assigned to the post. The tag serves as a board
165 A tag is a text node assigned to the post. The tag serves as a board
166 section. There can be multiple tags for each message
166 section. There can be multiple tags for each message
167 """
167 """
168
168
169 objects = TagManager()
169 objects = TagManager()
170
170
171 name = models.CharField(max_length=100)
171 name = models.CharField(max_length=100)
172 # TODO Connect the tag to its posts to check the number of threads for
172 # TODO Connect the tag to its posts to check the number of threads for
173 # the tag.
173 # the tag.
174
174
175 def __unicode__(self):
175 def __unicode__(self):
176 return self.name
176 return self.name
177
177
178 def is_empty(self):
178 def is_empty(self):
179 return self.get_post_count() == 0
179 return self.get_post_count() == 0
180
180
181 def get_post_count(self):
181 def get_post_count(self):
182 posts_with_tag = Post.objects.get_threads(tag=self)
182 posts_with_tag = Post.objects.get_threads(tag=self)
183 return posts_with_tag.count()
183 return posts_with_tag.count()
184
184
185 def get_popularity(self):
185 def get_popularity(self):
186 posts_with_tag = Post.objects.get_threads(tag=self)
186 posts_with_tag = Post.objects.get_threads(tag=self)
187 reply_count = 0
187 reply_count = 0
188 for post in posts_with_tag:
188 for post in posts_with_tag:
189 reply_count += post.get_reply_count()
189 reply_count += post.get_reply_count()
190 reply_count += OPENING_POST_POPULARITY_WEIGHT
190 reply_count += OPENING_POST_POPULARITY_WEIGHT
191
191
192 return reply_count
192 return reply_count
193
193
194
194
195 class Post(models.Model):
195 class Post(models.Model):
196 """A post is a message."""
196 """A post is a message."""
197
197
198 objects = PostManager()
198 objects = PostManager()
199
199
200 def _update_image_filename(self, filename):
200 def _update_image_filename(self, filename):
201 """Get unique image filename"""
201 """Get unique image filename"""
202
202
203 path = IMAGES_DIRECTORY
203 path = IMAGES_DIRECTORY
204 new_name = str(int(time.mktime(time.gmtime())))
204 new_name = str(int(time.mktime(time.gmtime())))
205 new_name += str(int(random() * 1000))
205 new_name += str(int(random() * 1000))
206 new_name += FILE_EXTENSION_DELIMITER
206 new_name += FILE_EXTENSION_DELIMITER
207 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
207 new_name += filename.split(FILE_EXTENSION_DELIMITER)[-1:][0]
208
208
209 return os.path.join(path, new_name)
209 return os.path.join(path, new_name)
210
210
211 title = models.CharField(max_length=TITLE_MAX_LENGTH)
211 title = models.CharField(max_length=TITLE_MAX_LENGTH)
212 pub_time = models.DateTimeField()
212 pub_time = models.DateTimeField()
213 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
213 text = MarkupField(default_markup_type=DEFAULT_MARKUP_TYPE,
214 escape_html=True)
214 escape_html=True)
215 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
215 image = thumbs.ImageWithThumbsField(upload_to=_update_image_filename,
216 blank=True, sizes=(IMAGE_THUMB_SIZE,))
216 blank=True, sizes=(IMAGE_THUMB_SIZE,))
217 poster_ip = models.IPAddressField()
217 poster_ip = models.IPAddressField()
218 poster_user_agent = models.TextField()
218 poster_user_agent = models.TextField()
219 parent = models.BigIntegerField()
219 parent = models.BigIntegerField()
220 tags = models.ManyToManyField(Tag)
220 tags = models.ManyToManyField(Tag)
221 last_edit_time = models.DateTimeField()
221 last_edit_time = models.DateTimeField()
222
222
223 def __unicode__(self):
223 def __unicode__(self):
224 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
224 return '#' + str(self.id) + ' ' + self.title + ' (' + self.text.raw + \
225 ')'
225 ')'
226
226
227 def _get_replies(self):
227 def _get_replies(self):
228 return Post.objects.filter(parent=self.id)
228 return Post.objects.filter(parent=self.id)
229
229
230 def get_reply_count(self):
230 def get_reply_count(self):
231 return self._get_replies().count()
231 return self._get_replies().count()
232
232
233 def get_images_count(self):
233 def get_images_count(self):
234 images_count = 1 if self.image else 0
234 images_count = 1 if self.image else 0
235 for reply in self._get_replies():
235 for reply in self._get_replies():
236 if reply.image:
236 if reply.image:
237 images_count += 1
237 images_count += 1
238
238
239 return images_count
239 return images_count
240
240
241 def get_gets_count(self):
241 def get_gets_count(self):
242 gets_count = 1 if self.is_get() else 0
242 gets_count = 1 if self.is_get() else 0
243 for reply in self._get_replies():
243 for reply in self._get_replies():
244 if reply.is_get():
244 if reply.is_get():
245 gets_count += 1
245 gets_count += 1
246
246
247 return gets_count
247 return gets_count
248
248
249 def is_get(self):
249 def is_get(self):
250 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
250 """If the post has pretty id (1, 1000, 77777), than it is called GET"""
251
251
252 first = self.id == 1
252 first = self.id == 1
253
253
254 id_str = str(self.id)
254 id_str = str(self.id)
255 pretty = REGEX_PRETTY.match(id_str)
255 pretty = REGEX_PRETTY.match(id_str)
256 same_digits = REGEX_SAME.match(id_str)
256 same_digits = REGEX_SAME.match(id_str)
257
257
258 return first or pretty or same_digits
258 return first or pretty or same_digits
259
259
260 def can_bump(self):
260 def can_bump(self):
261 """Check if the thread can be bumped by replying"""
261 """Check if the thread can be bumped by replying"""
262
262
263 replies_count = len(Post.objects.get_thread(self.id))
263 replies_count = len(Post.objects.get_thread(self.id))
264
264
265 return replies_count <= settings.MAX_POSTS_PER_THREAD
265 return replies_count <= settings.MAX_POSTS_PER_THREAD
266
266
267 def get_last_replies(self):
267 def get_last_replies(self):
268 if settings.LAST_REPLIES_COUNT > 0:
268 if settings.LAST_REPLIES_COUNT > 0:
269 reply_count = self.get_reply_count()
269 reply_count = self.get_reply_count()
270
270
271 if reply_count > 0:
271 if reply_count > 0:
272 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
272 reply_count_to_show = min(settings.LAST_REPLIES_COUNT,
273 reply_count)
273 reply_count)
274 last_replies = self._get_replies()[reply_count
274 last_replies = self._get_replies()[reply_count
275 - reply_count_to_show:]
275 - reply_count_to_show:]
276
276
277 return last_replies
277 return last_replies
278
278
279
279
280 class Admin(models.Model):
280 class Admin(models.Model):
281 """
281 """
282 Model for admin users
282 Model for admin users
283 """
283 """
284 name = models.CharField(max_length=100)
284 name = models.CharField(max_length=100)
285 password = models.CharField(max_length=100)
285 password = models.CharField(max_length=100)
286
286
287 def __unicode__(self):
287 def __unicode__(self):
288 return self.name + '/' + '*' * len(self.password)
288 return self.name + '/' + '*' * len(self.password)
@@ -1,44 +1,45 b''
1 {% load staticfiles %}
1 {% load staticfiles %}
2 {% load i18n %}
2 {% load i18n %}
3
3
4 <!DOCTYPE html>
4 <!DOCTYPE html>
5 <html>
5 <html>
6 <head>
6 <head>
7 <link rel="stylesheet" type="text/css"
7 <link rel="stylesheet" type="text/css"
8 href="{{ STATIC_URL }}css/jquery.fancybox.css" media="all"/>
8 href="{{ STATIC_URL }}css/jquery.fancybox.css" media="all"/>
9 <link rel="stylesheet" type="text/css"
9 <link rel="stylesheet" type="text/css"
10 href="{{ STATIC_URL }}css/{{ theme }}/base_page.css" media="all"/>
10 href="{{ STATIC_URL }}css/{{ theme }}/base_page.css" media="all"/>
11 <meta name="viewport" content="width=device-width, initial-scale=1"/>
11 <meta name="viewport" content="width=device-width, initial-scale=1"/>
12 <meta charset="utf-8"/>
12 {% block head %}{% endblock %}
13 {% block head %}{% endblock %}
13 </head>
14 </head>
14 <body>
15 <body>
15 <script src="{{ STATIC_URL }}js/jquery-2.0.1.min.js"></script>
16 <script src="{{ STATIC_URL }}js/jquery-2.0.1.min.js"></script>
16 <script src="{{ STATIC_URL }}js/jquery.fancybox.pack.js"></script>
17 <script src="{{ STATIC_URL }}js/jquery.fancybox.pack.js"></script>
17 <script src="{{ STATIC_URL }}js/main.js"></script>
18 <script src="{{ STATIC_URL }}js/main.js"></script>
18 <div id="admin_panel">
19 <div id="admin_panel">
19
20
20 {% if request.session.admin == True %}
21 {% if request.session.admin == True %}
21 Admin panel TODO: Need to implement <BR />
22 Admin panel TODO: Need to implement <BR />
22 {% endif %}
23 {% endif %}
23
24
24 </div>
25 </div>
25
26
26 <div class="navigation_panel">
27 <div class="navigation_panel">
27 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
28 <a class="link" href="{% url 'index' %}">{% trans "All threads" %}</a>
28 {% for tag in tags %}
29 {% for tag in tags %}
29 <a class="tag" href=" {% url 'tag' tag_name=tag.name %}">
30 <a class="tag" href=" {% url 'tag' tag_name=tag.name %}">
30 {{ tag.name }}</a>({{ tag.get_post_count }})
31 {{ tag.name }}</a>({{ tag.get_post_count }})
31 {% endfor %}
32 {% endfor %}
32 <a class="tag" href="{% url 'tags' %}">[...]</a>
33 <a class="tag" href="{% url 'tags' %}">[...]</a>
33 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
34 <a class="link" href="{% url 'settings' %}">{% trans 'Settings' %}</a>
34 </div>
35 </div>
35
36
36 {% block content %}{% endblock %}
37 {% block content %}{% endblock %}
37
38
38 <div class="navigation_panel">
39 <div class="navigation_panel">
39 {% block metapanel %}{% endblock %}
40 {% block metapanel %}{% endblock %}
40 <a class="link" href="#top">{% trans 'Up' %}</a>
41 <a class="link" href="#top">{% trans 'Up' %}</a>
41 </div>
42 </div>
42
43
43 </body>
44 </body>
44 </html> No newline at end of file
45 </html>
@@ -1,210 +1,218 b''
1 # coding=utf-8
1 # coding=utf-8
2 from django.utils.unittest import TestCase
2 from django.utils.unittest import TestCase
3 from django.test.client import Client
3 from django.test.client import Client
4
4
5 import boards
5 import boards
6
6
7 from boards.models import Post, Admin, Tag
7 from boards.models import Post, Admin, Tag
8 from neboard import settings
8 from neboard import settings
9
9
10 TEST_TEXT = 'test text'
10 TEST_TEXT = 'test text'
11
11
12 NEW_THREAD_PAGE = '/'
12 NEW_THREAD_PAGE = '/'
13 THREAD_PAGE_ONE = '/thread/1/'
13 THREAD_PAGE_ONE = '/thread/1/'
14 THREAD_PAGE = '/thread/'
14 THREAD_PAGE = '/thread/'
15 TAG_PAGE = '/tag/'
15 TAG_PAGE = '/tag/'
16 HTTP_CODE_REDIRECT = 302
16 HTTP_CODE_REDIRECT = 302
17 HTTP_CODE_OK = 200
17 HTTP_CODE_OK = 200
18 HTTP_CODE_NOT_FOUND = 404
18 HTTP_CODE_NOT_FOUND = 404
19
19
20
20
21 class BoardTests(TestCase):
21 class BoardTests(TestCase):
22 def _create_post(self):
22 def _create_post(self):
23 return Post.objects.create_post(title='title',
23 return Post.objects.create_post(title='title',
24 text='text')
24 text='text')
25
25
26 def test_post_add(self):
26 def test_post_add(self):
27 post = self._create_post()
27 post = self._create_post()
28
28
29 self.assertIsNotNone(post)
29 self.assertIsNotNone(post)
30 self.assertEqual(boards.models.NO_PARENT, post.parent)
30 self.assertEqual(boards.models.NO_PARENT, post.parent)
31
31
32 def test_delete_post(self):
32 def test_delete_post(self):
33 post = self._create_post()
33 post = self._create_post()
34 post_id = post.id
34 post_id = post.id
35
35
36 Post.objects.delete_post(post)
36 Post.objects.delete_post(post)
37
37
38 self.assertFalse(Post.objects.exists(post_id))
38 self.assertFalse(Post.objects.exists(post_id))
39
39
40 def test_delete_posts_by_ip(self):
40 def test_delete_posts_by_ip(self):
41 post = self._create_post()
41 post = self._create_post()
42 post_id = post.id
42 post_id = post.id
43
43
44 Post.objects.delete_posts_by_ip('0.0.0.0')
44 Post.objects.delete_posts_by_ip('0.0.0.0')
45
45
46 self.assertFalse(Post.objects.exists(post_id))
46 self.assertFalse(Post.objects.exists(post_id))
47
47
48 # Authentication tests
48 # Authentication tests
49
49
50 def _create_test_user(self):
50 def _create_test_user(self):
51 admin = Admin(name='test_username12313584353165',
51 admin = Admin(name='test_username12313584353165',
52 password='test_userpassword135135512')
52 password='test_userpassword135135512')
53
53
54 admin.save()
54 admin.save()
55 return admin
55 return admin
56
56
57 def test_admin_login(self):
57 def test_admin_login(self):
58 client = Client()
58 client = Client()
59
59
60 self.assertFalse('admin' in client.session)
60 self.assertFalse('admin' in client.session)
61
61
62 admin = self._create_test_user()
62 admin = self._create_test_user()
63
63
64 response = client.post('/login',
64 response = client.post('/login',
65 {'name': admin.name, 'password': admin.password})
65 {'name': admin.name, 'password': admin.password})
66
66
67 # it means that login passed and user are redirected to another page
67 # it means that login passed and user are redirected to another page
68 self.assertEqual(302, response.status_code)
68 self.assertEqual(302, response.status_code)
69
69
70 self.assertTrue('admin' in client.session)
70 self.assertTrue('admin' in client.session)
71 self.assertTrue(client.session['admin'])
71 self.assertTrue(client.session['admin'])
72
72
73 admin.delete()
73 admin.delete()
74
74
75 wrong_name = 'sd2f1s3d21fs3d21f'
75 wrong_name = 'sd2f1s3d21fs3d21f'
76 wrong_password = 'sd2f1s3d21fs3d21fsdfsd'
76 wrong_password = 'sd2f1s3d21fs3d21fsdfsd'
77
77
78 client.post('/login', {'name': wrong_name, 'password': wrong_password})
78 client.post('/login', {'name': wrong_name, 'password': wrong_password})
79 self.assertFalse(client.session['admin'])
79 self.assertFalse(client.session['admin'])
80
80
81 def test_admin_logout(self):
81 def test_admin_logout(self):
82 client = Client()
82 client = Client()
83
83
84 self.assertFalse('admin' in client.session)
84 self.assertFalse('admin' in client.session)
85
85
86 admin = self._create_test_user()
86 admin = self._create_test_user()
87
87
88 client.post('/login',
88 client.post('/login',
89 {'name': admin.name, 'password': admin.password})
89 {'name': admin.name, 'password': admin.password})
90
90
91 self.assertTrue(client.session['admin'])
91 self.assertTrue(client.session['admin'])
92
92
93 client.get('/logout')
93 client.get('/logout')
94
94
95 self.assertFalse(client.session['admin'])
95 self.assertFalse(client.session['admin'])
96
96
97 admin.delete()
97 admin.delete()
98
98
99 def test_get_thread(self):
99 def test_get_thread(self):
100 opening_post = self._create_post()
100 opening_post = self._create_post()
101 op_id = opening_post.id
101 op_id = opening_post.id
102
102
103 for i in range(0, 2):
103 for i in range(0, 2):
104 Post.objects.create_post('title', 'text',
104 Post.objects.create_post('title', 'text',
105 parent_id=op_id)
105 parent_id=op_id)
106
106
107 thread = Post.objects.get_thread(op_id)
107 thread = Post.objects.get_thread(op_id)
108
108
109 self.assertEqual(3, len(thread))
109 self.assertEqual(3, len(thread))
110
110
111 def test_create_post_with_tag(self):
111 def test_create_post_with_tag(self):
112 tag = Tag.objects.create(name='test_tag')
112 tag = Tag.objects.create(name='test_tag')
113 post = Post.objects.create_post(title='title', text='text', tags=[tag])
113 post = Post.objects.create_post(title='title', text='text', tags=[tag])
114 self.assertIsNotNone(post)
114 self.assertIsNotNone(post)
115
115
116 def test_thread_max_count(self):
116 def test_thread_max_count(self):
117 for i in range(settings.MAX_THREAD_COUNT + 1):
117 for i in range(settings.MAX_THREAD_COUNT + 1):
118 self._create_post()
118 self._create_post()
119
119
120 self.assertEqual(settings.MAX_THREAD_COUNT,
120 self.assertEqual(settings.MAX_THREAD_COUNT,
121 len(Post.objects.get_threads()))
121 len(Post.objects.get_threads()))
122
122
123 def test_get(self):
123 def test_get(self):
124 """Test if the get computes properly"""
124 """Test if the get computes properly"""
125
125
126 post = self._create_post()
126 post = self._create_post()
127
127
128 self.assertTrue(post.is_get())
128 self.assertTrue(post.is_get())
129
129
130 def test_pages(self):
130 def test_pages(self):
131 """Test that the thread list is properly split into pages"""
131 """Test that the thread list is properly split into pages"""
132
132
133 for i in range(settings.MAX_THREAD_COUNT):
133 for i in range(settings.MAX_THREAD_COUNT):
134 self._create_post()
134 self._create_post()
135
135
136 all_threads = Post.objects.get_threads()
136 all_threads = Post.objects.get_threads()
137
137
138 posts_in_second_page = Post.objects.get_threads(page=1)
138 posts_in_second_page = Post.objects.get_threads(page=1)
139 first_post = posts_in_second_page[0]
139 first_post = posts_in_second_page[0]
140
140
141 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
141 self.assertEqual(all_threads[settings.THREADS_PER_PAGE].id,
142 first_post.id)
142 first_post.id)
143
143
144 def test_post_validation(self):
144 def test_post_validation(self):
145 """Test the validation of the post form"""
145 """Test the validation of the post form"""
146
146
147 Post.objects.all().delete()
147 Post.objects.all().delete()
148
148
149 client = Client()
149 client = Client()
150
150
151 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
151 valid_tags = u'tag1 tag_2 Ρ‚Π΅Π³_3'
152 invalid_tags = u'$%_356 ---'
152 invalid_tags = u'$%_356 ---'
153
153
154 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
154 response = client.post(NEW_THREAD_PAGE, {'title': 'test title',
155 'text': TEST_TEXT,
155 'text': TEST_TEXT,
156 'tags': valid_tags})
156 'tags': valid_tags})
157 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
157 self.assertEqual(response.status_code, HTTP_CODE_REDIRECT,
158 msg='Posting new message failed: got code ' +
158 msg='Posting new message failed: got code ' +
159 str(response.status_code))
159 str(response.status_code))
160
160
161 self.assertEqual(1, Post.objects.count(),
161 self.assertEqual(1, Post.objects.count(),
162 msg='No posts were created')
162 msg='No posts were created')
163
163
164 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
164 client.post(NEW_THREAD_PAGE, {'text': TEST_TEXT,
165 'tags': invalid_tags})
165 'tags': invalid_tags})
166 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
166 self.assertEqual(1, Post.objects.count(), msg='The validation passed '
167 'where it should fail')
167 'where it should fail')
168
168
169 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
169 response = client.post(THREAD_PAGE_ONE, {'text': TEST_TEXT,
170 'tags': valid_tags})
170 'tags': valid_tags})
171 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
171 self.assertEqual(HTTP_CODE_REDIRECT, response.status_code,
172 msg=u'Posting new message failed: got code ' +
172 msg=u'Posting new message failed: got code ' +
173 str(response.status_code))
173 str(response.status_code))
174
174
175 self.assertEqual(2, Post.objects.count(),
175 self.assertEqual(2, Post.objects.count(),
176 msg=u'No posts were created')
176 msg=u'No posts were created')
177
177
178 def test_404(self):
178 def test_404(self):
179 """Test receiving error 404 when opening a non-existent page"""
179 """Test receiving error 404 when opening a non-existent page"""
180
180
181 Post.objects.all().delete()
181 Post.objects.all().delete()
182 Tag.objects.all().delete()
182 Tag.objects.all().delete()
183
183
184 tag_name = u'test_tag'
184 tag_name = u'test_tag'
185 tags, = [Tag.objects.get_or_create(name=tag_name)]
185 tags, = [Tag.objects.get_or_create(name=tag_name)]
186 client = Client()
186 client = Client()
187
187
188 Post.objects.create_post('title', TEST_TEXT, tags=tags)
188 Post.objects.create_post('title', TEST_TEXT, tags=tags)
189
189
190 existing_post_id = Post.objects.all()[0].id
190 existing_post_id = Post.objects.all()[0].id
191 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
191 response_existing = client.get(THREAD_PAGE + str(existing_post_id) +
192 '/')
192 '/')
193 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
193 self.assertEqual(HTTP_CODE_OK, response_existing.status_code,
194 u'Cannot open existing thread')
194 u'Cannot open existing thread')
195
195
196 response_not_existing = client.get(THREAD_PAGE + str(
196 response_not_existing = client.get(THREAD_PAGE + str(
197 existing_post_id + 1) + '/')
197 existing_post_id + 1) + '/')
198 self.assertEqual(HTTP_CODE_NOT_FOUND,
198 self.assertEqual(HTTP_CODE_NOT_FOUND,
199 response_not_existing.status_code,
199 response_not_existing.status_code,
200 u'Not existing thread is opened')
200 u'Not existing thread is opened')
201
201
202 response_existing = client.get(TAG_PAGE + tag_name + '/')
202 response_existing = client.get(TAG_PAGE + tag_name + '/')
203 self.assertEqual(HTTP_CODE_OK,
203 self.assertEqual(HTTP_CODE_OK,
204 response_existing.status_code,
204 response_existing.status_code,
205 u'Cannot open existing tag')
205 u'Cannot open existing tag')
206
206
207 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
207 response_not_existing = client.get(TAG_PAGE + u'not_tag' + '/')
208 self.assertEqual(HTTP_CODE_NOT_FOUND,
208 self.assertEqual(HTTP_CODE_NOT_FOUND,
209 response_not_existing.status_code,
209 response_not_existing.status_code,
210 u'Not existing tag is opened') No newline at end of file
210 u'Not existing tag is opened')
211
212 reply_id = Post.objects.create_post('', TEST_TEXT,
213 parent_id=existing_post_id)
214 response_not_existing = client.get(THREAD_PAGE + str(
215 reply_id) + '/')
216 self.assertEqual(HTTP_CODE_NOT_FOUND,
217 response_not_existing.status_code,
218 u'Not existing thread is opened')
General Comments 0
You need to be logged in to leave comments. Login now