summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-07-31 21:10:59 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-07-31 21:10:59 -0400
commitbdbbe34c030aad6aa72a925a7fae3037b55bb7d3 (patch)
treec333425f7315070cb165263bbfe1a6b12f8b29c1
parentabb6ed03bac2464e0ca0e7473b1351d8c3c73db6 (diff)
downloadaskbot-bdbbe34c030aad6aa72a925a7fae3037b55bb7d3.tar.gz
askbot-bdbbe34c030aad6aa72a925a7fae3037b55bb7d3.tar.bz2
askbot-bdbbe34c030aad6aa72a925a7fae3037b55bb7d3.zip
added permission tests for voting, split tests.py into several files
-rw-r--r--askbot/auth.py47
-rw-r--r--askbot/conf/minimum_reputation.py9
-rw-r--r--askbot/models/__init__.py473
-rw-r--r--askbot/models/content.py3
-rw-r--r--askbot/models/meta.py3
-rw-r--r--askbot/models/signals.py2
-rw-r--r--askbot/templatetags/extra_filters.py36
-rw-r--r--askbot/tests.py1839
-rw-r--r--askbot/tests/__init__.py4
-rw-r--r--askbot/tests/email_alert_tests.py643
-rw-r--r--askbot/tests/on_screen_notification_tests.py709
-rw-r--r--askbot/tests/page_load_tests.py273
-rw-r--r--askbot/tests/permission_assertion_tests.py426
-rw-r--r--askbot/tests/utils.py30
-rw-r--r--askbot/views/commands.py13
15 files changed, 2511 insertions, 1999 deletions
diff --git a/askbot/auth.py b/askbot/auth.py
index 809fc708..6a92b005 100644
--- a/askbot/auth.py
+++ b/askbot/auth.py
@@ -1,8 +1,11 @@
"""
Authorisation related functions.
-The actions a User is authorised to perform are dependent on their reputation
-and superuser status.
+This entire module will be removed some time in
+the future
+
+Many of these functions are being replaced with assertions:
+User.assert_can...
"""
import datetime
from django.utils.translation import ugettext as _
@@ -15,18 +18,6 @@ import logging
from askbot.conf import settings as askbot_settings
-def can_vote_up(user):
- """Determines if a User can vote Questions and Answers up."""
- if user.is_authenticated():
- if user.reputation >= askbot_settings.MIN_REP_TO_VOTE_UP:
- if user.is_blocked():
- return False
- else:
- return True
- if user.is_administrator() or user.is_moderator():
- return True
- return False
-
def can_flag_offensive(user):
"""Determines if a User can flag Questions and Answers as offensive."""
return user.is_authenticated() and (
@@ -47,15 +38,6 @@ def can_add_comments(user, subject):
return True
return False
-def can_vote_down(user):
- """Determines if a User can vote Questions and Answers down."""
- if user.is_authenticated():
- if user.reputation >= askbot_settings.MIN_REP_TO_VOTE_DOWN:
- return True
- if user.is_superuser:
- return True
- return False
-
def can_retag_questions(user):
"""Determines if a User can retag Questions."""
if user.is_authenticated():
@@ -131,23 +113,6 @@ def can_reopen_question(user, question):
return True
return False
-def can_delete_post(user, post):
- if user.is_superuser:
- return True
- elif user.is_authenticated() and user == post.author:
- if isinstance(post, Answer):
- return True
- elif isinstance(post, Question):
- answers = post.answers.all()
- for answer in answers:
- if user != answer.author and answer.deleted == False:
- return False
- return True
- else:
- return False
- else:
- return False
-
def can_view_deleted_post(user, post):
return user.is_superuser
@@ -541,7 +506,7 @@ def onDeleted(post, user, timestamp=None):
elif isinstance(post, Answer):
Question.objects.update_answer_count(post.question)
logging.debug('updated answer count to %d' % post.question.answer_count)
- signals.delete_post_or_answer.send(
+ signals.delete_question_or_answer.send(
sender=post.__class__,
instance=post,
delete_by=user
diff --git a/askbot/conf/minimum_reputation.py b/askbot/conf/minimum_reputation.py
index d7044911..59726981 100644
--- a/askbot/conf/minimum_reputation.py
+++ b/askbot/conf/minimum_reputation.py
@@ -60,6 +60,15 @@ settings.register(
settings.register(
IntegerValue(
MIN_REP,
+ 'MIN_REP_TO_DELETE_OTHERS_POSTS',
+ default=5000,
+ description=_('Delete questions and answers posted by others')
+ )
+ )
+
+settings.register(
+ IntegerValue(
+ MIN_REP,
'MIN_REP_TO_UPLOAD_FILES',
default=60,
description=_('Upload files')
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 9d4d6db7..e75593b5 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -33,6 +33,12 @@ from askbot.startup_tests import run_startup_tests
run_startup_tests()
+class InsufficientReputation(exceptions.PermissionDenied):
+ """exception class to indicate that permission
+ was denied due to insufficient reputation
+ """
+ pass
+
User.add_to_class(
'status',
models.CharField(
@@ -72,52 +78,6 @@ User.add_to_class('tag_filter_setting',
)
User.add_to_class('response_count', models.IntegerField(default=0))
-
-def user_assert_can_vote_for_post(
- self,
- post = None,
- direction = None,
- ):
- """raises exceptions.PermissionDenied exception
- if user can't in fact upvote
-
- :param:direction can be 'up' or 'down'
- :param:post can be instance of question or answer
- """
- if self.is_blocked():
- raise exceptions.PermissionDenied(
- _(
- 'Sorry your account appears to be blocked ' +
- 'and you cannot vote - please contact the ' +
- 'site administrator to resolve the issue'
- )
- )
- if self.is_suspended():
- raise exceptions.PermissionDenied(
- _(
- 'Sorry your account appears to be suspended ' +
- 'and you cannot vote - please contact the ' +
- 'site administrator to resolve the issue'
- )
- )
- if self.is_moderator() or self.is_administrator():
- return
-
- if direction == 'up':
- if self.reputation < askbot_settings.MIN_REP_TO_VOTE_UP:
- msg = _(">%(points)s points required to upvote") % \
- {'points': askbot_settings.MIN_REP_TO_VOTE_UP}
- raise exceptions.PermissionDenied(msg)
- else:
- if self.reputation < askbot_settings.MIN_REP_TO_VOTE_DOWN:
- msg = _(">%(points)s points required to downvote") % \
- {'points': askbot_settings.MIN_REP_TO_VOTE_DOWN}
- raise exceptions.PermissionDenied(msg)
-
- if self == post.author:
- raise exceptions.PermissionDenied('cannot vote for own posts')
-
-
def user_get_old_vote_for_post(self, post):
"""returns previous vote for this post
by the user or None, if does not exist
@@ -139,29 +99,104 @@ def user_get_old_vote_for_post(self, post):
return old_votes[0]
-def user_assert_can_upload_file(request_user):
+def _assert_user_can(
+ user = None,
+ post = None, #related post (may be parent)
+ owner_can = False,
+ blocked_error_message = None,
+ suspended_error_message = None,
+ min_rep_setting = None,
+ low_rep_error_message = None,
+ ):
+ """generic helper assert for use in several
+ User.assert_can_XYZ() calls regarding changing content
- if request_user.is_authenticated():
- if request_user.is_suspended():
- raise exceptions.PermissionDenied(
- _('Sorry, suspended users cannot upload files')
- )
- elif request_user.is_blocked():
- raise exceptions.PermissionDenied(
- _('Sorry, blocked users cannot upload files')
- )
- elif request_user.is_moderator():
- return
- elif request_user.is_administrator():
- return
- if request_user.reputation < askbot_settings.MIN_REP_TO_UPLOAD_FILES:
- msg = _(
- 'uploading images is limited to users ' +
- 'with >%(min_rep)s reputation points'
- ) % {'min_rep': askbot_settings.MIN_REP_TO_UPLOAD_FILES }
- raise exceptions.PermissionDenied(msg)
+ user is required and at least one error message
+
+ if assertion fails, method raises exception.PermissionDenied
+ with appropriate text as a payload
+ """
+ if blocked_error_message and user.is_blocked():
+ error_message = blocked_error_message
+ elif post and owner_can and user == post.get_owner():
+ return
+ elif suspended_error_message and user.is_suspended():
+ error_message = suspended_error_message
+ elif user.is_administrator() or user.is_moderator():
+ return
+ elif low_rep_error_message and user.reputation < min_rep_setting:
+ error_message = low_rep_error_message % {'min_rep': min_rep_setting}
+ raise InsufficientReputation(error_message)
else:
- raise exceptions.PermissionDenied(_('Sorry, anonymous users cannot upload files'))
+ return
+ raise exceptions.PermissionDenied(error_message)
+
+
+def user_assert_can_vote_for_post(
+ self,
+ post = None,
+ direction = None,
+ ):
+ """raises exceptions.PermissionDenied exception
+ if user can't in fact upvote
+
+ :param:direction can be 'up' or 'down'
+ :param:post can be instance of question or answer
+ """
+
+ if self == post.author:
+ raise exceptions.PermissionDenied('cannot vote for own posts')
+
+ blocked_error_message = _(
+ 'Sorry your account appears to be blocked ' +
+ 'and you cannot vote - please contact the ' +
+ 'site administrator to resolve the issue'
+ ),
+ suspended_error_message = _(
+ 'Sorry your account appears to be suspended ' +
+ 'and you cannot vote - please contact the ' +
+ 'site administrator to resolve the issue'
+ )
+
+ assert(direction in ('up', 'down'))
+
+ if direction == 'up':
+ min_rep_setting = askbot_settings.MIN_REP_TO_VOTE_UP
+ low_rep_error_message = _(
+ ">%(points)s points required to upvote"
+ ) % \
+ {'points': askbot_settings.MIN_REP_TO_VOTE_UP}
+ else:
+ min_rep_setting = askbot_settings.MIN_REP_TO_VOTE_DOWN
+ low_rep_error_message = _(
+ ">%(points)s points required to downvote"
+ ) % \
+ {'points': askbot_settings.MIN_REP_TO_VOTE_DOWN}
+
+ _assert_user_can(
+ user = self,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ min_rep_setting = min_rep_setting,
+ low_rep_error_message = low_rep_error_message
+ )
+
+
+def user_assert_can_upload_file(request_user):
+
+ blocked_error_message = _('Sorry, blocked users cannot upload files')
+ suspended_error_message = _('Sorry, suspended users cannot upload files')
+ low_rep_error_message = _(
+ 'uploading images is limited to users ' + \
+ 'with >%(min_rep)s reputation points'
+ ) % {'min_rep': askbot_settings.MIN_REP_TO_UPLOAD_FILES }
+
+ _assert_user_can(
+ user = request_user,
+ suspended_error_message = suspended_error_message,
+ min_rep_setting = askbot_settings.MIN_REP_TO_UPLOAD_FILES,
+ low_rep_error_message = low_rep_error_message
+ )
def user_assert_can_post_question(self):
@@ -169,45 +204,226 @@ def user_assert_can_post_question(self):
text that has the reason for the denial
"""
- if self.is_suspended():
- raise exceptions.PermissionDenied(_('suspended users cannot post'))
- if self.is_blocked():
- raise exceptions.PermissionDenied(_('blocked users cannot post'))
+ _assert_user_can(
+ user = self,
+ blocked_error_message = _('blocked users cannot post'),
+ suspended_error_message = _('suspended users cannot post'),
+ )
+
def user_assert_can_post_answer(self):
"""same as user_can_post_question
"""
self.assert_can_post_question()
-def user_assert_can_post_comment(self, parent_post):
+
+def user_assert_can_post_comment(self, parent_post = None):
"""raises exceptions.PermissionDenied if
user cannot post comment
the reason will be in text of exception
"""
- if self.is_authenticated():
- if self.is_blocked():
- error_message = _('blocked users cannot post')
- elif self == parent_post.author:
- return
- elif self.is_suspended():
- error_message = _(
- 'Sorry, since your account is suspended ' + \
- 'you can comment only you own posts'
- )
- elif self.is_administrator() or self.is_moderator():
- return
- elif self.reputation < askbot_settings.MIN_REP_TO_LEAVE_COMMENTS:
- error_message = _(
- 'Sorry, to comment any post a minimum reputation of ' + \
- '%(min_rep)s points is required.'
- ) % {'min_rep': askbot_settings.MIN_REP_TO_LEAVE_COMMENTS}
- else:
- return
- else:
- error_message = _('anonymous users cannot comment %(sign_in_url)s') % \
- {'sign_in_url': reverse('user_signin')}
- raise exceptions.PermissionDenied(error_message)
+
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you can comment only your own posts'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to comment any post a minimum reputation of ' + \
+ '%(min_rep)s points is required.'
+ ) % {'min_rep': askbot_settings.MIN_REP_TO_LEAVE_COMMENTS}
+
+ try:
+ _assert_user_can(
+ user = self,
+ post = parent_post,
+ owner_can = True,
+ blocked_error_message = _('blocked users cannot post'),
+ suspended_error_message = suspended_error_message,
+ min_rep_setting = askbot_settings.MIN_REP_TO_LEAVE_COMMENTS,
+ low_rep_error_message = low_rep_error_message,
+ )
+ except InsufficientReputation, e:
+ if isinstance(parent_post, Answer):
+ if self == parent_post.question.author:
+ return
+ raise e
+
+
+def user_assert_can_edit_post(self, post = None):
+ """assertion that raises exceptions.PermissionDenied
+ when user is not authorised to edit this post
+ """
+
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot edit posts'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you can edit only your own posts'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to edit other people\' posts, a minimum ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_EDIT_OTHERS_POSTS}
+ min_rep_setting = askbot_settings.MIN_REP_TO_EDIT_OTHERS_POSTS
+
+ _assert_user_can(
+ user = self,
+ post = post,
+ owner_can = True,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
+
+
+def user_assert_can_delete_post(self, post = None):
+ #todo: move here advanced logic to authorize deletion
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot delete posts'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you can delete only your own posts'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to deleted other people\' posts, a minimum ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_DELETE_OTHERS_POSTS}
+ min_rep_setting = askbot_settings.MIN_REP_TO_DELETE_OTHERS_POSTS
+
+ _assert_user_can(
+ user = self,
+ post = post,
+ owner_can = True,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
+
+
+def user_assert_can_delete_question(self, question = None):
+ self.assert_can_delete_post(post = question)
+
+
+def user_assert_can_delete_answer(self, answer = None):
+ self.assert_can_delete_post(post = answer)
+
+
+def user_assert_can_close_question(self, question = None):
+ assert(isinstance(question, Question) == True)
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot close questions'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you cannot close questions'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to deleted other people\' posts, a minimum ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_CLOSE_OTHERS_QUESTIONS}
+ min_rep_setting = askbot_settings.MIN_REP_TO_CLOSE_OTHERS_QUESTIONS
+
+ _assert_user_can(
+ user = self,
+ post = post,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
+
+
+def user_assert_can_flag_offensive(self):
+
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot flag posts as offensive'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you cannot flag posts as offensive'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to flag posts as offensive a minimum ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE}
+ min_rep_setting = askbot_settings.MIN_REP_TO_FLAG_OFFENSIVE
+
+ _assert_user_can(
+ user = self,
+ post = post,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
+
+
+def user_assert_can_retag_questions(self):
+
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot retag questions'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you can retag only your own questions'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to retag questions a minimum ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_RETAG_QUESTIONS}
+ min_rep_setting = askbot_settings.MIN_REP_TO_RETAG_QUESTIONS
+
+ _assert_user_can(
+ user = self,
+ post = post,
+ owner_can = True,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
+
+
+def user_assert_can_delete_comment(self, comment = None):
+ blocked_error_message = _(
+ 'Sorry, since your account is blocked ' + \
+ 'you cannot delete comment'
+ )
+ suspended_error_message = _(
+ 'Sorry, since your account is suspended ' + \
+ 'you can delete only your own comments'
+ )
+ low_rep_error_message = _(
+ 'Sorry, to delete comments ' + \
+ 'reputation of %(min_rep)s is required'
+ ) % \
+ {'min_rep': askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS}
+ min_rep_setting = askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
+
+ _assert_user_can(
+ user = self,
+ post = comment,
+ owner_can = True,
+ blocked_error_message = blocked_error_message,
+ suspended_error_message = suspended_error_message,
+ low_rep_error_message = low_rep_error_message,
+ min_rep_setting = min_rep_setting
+ )
def user_assert_can_revoke_old_vote(self, vote):
@@ -262,15 +478,49 @@ def user_post_comment(
#print len(Comment.objects.all())
return comment
+def user_delete_comment(
+ self,
+ comment = None,
+ timestamp = None
+ ):
+ self.assert_can_delete_comment(comment = comment)
+ comment.delete()
+
+def user_delete_answer(
+ self,
+ answer = None,
+ timestamp = None
+ ):
+ self.assert_can_delete_answer(answer = answer)
+ #todo - move onDeleted method where appropriate
+ auth.onDeleted(answer, self, timestamp = timestamp)
+
+def user_delete_question(
+ self,
+ question = None,
+ timestamp = None
+ ):
+ self.assert_can_delete_question(question = question)
+ #todo - move onDeleted method to a fitting class
+ auth.onDeleted(question, self, timestamp = timestamp)
+
def user_delete_post(
self,
post = None,
timestamp = None
):
+ """generic delete method for all kinds of posts
+
+ if there is no use cases for it, the method will be removed
+ """
if isinstance(post, Comment):
- post.delete()
+ self.delete_comment(comment = post, timestamp = timestamp)
+ elif isinstance(post, Answer):
+ self.delete_answer(answer = post, timestamp = timestamp)
+ elif isinstance(post, Question):
+ self.delete_question(question = post, timestamp = timestamp)
else:
- auth.onDeleted(post, self, timestamp = timestamp)
+ raise TypeError('either Comment, Question or Answer expected')
def user_post_question(
self,
@@ -433,7 +683,12 @@ def user_set_status(self, new_status):
if new status is applied to user, then the record is
committed to the database
"""
- assert(new_status in ('m','s','b','w','a'))
+ #m - moderator
+ #s - suspended
+ #b - blocked
+ #w - watched
+ #a - approved (regular user)
+ assert(new_status in ('m', 's', 'b', 'w', 'a'))
if new_status == self.status:
return
@@ -747,14 +1002,28 @@ User.add_to_class('can_moderate_user', user_can_moderate_user)
User.add_to_class('moderate_user_reputation', user_moderate_user_reputation)
User.add_to_class('set_status', user_set_status)
User.add_to_class('get_status_display', user_get_status_display)
-User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post)
User.add_to_class('get_old_vote_for_post', user_get_old_vote_for_post)
+User.add_to_class('get_unused_votes_today', user_get_unused_votes_today)
+User.add_to_class('delete_comment', user_delete_comment)
+User.add_to_class('delete_question', user_delete_question)
+User.add_to_class('delete_answer', user_delete_answer)
+
+#assertions
+User.add_to_class('assert_can_vote_for_post', user_assert_can_vote_for_post)
User.add_to_class('assert_can_revoke_old_vote', user_assert_can_revoke_old_vote)
User.add_to_class('assert_can_upload_file', user_assert_can_upload_file)
User.add_to_class('assert_can_post_question', user_assert_can_post_question)
User.add_to_class('assert_can_post_answer', user_assert_can_post_answer)
User.add_to_class('assert_can_post_comment', user_assert_can_post_comment)
-User.add_to_class('get_unused_votes_today', user_get_unused_votes_today)
+User.add_to_class('assert_can_edit_post', user_assert_can_edit_post)
+User.add_to_class('assert_can_close_question', user_assert_can_close_question)
+User.add_to_class('assert_can_flag_offensive', user_assert_can_flag_offensive)
+User.add_to_class('assert_can_retag_questions', user_assert_can_retag_questions)
+#todo: do we need assert_can_delete_post
+User.add_to_class('assert_can_delete_post', user_assert_can_delete_post)
+User.add_to_class('assert_can_delete_comment', user_assert_can_delete_comment)
+User.add_to_class('assert_can_delete_answer', user_assert_can_delete_answer)
+User.add_to_class('assert_can_delete_question', user_assert_can_delete_question)
#todo: move this to askbot/utils ??
def format_instant_notification_body(
@@ -1144,8 +1413,8 @@ django_signals.post_save.connect(
django_signals.post_delete.connect(record_cancel_vote, sender=Vote)
#change this to real m2m_changed with Django1.2
-signals.delete_post_or_answer.connect(record_delete_question, sender=Question)
-signals.delete_post_or_answer.connect(record_delete_question, sender=Answer)
+signals.delete_question_or_answer.connect(record_delete_question, sender=Question)
+signals.delete_question_or_answer.connect(record_delete_question, sender=Answer)
signals.mark_offensive.connect(record_mark_offensive, sender=Question)
signals.mark_offensive.connect(record_mark_offensive, sender=Answer)
signals.tags_updated.connect(record_update_tags, sender=Question)
diff --git a/askbot/models/content.py b/askbot/models/content.py
index 57f9ee97..c439b4dc 100644
--- a/askbot/models/content.py
+++ b/askbot/models/content.py
@@ -195,6 +195,9 @@ class Content(models.Model):
else:
return self.added_at
+ def get_owner(self):
+ return self.author
+
def get_author_list(
self,
include_comments = False,
diff --git a/askbot/models/meta.py b/askbot/models/meta.py
index c10bb8fe..83e82933 100644
--- a/askbot/models/meta.py
+++ b/askbot/models/meta.py
@@ -125,6 +125,9 @@ class Comment(base.MetaContent, base.UserContent):
def set_text(self, text):
self.comment = text
+ def get_owner(self):
+ return self.user
+
def get_updated_activity_data(self, created = False):
if self.content_object.__class__.__name__ == 'Question':
return const.TYPE_ACTIVITY_COMMENT_QUESTION, self
diff --git a/askbot/models/signals.py b/askbot/models/signals.py
index b4ed0d1b..1c5324c3 100644
--- a/askbot/models/signals.py
+++ b/askbot/models/signals.py
@@ -6,7 +6,7 @@ tags_updated = django.dispatch.Signal(providing_args=['question'])
edit_question_or_answer = django.dispatch.Signal(
providing_args=['instance', 'modified_by']
)
-delete_post_or_answer = django.dispatch.Signal(
+delete_question_or_answer = django.dispatch.Signal(
providing_args=['instance', 'deleted_by']
)
mark_offensive = django.dispatch.Signal(providing_args=['instance', 'mark_by'])
diff --git a/askbot/templatetags/extra_filters.py b/askbot/templatetags/extra_filters.py
index 1418f535..a8884895 100644
--- a/askbot/templatetags/extra_filters.py
+++ b/askbot/templatetags/extra_filters.py
@@ -1,5 +1,7 @@
from django import template
+from django.core import exceptions
from askbot import auth
+from askbot import models
from askbot.deps.grapefruit import Color
from django.utils.translation import ugettext as _
import logging
@@ -18,20 +20,12 @@ def can_moderate_user(user, other_user):
return False
@register.filter
-def can_vote_up(user):
- return auth.can_vote_up(user)
-
-@register.filter
def can_flag_offensive(user):
return auth.can_flag_offensive(user)
@register.filter
-def can_add_comments(user,subject):
- return auth.can_add_comments(user,subject)
-
-@register.filter
-def can_vote_down(user):
- return auth.can_vote_down(user)
+def can_add_comments(user, subject):
+ return auth.can_add_comments(user, subject)
@register.filter
def can_retag_questions(user):
@@ -43,7 +37,13 @@ def can_edit_post(user, post):
@register.filter
def can_delete_comment(user, comment):
- return auth.can_delete_comment(user, comment)
+ if user.is_anonymous():
+ return False
+ try:
+ user.assert_can_delete_comment(comment)
+ return True
+ except exceptions.PermissionDenied:
+ return False
@register.filter
def can_view_offensive_flags(user):
@@ -67,7 +67,19 @@ def can_reopen_question(user, question):
@register.filter
def can_delete_post(user, post):
- return auth.can_delete_post(user, post)
+ if user.is_anonymous():
+ return False
+ try:
+ if isinstance(post, models.Question):
+ user.assert_can_delete_question(question = post)
+ return True
+ elif isinstance(post, models.Answer):
+ user.assert_can_delete_answer(answer = post)
+ return True
+ else:
+ return False
+ except exceptions.PermissionDenied:
+ return False
@register.filter
def can_view_user_edit(request_user, target_user):
diff --git a/askbot/tests.py b/askbot/tests.py
deleted file mode 100644
index 0dbdafcb..00000000
--- a/askbot/tests.py
+++ /dev/null
@@ -1,1839 +0,0 @@
-"""
-.. _tests:
-
-:mod:`tests` -- Module for testing Askbot
-==========================================
-
-.. automodule:: tests
- .. moduleauthor:: Evgeny Fadeev <evgeny.fadeev@gmail.com>
-"""
-import copy
-import datetime
-import time
-import functools
-import django.core.mail
-from django.core import exceptions
-from django.conf import settings as django_settings
-from django.test import TestCase
-from django.template import defaultfilters
-from django.core import management
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import AnonymousUser
-from askbot.models import User, Question, Answer, Activity, Comment
-from askbot.models import EmailFeedSetting
-from askbot import const
-from askbot.conf import settings as askbot_settings
-
-def email_alert_test(test_func):
- """decorator for test methods in EmailAlertTests
- wraps tests with a generic sequence of testing
- email alerts on updates to anything relating to
- given question
- """
- @functools.wraps(test_func)
- def wrapped_test(test_object, *args, **kwargs):
- func_name = test_func.__name__
- if func_name.startswith('test_'):
- test_name = func_name.replace('test_', '', 1)
- test_func(test_object)
- test_object.maybe_visit_question()
- test_object.send_alerts()
- test_object.check_results(test_name)
- else:
- raise ValueError('test method names must have prefix "test_"')
- return wrapped_test
-
-def setup_email_alert_tests(setup_func):
- @functools.wraps(setup_func)
- def wrapped_setup(test_object, *args, **kwargs):
- #empty email subscription schedule
- #no email is sent
- test_object.notification_schedule = copy.deepcopy(EmailFeedSetting.NO_EMAIL_SCHEDULE)
- #timestamp to use for the setup
- #functions
- test_object.setup_timestamp = datetime.datetime.now()
-
- #timestamp to use for the question visit
- #by the target user
- #if this timestamp is None then there will be no visit
- #otherwise question will be visited by the target user
- #at that time
- test_object.visit_timestamp = None
-
- #dictionary to hols expected results for each test
- #actual data@is initialized in the code just before the function
- #or in the body of the subclass
- test_object.expected_results = dict()
-
- #do not follow by default (do not use q_sel type subscription)
- test_object.follow_question = False
-
- #fill out expected result for each test
- test_object.expected_results['q_ask'] = {'message_count': 0, }
- test_object.expected_results['q_ask_delete_answer'] = {'message_count': 0, }
- test_object.expected_results['question_comment'] = {'message_count': 0, }
- test_object.expected_results['question_comment_delete'] = {'message_count': 0, }
- test_object.expected_results['answer_comment'] = {'message_count': 0, }
- test_object.expected_results['answer_delete_comment'] = {'message_count': 0, }
- test_object.expected_results['mention_in_question'] = {'message_count': 0, }
- test_object.expected_results['mention_in_answer'] = {'message_count': 0, }
- test_object.expected_results['question_edit'] = {'message_count': 0, }
- test_object.expected_results['answer_edit'] = {'message_count': 0, }
- test_object.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
- test_object.expected_results['q_ans'] = {'message_count': 0, }
- test_object.expected_results['q_ans_new_answer'] = {'message_count': 0, }
-
- #this function is expected to contain a difference between this
- #one and the desired setup within the concrete test
- setup_func(test_object)
- #must call this after setting up the notification schedule
- #because it is needed in setUpUsers() function
- test_object.setUpUsers()
- return wrapped_setup
-
-def create_user(
- username = None,
- email = None,
- notification_schedule = None,
- date_joined = None
- ):
- """Creates a user and sets default update subscription
- settings"""
- user = User.objects.create_user(username, email)
- if date_joined is not None:
- user.date_joined = date_joined
- user.save()
- if notification_schedule == None:
- notification_schedule = EmailFeedSetting.NO_EMAIL_SCHEDULE
-
- for feed_type, frequency in notification_schedule.items():
- feed = EmailFeedSetting(
- feed_type = feed_type,
- frequency = frequency,
- subscriber = user
- )
- feed.save()
- return user
-
-def get_re_notif_after(timestamp):
- notifications = Activity.objects.filter(
- activity_type__in = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY,
- active_at__gte = timestamp
- )
- return notifications
-
-class CommentPermissionAssertionTests(TestCase):
-
- def setUp(self):
- self.user = create_user(
- username = 'test',
- email = 'test@test.com'
- )
- self.min_rep = askbot_settings.MIN_REP_TO_LEAVE_COMMENTS
-
- def create_other_user(self):
- return create_user(
- username = 'other',
- email = 'other@test.com'
- )
-
- def post_question(self, author = None):
- if author is None:
- author = self.user
- return author.post_question(
- title = 'test question title',
- body_text = 'test question body',
- tags = 'test'
- )
-
- def post_answer(self, question = None, author = None):
- if author is None:
- author = self.user
- return author.post_answer(
- question = question,
- body_text = 'test answer'
- )
-
- def test_blocked_user_cannot_comment_own_question(self):
- question = self.post_question()
-
- self.user.set_status('b')
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.post_comment,
- parent_post = question,
- body_text = 'test comment'
- )
-
- def test_blocked_user_cannot_comment_own_answer(self):
- question = self.post_question()
- answer = self.post_answer(question)
-
- self.user.set_status('b')
-
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.post_comment,
- parent_post = answer,
- body_text = 'test comment'
- )
-
- def test_low_rep_user_cannot_comment_others(self):
- other_user = create_user(
- username = 'other',
- email = 'other@test.com'
- )
- question = self.post_question(
- author = other_user
- )
- assert(self.user.reputation < self.min_rep)
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.post_comment,
- parent_post = question,
- body_text = 'test comment'
- )
-
- def test_low_rep_user_can_comment_others_answer_to_own_question(self):
- question = self.post_question()
- assert(self.user.reputation < self.min_rep)
- other_user = self.create_other_user()
- answer = other_user.post_answer(
- question = question,
- body_text = 'test answer'
- )
- comment = other_user.post_comment(
- parent_post = answer,
- body_text = 'test comment'
- )
- self.assertTrue(isinstance(comment, Comment))
-
- def test_high_rep_user_can_comment(self):
- other_user = self.create_other_user()
- question = self.post_question(
- author = other_user
- )
- self.user.reputation = self.min_rep
- comment = self.user.post_comment(
- parent_post = question,
- body_text = 'test comment'
- )
- self.assertTrue(isinstance(comment, Comment))
-
- def test_suspended_user_cannot_comment_others_question(self):
- other_user = self.create_other_user()
- question = self.post_question(author = other_user)
- self.user.set_status('s')
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.post_comment,
- parent_post = question,
- body_text = 'test comment'
- )
-
- def test_suspended_user_can_comment_own_question(self):
- question = self.post_question()
- self.user.set_status('s')
- comment = self.user.post_comment(
- parent_post = question,
- body_text = 'test comment'
- )
- self.assertTrue(isinstance(comment, Comment))
-
- def test_low_rep_admin_can_comment_others_question(self):
- question = self.post_question()
- other_user = self.create_other_user()
- other_user.is_superuser = True
- assert(other_user.is_administrator())
- assert(other_user.reputation < self.min_rep)
- comment = other_user.post_comment(
- parent_post = question,
- body_text = 'test comment'
- )
- self.assertTrue(isinstance(comment, Comment))
-
- def test_low_rep_moderator_can_comment_others_question(self):
- question = self.post_question()
- other_user = self.create_other_user()
- other_user.set_status('m')
- assert(other_user.is_moderator())
- assert(other_user.reputation < self.min_rep)
- comment = other_user.post_comment(
- parent_post = question,
- body_text = 'test comment'
- )
- self.assertTrue(isinstance(comment, Comment))
-
-class UploadPermissionAssertionTests(TestCase):
- """Tests permissions for file uploads
- """
-
- def setUp(self):
- self.user = create_user(
- username = 'test',
- email = 'test@test.com'
- )
- self.min_rep = askbot_settings.MIN_REP_TO_UPLOAD_FILES
-
- def test_suspended_user_cannot_upload(self):
- self.user.set_status('s')
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.assert_can_upload_file
- )
-
- def test_blocked_user_cannot_upload(self):
- self.user.set_status('b')
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.assert_can_upload_file
- )
- def test_low_rep_user_cannot_upload(self):
- self.user.reputation = self.min_rep - 1
- self.assertRaises(
- exceptions.PermissionDenied,
- self.user.assert_can_upload_file
- )
-
- def test_high_rep_user_can_upload(self):
- self.user.reputation = self.min_rep
- try:
- self.user.assert_can_upload_file()
- except exceptions.PermissionDenied:
- fail('high rep user must be able to upload')
-
- def test_low_rep_moderator_can_upload(self):
- assert(self.user.reputation < self.min_rep)
- self.user.set_status('m')
- try:
- self.user.assert_can_upload_file()
- except exceptions.PermissionDenied:
- fail('high rep user must be able to upload')
-
- def test_low_rep_administrator_can_upload(self):
- assert(self.user.reputation < self.min_rep)
- self.user.is_superuser = True
- try:
- self.user.assert_can_upload_file()
- except exceptions.PermissionDenied:
- fail('high rep user must be able to upload')
-
-
-class EmailAlertTests(TestCase):
- """Base class for testing delayed Email notifications
- that are triggered by the send_email_alerts
- command
-
- this class tests cases where target user has no subscriptions
- that is all subscriptions off
-
- subclasses should redefine initial data via the static
- class member this class tests cases where target user has no subscriptions
- that is all subscriptions off
-
- this class also defines a few utility methods that do
- not run any tests themselves
-
- class variables:
-
- * notification_schedule
- * setup_timestamp
- * visit_timestamp
- * expected_results
-
- should be set in subclasses to reuse testing code
- """
-
- def send_alerts(self):
- """runs the send_email_alerts management command
- and makes a shortcut access to the outbox
- """
- #make sure tha we are not sending email for real
- #this setting must be present in settings.py
- assert(
- django_settings.EMAIL_BACKEND == 'django.core.mail.backends.locmem.EmailBackend'
- )
- management.call_command('send_email_alerts')
-
- @setup_email_alert_tests
- def setUp(self):
- """generic pre-test setup method:
-
- this function is empty - because it's intendend content is
- entirely defined by the decorator
-
- the ``setUp()`` function in any subclass must only enter differences
- between the default version (defined in the decorator) and the
- desired version in the "real" test
- """
- pass
-
- def setUpUsers(self):
- self.other_user = create_user(
- username = 'other',
- email = 'other@domain.com',
- date_joined = self.setup_timestamp
- )
- self.target_user = create_user(
- username = 'target',
- email = 'target@domain.com',
- notification_schedule = self.notification_schedule,
- date_joined = self.setup_timestamp
- )
- #moderators to avoid permission issues
- self.other_user.set_status('m')
- self.other_user.save()
- self.target_user.set_status('m')
- self.target_user.save()
-
- def post_comment(
- self,
- author = None,
- parent_post = None,
- body_text = 'dummy test comment',
- timestamp = None
- ):
- """posts and returns a comment to parent post, uses
- now timestamp if not given, dummy body_text
- author is required
- """
- if timestamp is None:
- timestamp = self.setup_timestamp
- comment = author.post_comment(
- parent_post = parent_post,
- body_text = body_text,
- timestamp = timestamp,
- )
- return comment
-
- def edit_post(
- self,
- author = None,
- post = None,
- timestamp = None,
- body_text = 'edited body text',
- ):
- """todo: this method may also edit other stuff
- like post title and tags - in the case when post is
- of type question
- """
- if timestamp is None:
- timestamp = self.setup_timestamp
- post.apply_edit(
- edited_by = author,
- edited_at = timestamp,
- text = body_text,
- comment = 'nothing serious'
- )
-
- def post_question(
- self,
- author = None,
- timestamp = None,
- title = 'test question title',
- body_text = 'test question body',
- tags = 'test',
- ):
- """post a question with dummy content
- and return it
- """
- if timestamp is None:
- timestamp = self.setup_timestamp
- self.question = author.post_question(
- title = title,
- body_text = body_text,
- tags = tags,
- timestamp = timestamp
- )
- if self.follow_question:
- self.target_user.follow_question(self.question)
- return self.question
-
- def maybe_visit_question(self, user = None):
- """visits question on behalf of a given user and applies
- a timestamp set in the class attribute ``visit_timestamp``
-
- if ``visit_timestamp`` is None, then visit is skipped
-
- parameter ``user`` is optional if not given, the visit will occur
- on behalf of the user stored in the class attribute ``target_user``
- """
- if self.visit_timestamp:
- if user is None:
- user = self.target_user
- user.visit_post(
- question = self.question,
- timestamp = self.visit_timestamp
- )
-
- def post_answer(
- self,
- question = None,
- author = None,
- body_text = 'test answer body',
- timestamp = None,
- follow = None,#None - do nothing, True/False - follow/unfollow
- ):
- """post answer with dummy content and return it
- """
- if timestamp is None:
- timestamp = self.setup_timestamp
-
- if follow is None:
- if author.is_following(question):
- follow = True
- else:
- follow = False
- elif follow not in (True, False):
- raise ValueError('"follow" may be only None, True or False')
-
- return author.post_answer(
- question = question,
- body_text = body_text,
- timestamp = timestamp,
- follow = follow,
- )
-
- def check_results(self, test_key = None):
- if test_key is None:
- raise ValueError('test_key parameter is required')
- expected = self.expected_results[test_key]
- outbox = django.core.mail.outbox
- error_message = 'emails_sent=%d, expected=%d, function=%s.test_%s' % (
- len(outbox),
- expected['message_count'],
- self.__class__.__name__,
- test_key,
- )
- self.assertEqual(len(outbox), expected['message_count'], error_message)
- if expected['message_count'] > 0:
- if len(outbox) > 0:
- self.assertEqual(
- outbox[0].recipients()[0],
- self.target_user.email,
- error_message
- )
-
- def proto_post_answer_comment(self):
- """base method for use in some tests
- """
- question = self.post_question(
- author = self.other_user
- )
- answer = self.post_answer(
- question = question,
- author = self.target_user
- )
- comment = self.post_comment(
- parent_post = answer,
- author = self.other_user,
- )
- return comment
-
- @email_alert_test
- def test_answer_comment(self):
- """target user posts answer and other user posts a comment
- to the answer
- """
- self.proto_post_answer_comment()
-
- @email_alert_test
- def test_answer_delete_comment(self):
- comment = self.proto_post_answer_comment()
- comment.user.delete_post(comment)
-
- @email_alert_test
- def test_question_edit(self):
- question = self.post_question(
- author = self.target_user
- )
- self.edit_post(
- post = question,
- author = self.other_user
- )
- self.question = question
-
- @email_alert_test
- def test_answer_edit(self):
- question = self.post_question(
- author = self.target_user
- )
- answer = self.post_answer(
- question = question,
- author = self.target_user
- )
- self.edit_post(
- post = answer,
- author = self.other_user
- )
- self.question = question
-
- @email_alert_test
- def test_question_and_answer_by_target(self):
- question = self.post_question(
- author = self.target_user
- )
- answer = self.post_answer(
- question = question,
- author = self.target_user
- )
- self.question = question
-
- def proto_question_comment(self):
- question = self.post_question(
- author = self.target_user,
- )
- comment = self.post_comment(
- author = self.other_user,
- parent_post = question,
- )
- return comment
-
- @email_alert_test
- def test_question_comment(self):
- """target user posts question other user posts a comment
- target user does or does not receive email notification
- depending on the setup parameters
-
- in the base class user does not receive a notification
- """
- self.proto_question_comment()
-
- @email_alert_test
- def test_question_comment_delete(self):
- """target user posts question other user posts a comment
- target user does or does not receive email notification
- depending on the setup parameters
-
- in the base class user does not receive a notification
- """
- comment = self.proto_question_comment()
- comment.user.delete_post(comment)
-
- def proto_test_q_ask(self):
- """base method for tests that
- have name containing q_ask - i.e. target asks other answers
- answer is returned
- """
- question = self.post_question(
- author = self.target_user,
- )
- answer = self.post_answer(
- question = question,
- author = self.other_user,
- )
- return answer
-
- @email_alert_test
- def test_q_ask(self):
- """target user posts question
- other user answer the question
- """
- self.proto_test_q_ask()
-
- @email_alert_test
- def test_q_ask_delete_answer(self):
- answer = self.proto_test_q_ask()
- self.other_user.delete_post(answer)
-
- @email_alert_test
- def test_q_ans(self):
- """other user posts question
- target user post answer
- """
- question = self.post_question(
- author = self.other_user,
- )
- self.post_answer(
- question = question,
- author = self.target_user
- )
- self.question = question
-
- @email_alert_test
- def test_q_ans_new_answer(self):
- """other user posts question
- target user post answer and other user
- posts another answer
- """
- question = self.post_question(
- author = self.other_user,
- )
- self.post_answer(
- question = question,
- author = self.target_user
- )
- self.post_answer(
- question = question,
- author = self.other_user
- )
- self.question = question
-
- @email_alert_test
- def test_mention_in_question(self):
- question = self.post_question(
- author = self.other_user,
- body_text = 'hey @target get this'
- )
- self.question = question
-
- @email_alert_test
- def test_mention_in_answer(self):
- question = self.post_question(
- author = self.other_user,
- )
- self.post_answer(
- question = question,
- author = self.other_user,
- body_text = 'hey @target check this out'
- )
- self.question = question
-
-class WeeklyQAskEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_ask'] = 'w'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
- self.expected_results['q_ask'] = {'message_count': 1}
- self.expected_results['q_ask_delete_answer'] = {'message_count': 0}
- self.expected_results['question_edit'] = {'message_count': 1, }
- self.expected_results['answer_edit'] = {'message_count': 1, }
-
- #local tests
- self.expected_results['question_edit_reedited_recently'] = \
- {'message_count': 1}
- self.expected_results['answer_edit_reedited_recently'] = \
- {'message_count': 1}
-
- @email_alert_test
- def test_question_edit_reedited_recently(self):
- question = self.post_question(
- author = self.target_user
- )
- self.edit_post(
- post = question,
- author = self.other_user,
- )
- self.edit_post(
- post = question,
- author = self.other_user,
- timestamp = datetime.datetime.now() - datetime.timedelta(1)
- )
-
- @email_alert_test
- def test_answer_edit_reedited_recently(self):
- question = self.post_question(
- author = self.target_user
- )
- answer = self.post_answer(
- question = question,
- author = self.other_user,
- )
- self.edit_post(
- post = answer,
- author = self.other_user,
- timestamp = datetime.datetime.now() - datetime.timedelta(1)
- )
-
-class WeeklyMentionsAndCommentsEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['m_and_c'] = 'w'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
- self.expected_results['question_comment'] = {'message_count': 1, }
- self.expected_results['question_comment_delete'] = {'message_count': 0, }
- self.expected_results['answer_comment'] = {'message_count': 1, }
- self.expected_results['answer_delete_comment'] = {'message_count': 0, }
- self.expected_results['mention_in_question'] = {'message_count': 1, }
- self.expected_results['mention_in_answer'] = {'message_count': 1, }
-
-class WeeklyQAnsEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_ans'] = 'w'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
- self.expected_results['answer_edit'] = {'message_count': 1, }
- self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
-
-class InstantQAskEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_ask'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
- self.expected_results['q_ask'] = {'message_count': 1}
- self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
- self.expected_results['question_edit'] = {'message_count': 1, }
- self.expected_results['answer_edit'] = {'message_count': 1, }
-
-class InstantWholeForumEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_all'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
-
- self.expected_results['q_ask'] = {'message_count': 1, }
- self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
- self.expected_results['question_comment'] = {'message_count': 1, }
- self.expected_results['question_comment_delete'] = {'message_count': 1, }
- self.expected_results['answer_comment'] = {'message_count': 2, }
- self.expected_results['answer_delete_comment'] = {'message_count': 2, }
- self.expected_results['mention_in_question'] = {'message_count': 1, }
- self.expected_results['mention_in_answer'] = {'message_count': 2, }
- self.expected_results['question_edit'] = {'message_count': 1, }
- self.expected_results['answer_edit'] = {'message_count': 1, }
- self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
- self.expected_results['q_ans'] = {'message_count': 1, }
- self.expected_results['q_ans_new_answer'] = {'message_count': 2, }
-
-class BlankWeeklySelectedQuestionsEmailAlertTests(EmailAlertTests):
- """blank means that this is testing for the absence of email
- because questions are not followed as set by default in the
- parent class
- """
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_sel'] = 'w'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
-
-class BlankInstantSelectedQuestionsEmailAlertTests(EmailAlertTests):
- """blank means that this is testing for the absence of email
- because questions are not followed as set by default in the
- parent class
- """
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_sel'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
-
-class LiveWeeklySelectedQuestionsEmailAlertTests(EmailAlertTests):
- """live means that this is testing for the presence of email
- as all questions are automatically followed by user here
- """
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_sel'] = 'w'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
- self.follow_question = True
-
- self.expected_results['q_ask'] = {'message_count': 1, }
- self.expected_results['q_ask_delete_answer'] = {'message_count': 0}
- self.expected_results['question_comment'] = {'message_count': 0, }
- self.expected_results['question_comment_delete'] = {'message_count': 0, }
- self.expected_results['answer_comment'] = {'message_count': 0, }
- self.expected_results['answer_delete_comment'] = {'message_count': 0, }
- self.expected_results['mention_in_question'] = {'message_count': 1, }
- self.expected_results['mention_in_answer'] = {'message_count': 1, }
- self.expected_results['question_edit'] = {'message_count': 1, }
- self.expected_results['answer_edit'] = {'message_count': 1, }
- self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
- self.expected_results['q_ans'] = {'message_count': 0, }
- self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
-
-class LiveInstantSelectedQuestionsEmailAlertTests(EmailAlertTests):
- """live means that this is testing for the presence of email
- as all questions are automatically followed by user here
- """
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_sel'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
- self.follow_question = True
-
- self.expected_results['q_ask'] = {'message_count': 1, }
- self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
- self.expected_results['question_comment'] = {'message_count': 1, }
- self.expected_results['question_comment_delete'] = {'message_count': 1, }
- self.expected_results['answer_comment'] = {'message_count': 1, }
- self.expected_results['answer_delete_comment'] = {'message_count': 1, }
- self.expected_results['mention_in_question'] = {'message_count': 0, }
- self.expected_results['mention_in_answer'] = {'message_count': 1, }
- self.expected_results['question_edit'] = {'message_count': 1, }
- self.expected_results['answer_edit'] = {'message_count': 1, }
- self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
- self.expected_results['q_ans'] = {'message_count': 0, }
- self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
-
-class InstantMentionsAndCommentsEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['m_and_c'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
- self.expected_results['question_comment'] = {'message_count': 1, }
- self.expected_results['question_comment_delete'] = {'message_count': 1, }
- self.expected_results['answer_comment'] = {'message_count': 1, }
- self.expected_results['answer_delete_comment'] = {'message_count': 1, }
- self.expected_results['mention_in_question'] = {'message_count': 1, }
- self.expected_results['mention_in_answer'] = {'message_count': 1, }
-
- #specialized local test case
- self.expected_results['question_edited_mention_stays'] = {'message_count': 1}
-
- @email_alert_test
- def test_question_edited_mention_stays(self):
- question = self.post_question(
- author = self.other_user,
- body_text = 'hey @target check this one',
- )
- self.edit_post(
- post = question,
- author = self.other_user,
- body_text = 'yoyo @target do look here'
- )
-
-class InstantQAnsEmailAlertTests(EmailAlertTests):
- @setup_email_alert_tests
- def setUp(self):
- self.notification_schedule['q_ans'] = 'i'
- self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
- self.expected_results['answer_edit'] = {'message_count': 1, }
- self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
-
-class OnScreenUpdateNotificationTests(TestCase):
- """Test update notifications that are displayed on
- screen in the user profile responses view
- and "the red envelope"
- """
-
- def reset_response_counts(self):
- self.reload_users()
- for user in self.users:
- user.response_count = 0
- user.save()
-
- def reload_users(self):
- self.u11 = User.objects.get(id=self.u11.id)
- self.u12 = User.objects.get(id=self.u12.id)
- self.u13 = User.objects.get(id=self.u13.id)
- self.u14 = User.objects.get(id=self.u14.id)
- self.u21 = User.objects.get(id=self.u21.id)
- self.u22 = User.objects.get(id=self.u22.id)
- self.u23 = User.objects.get(id=self.u23.id)
- self.u24 = User.objects.get(id=self.u24.id)
- self.u31 = User.objects.get(id=self.u31.id)
- self.u32 = User.objects.get(id=self.u32.id)
- self.u33 = User.objects.get(id=self.u33.id)
- self.u34 = User.objects.get(id=self.u34.id)
- self.users = [
- self.u11,
- self.u12,
- self.u13,
- self.u14,
- self.u21,
- self.u22,
- self.u23,
- self.u24,
- self.u31,
- self.u32,
- self.u33,
- self.u34,
- ]
-
- def setUp(self):
- #users for the question
- self.u11 = create_user('user11', 'user11@example.com')
- self.u12 = create_user('user12', 'user12@example.com')
- self.u13 = create_user('user13', 'user13@example.com')
- self.u14 = create_user('user14', 'user14@example.com')
-
- #users for first answer
- self.u21 = create_user('user21', 'user21@example.com')#post answer
- self.u22 = create_user('user22', 'user22@example.com')#edit answer
- self.u23 = create_user('user23', 'user23@example.com')
- self.u24 = create_user('user24', 'user24@example.com')
-
- #users for second answer
- self.u31 = create_user('user31', 'user31@example.com')#post answer
- self.u32 = create_user('user32', 'user32@example.com')#edit answer
- self.u33 = create_user('user33', 'user33@example.com')
- self.u34 = create_user('user34', 'user34@example.com')
-
- #a hack to initialize .users list
- self.reload_users()
-
- #pre-populate askbot with some content
- self.question = Question.objects.create_new(
- title = 'test question',
- author = self.u11,
- added_at = datetime.datetime.now(),
- wiki = False,
- tagnames = 'test',
- text = 'hey listen up',
- )
- self.comment12 = self.question.add_comment(
- user = self.u12,
- comment = 'comment12'
- )
- self.comment13 = self.question.add_comment(
- user = self.u13,
- comment = 'comment13'
- )
- self.answer1 = Answer.objects.create_new(
- question = self.question,
- author = self.u21,
- added_at = datetime.datetime.now(),
- text = 'answer1'
- )
- self.comment22 = self.answer1.add_comment(
- user = self.u22,
- comment = 'comment22'
- )
- self.comment23 = self.answer1.add_comment(
- user = self.u23,
- comment = 'comment23'
- )
- self.answer2 = Answer.objects.create_new(
- question = self.question,
- author = self.u31,
- added_at = datetime.datetime.now(),
- text = 'answer2'
- )
- self.comment32 = self.answer2.add_comment(
- user = self.u32,
- comment = 'comment32'
- )
- self.comment33 = self.answer2.add_comment(
- user = self.u33,
- comment = 'comment33'
- )
-
- def post_then_delete_answer_comment(self):
- pass
-
- def post_then_delete_answer(self):
- pass
-
- def post_then_delete_question_comment(self):
- pass
-
- def post_mention_in_question_then_delete(self):
- pass
-
- def post_mention_in_answer_then_delete(self):
- pass
-
- def post_mention_in_question_then_edit_out(self):
- pass
-
- def post_mention_in_answer_then_edit_out(self):
- pass
-
- def test_post_mention_in_question_comment_then_delete(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- comment = self.question.add_comment(
- user = self.u11,
- comment = '@user12 howyou doin?',
- added_at = timestamp
- )
- comment.delete()
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 0)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- comment = self.answer1.add_comment(
- user = self.u21,
- comment = 'hey @user22 blah',
- added_at = timestamp
- )
- comment.delete()
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 0)
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_self_comments(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.add_comment(
- user = self.u11,
- comment = 'self-comment',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u12, self.u13]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 1, 1, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer1.add_comment(
- user = self.u21,
- comment = 'self-comment 2',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u22, self.u23]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 0, 0, 0,
- 0, 1, 1, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_self_mention_not_posting_in_comment_to_question1(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.add_comment(
- user = self.u11,
- comment = 'self-comment @user11',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u12, self.u13]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 1, 1, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_self_mention_not_posting_in_comment_to_question2(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.add_comment(
- user = self.u11,
- comment = 'self-comment @user11 blah',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u12, self.u13]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 1, 1, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_self_mention_not_posting_in_comment_to_answer(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer1.add_comment(
- user = self.u21,
- comment = 'self-comment 1 @user21',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u22, self.u23]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 0, 0, 0,
- 0, 1, 1, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_comments_to_post_authors(self):
- self.question.apply_edit(
- edited_by = self.u14,
- text = 'now much better',
- comment = 'improved text'
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.add_comment(
- user = self.u12,
- comment = 'self-comment 1',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u11, self.u13, self.u14]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 1, 0, 1, 1,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
- self.answer1.apply_edit(
- edited_by = self.u24,
- text = 'now much better',
- comment = 'improved text'
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer1.add_comment(
- user = self.u22,
- comment = 'self-comment 1',
- added_at = timestamp
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u21, self.u23, self.u24]),
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 0, 0, 0,
- 1, 0, 1, 1,
- 0, 0, 0, 0,
- ]
- )
-
- def test_question_edit(self):
- """when question is edited
- response receivers are question authors, commenters
- and answer authors, but not answer commenters
- """
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.apply_edit(
- edited_by = self.u14,
- text = 'waaay better question!',
- comment = 'improved question',
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u11, self.u12, self.u13, self.u21, self.u31])
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 1, 1, 1, 0,
- 1, 0, 0, 0,
- 1, 0, 0, 0,
- ]
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.question.apply_edit(
- edited_by = self.u31,
- text = 'waaay even better question!',
- comment = 'improved question',
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set([self.u11, self.u12, self.u13, self.u14, self.u21])
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 1, 1, 1, 1,
- 1, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
-
- def test_answer_edit(self):
- """
- """
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer1.apply_edit(
- edited_by = self.u24,
- text = 'waaay better answer!',
- comment = 'improved answer1',
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set(
- [
- self.u11, self.u12, self.u13,
- self.u21, self.u22, self.u23,
- self.u31
- ]
- )
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 1, 1, 1, 0,
- 1, 1, 1, 0,
- 1, 0, 0, 0,
- ]
- )
-
- def test_new_answer(self):
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer3 = Answer.objects.create_new(
- question = self.question,
- author = self.u11,
- added_at = timestamp,
- text = 'answer3'
- )
- time_end = datetime.datetime.now()
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set(
- [
- self.u12, self.u13,
- self.u21, self.u31
- ]
- )
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 0, 1, 1, 0,
- 1, 0, 0, 0,
- 1, 0, 0, 0,
- ]
- )
- self.reset_response_counts()
- time.sleep(1)
- timestamp = datetime.datetime.now()
- self.answer3 = Answer.objects.create_new(
- question = self.question,
- author = self.u31,
- added_at = timestamp,
- text = 'answer4'
- )
- notifications = get_re_notif_after(timestamp)
- self.assertEqual(len(notifications), 1)
- self.assertEqual(
- set(notifications[0].receiving_users.all()),
- set(
- [
- self.u11, self.u12, self.u13,
- self.u21
- ]
- )
- )
- self.reload_users()
- self.assertEqual(
- [
- self.u11.response_count,
- self.u12.response_count,
- self.u13.response_count,
- self.u14.response_count,
- self.u21.response_count,
- self.u22.response_count,
- self.u23.response_count,
- self.u24.response_count,
- self.u31.response_count,
- self.u32.response_count,
- self.u33.response_count,
- self.u34.response_count,
- ],
- [
- 1, 1, 1, 0,
- 1, 0, 0, 0,
- 0, 0, 0, 0,
- ]
- )
-
-
-class PageLoadTestCase(TestCase):
- def try_url(
- self,
- url_name, status_code=200, template=None,
- kwargs={}, redirect_url=None, follow=False,
- data = {},
- ):
- url = reverse(url_name, kwargs = kwargs)
- url_info = 'getting url %s' % url
- if data:
- url_info += '?' + '&'.join(['%s=%s' % (k, v) for k, v in data.iteritems()])
- print url_info
-
- r = self.client.get(url, data=data, follow=follow)
- if hasattr(self.client, 'redirect_chain'):
- print 'redirect chain: %s' % ','.join(self.client.redirect_chain)
-
- self.assertEqual(r.status_code, status_code)
-
- if template:
- #asuming that there is more than one template
- print 'templates are %s' % ','.join([t.name for t in r.template])
- self.assertEqual(r.template[0].name, template)
-
-class PageLoadTests(PageLoadTestCase):
- fixtures = ['tmp/fixture1.json', ]
-
- def test_index(self):
- #todo: merge this with all reader url tests
- print 'trying to reverse index'
- response = self.client.get(reverse('index'), follow=True)
- self.assertEqual(response.status_code, 200)
- self.failUnless(len(response.redirect_chain) == 1)
- self.failUnless(response.redirect_chain[0][0].endswith('/questions/'))
- c = response.context[0]
- t = response.template[0]
- self.assertEqual(t.name, 'questions.html')
- print 'index works'
-
- def proto_test_non_user_urls(self):
- """test all reader views thoroughly
- on non-crashiness (no correcteness tests here)
- """
-
- self.try_url('sitemap')
- self.try_url('feeds', kwargs={'url':'rss'})
- self.try_url('about', template='about.html')
- self.try_url('privacy', template='privacy.html')
- self.try_url('logout', template='logout.html')
- self.try_url('user_signin', template='authopenid/signin.html')
- #todo: test different tabs
- self.try_url('tags', template='tags.html')
- self.try_url('tags', data={'sort':'name'}, template='tags.html')
- self.try_url('tags', data={'sort':'used'}, template='tags.html')
- self.try_url('badges', template='badges.html')
- self.try_url(
- 'answer_revisions',
- template='revisions_answer.html',
- kwargs={'id':38}
- )
- #todo: test different sort methods and scopes
- self.try_url(
- 'questions',
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'start_over':'true'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'unanswered'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'all'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'favorite'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'unanswered', 'sort':'latest'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'unanswered', 'sort':'oldest'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'unanswered', 'sort':'active'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'scope':'unanswered', 'sort':'inactive'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'sort':'hottest'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'sort':'coldest'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'sort':'mostvoted'},
- template='questions.html'
- )
- self.try_url(
- 'questions',
- data={'sort':'leastvoted'},
- template='questions.html'
- )
- self.try_url(
- 'question',
- kwargs={'id':1},
- )
- self.try_url(
- 'question',
- kwargs={'id':2},
- )
- self.try_url(
- 'question',
- kwargs={'id':3},
- )
- self.try_url(
- 'question_revisions',
- kwargs={'id':17},
- template='revisions_question.html'
- )
- self.try_url('users', template='users.html')
- #todo: really odd naming conventions for sort methods
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'reputation'},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'newest'},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'last'},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'user'},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'reputation', 'page':2},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'newest', 'page':2},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'last', 'page':2},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'user', 'page':2},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'reputation', 'page':1},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'newest', 'page':1},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'last', 'page':1},
- )
- self.try_url(
- 'users',
- template='users.html',
- data={'sort':'user', 'page':1},
- )
- self.try_url(
- 'edit_user',
- template='authopenid/signin.html',
- kwargs={'id':4},
- status_code=200,
- follow=True,
- )
-
- def test_non_user_urls(self):
- self.proto_test_non_user_urls()
-
- #def test_non_user_urls_logged_in(self):
- #user = User.objects.get(id=1)
- #somehow login this user
- #self.proto_test_non_user_urls()
-
- def test_user_urls(self):
- user = User.objects.get(id=2)
- name_slug = defaultfilters.slugify(user.username)
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'stats'},
- template='user_stats.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'recent'},
- template='user_recent.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'responses'},
- status_code=404,
- template='404.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'reputation'},
- template='user_reputation.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'votes'},
- status_code=404,
- template='404.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'favorites'},
- template='user_favorites.html'
- )
- self.try_url(
- 'user_profile',
- kwargs={'id': 2, 'slug': name_slug},
- data={'sort':'email_subscriptions'},
- status_code=404,
- template='404.html'
- )
diff --git a/askbot/tests/__init__.py b/askbot/tests/__init__.py
new file mode 100644
index 00000000..bc1b9431
--- /dev/null
+++ b/askbot/tests/__init__.py
@@ -0,0 +1,4 @@
+from askbot.tests.email_alert_tests import *
+from askbot.tests.on_screen_notification_tests import *
+from askbot.tests.page_load_tests import *
+from askbot.tests.permission_assertion_tests import *
diff --git a/askbot/tests/email_alert_tests.py b/askbot/tests/email_alert_tests.py
new file mode 100644
index 00000000..48e32240
--- /dev/null
+++ b/askbot/tests/email_alert_tests.py
@@ -0,0 +1,643 @@
+import datetime
+import functools
+import copy
+from django.conf import settings as django_settings
+from django.core import management
+import django.core.mail
+from django.test import TestCase
+from askbot.tests import utils
+from askbot import models
+
+def email_alert_test(test_func):
+ """decorator for test methods in EmailAlertTests
+ wraps tests with a generic sequence of testing
+ email alerts on updates to anything relating to
+ given question
+ """
+ @functools.wraps(test_func)
+ def wrapped_test(test_object, *args, **kwargs):
+ func_name = test_func.__name__
+ if func_name.startswith('test_'):
+ test_name = func_name.replace('test_', '', 1)
+ test_func(test_object)
+ test_object.maybe_visit_question()
+ test_object.send_alerts()
+ test_object.check_results(test_name)
+ else:
+ raise ValueError('test method names must have prefix "test_"')
+ return wrapped_test
+
+def setup_email_alert_tests(setup_func):
+ @functools.wraps(setup_func)
+ def wrapped_setup(test_object, *args, **kwargs):
+ #empty email subscription schedule
+ #no email is sent
+ test_object.notification_schedule = \
+ copy.deepcopy(models.EmailFeedSetting.NO_EMAIL_SCHEDULE)
+ #timestamp to use for the setup
+ #functions
+ test_object.setup_timestamp = datetime.datetime.now()
+
+ #timestamp to use for the question visit
+ #by the target user
+ #if this timestamp is None then there will be no visit
+ #otherwise question will be visited by the target user
+ #at that time
+ test_object.visit_timestamp = None
+
+ #dictionary to hols expected results for each test
+ #actual data@is initialized in the code just before the function
+ #or in the body of the subclass
+ test_object.expected_results = dict()
+
+ #do not follow by default (do not use q_sel type subscription)
+ test_object.follow_question = False
+
+ #fill out expected result for each test
+ test_object.expected_results['q_ask'] = {'message_count': 0, }
+ test_object.expected_results['q_ask_delete_answer'] = {'message_count': 0, }
+ test_object.expected_results['question_comment'] = {'message_count': 0, }
+ test_object.expected_results['question_comment_delete'] = {'message_count': 0, }
+ test_object.expected_results['answer_comment'] = {'message_count': 0, }
+ test_object.expected_results['answer_delete_comment'] = {'message_count': 0, }
+ test_object.expected_results['mention_in_question'] = {'message_count': 0, }
+ test_object.expected_results['mention_in_answer'] = {'message_count': 0, }
+ test_object.expected_results['question_edit'] = {'message_count': 0, }
+ test_object.expected_results['answer_edit'] = {'message_count': 0, }
+ test_object.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
+ test_object.expected_results['q_ans'] = {'message_count': 0, }
+ test_object.expected_results['q_ans_new_answer'] = {'message_count': 0, }
+
+ #this function is expected to contain a difference between this
+ #one and the desired setup within the concrete test
+ setup_func(test_object)
+ #must call this after setting up the notification schedule
+ #because it is needed in setUpUsers() function
+ test_object.setUpUsers()
+ return wrapped_setup
+
+class EmailAlertTests(TestCase):
+ """Base class for testing delayed Email notifications
+ that are triggered by the send_email_alerts
+ command
+
+ this class tests cases where target user has no subscriptions
+ that is all subscriptions off
+
+ subclasses should redefine initial data via the static
+ class member this class tests cases where target user has no subscriptions
+ that is all subscriptions off
+
+ this class also defines a few utility methods that do
+ not run any tests themselves
+
+ class variables:
+
+ * notification_schedule
+ * setup_timestamp
+ * visit_timestamp
+ * expected_results
+
+ should be set in subclasses to reuse testing code
+ """
+
+ def send_alerts(self):
+ """runs the send_email_alerts management command
+ and makes a shortcut access to the outbox
+ """
+ #make sure tha we are not sending email for real
+ #this setting must be present in settings.py
+ assert(
+ django_settings.EMAIL_BACKEND == 'django.core.mail.backends.locmem.EmailBackend'
+ )
+ management.call_command('send_email_alerts')
+
+ @setup_email_alert_tests
+ def setUp(self):
+ """generic pre-test setup method:
+
+ this function is empty - because it's intendend content is
+ entirely defined by the decorator
+
+ the ``setUp()`` function in any subclass must only enter differences
+ between the default version (defined in the decorator) and the
+ desired version in the "real" test
+ """
+ pass
+
+ def setUpUsers(self):
+ self.other_user = utils.create_user(
+ username = 'other',
+ email = 'other@domain.com',
+ date_joined = self.setup_timestamp,
+ status = 'm'
+ )
+ self.target_user = utils.create_user(
+ username = 'target',
+ email = 'target@domain.com',
+ notification_schedule = self.notification_schedule,
+ date_joined = self.setup_timestamp,
+ status = 'm'
+ )
+
+ def post_comment(
+ self,
+ author = None,
+ parent_post = None,
+ body_text = 'dummy test comment',
+ timestamp = None
+ ):
+ """posts and returns a comment to parent post, uses
+ now timestamp if not given, dummy body_text
+ author is required
+ """
+ if timestamp is None:
+ timestamp = self.setup_timestamp
+ comment = author.post_comment(
+ parent_post = parent_post,
+ body_text = body_text,
+ timestamp = timestamp,
+ )
+ return comment
+
+ def edit_post(
+ self,
+ author = None,
+ post = None,
+ timestamp = None,
+ body_text = 'edited body text',
+ ):
+ """todo: this method may also edit other stuff
+ like post title and tags - in the case when post is
+ of type question
+ """
+ if timestamp is None:
+ timestamp = self.setup_timestamp
+ post.apply_edit(
+ edited_by = author,
+ edited_at = timestamp,
+ text = body_text,
+ comment = 'nothing serious'
+ )
+
+ def post_question(
+ self,
+ author = None,
+ timestamp = None,
+ title = 'test question title',
+ body_text = 'test question body',
+ tags = 'test',
+ ):
+ """post a question with dummy content
+ and return it
+ """
+ if timestamp is None:
+ timestamp = self.setup_timestamp
+ self.question = author.post_question(
+ title = title,
+ body_text = body_text,
+ tags = tags,
+ timestamp = timestamp
+ )
+ if self.follow_question:
+ self.target_user.follow_question(self.question)
+ return self.question
+
+ def maybe_visit_question(self, user = None):
+ """visits question on behalf of a given user and applies
+ a timestamp set in the class attribute ``visit_timestamp``
+
+ if ``visit_timestamp`` is None, then visit is skipped
+
+ parameter ``user`` is optional if not given, the visit will occur
+ on behalf of the user stored in the class attribute ``target_user``
+ """
+ if self.visit_timestamp:
+ if user is None:
+ user = self.target_user
+ user.visit_post(
+ question = self.question,
+ timestamp = self.visit_timestamp
+ )
+
+ def post_answer(
+ self,
+ question = None,
+ author = None,
+ body_text = 'test answer body',
+ timestamp = None,
+ follow = None,#None - do nothing, True/False - follow/unfollow
+ ):
+ """post answer with dummy content and return it
+ """
+ if timestamp is None:
+ timestamp = self.setup_timestamp
+
+ if follow is None:
+ if author.is_following(question):
+ follow = True
+ else:
+ follow = False
+ elif follow not in (True, False):
+ raise ValueError('"follow" may be only None, True or False')
+
+ return author.post_answer(
+ question = question,
+ body_text = body_text,
+ timestamp = timestamp,
+ follow = follow,
+ )
+
+ def check_results(self, test_key = None):
+ if test_key is None:
+ raise ValueError('test_key parameter is required')
+ expected = self.expected_results[test_key]
+ outbox = django.core.mail.outbox
+ error_message = 'emails_sent=%d, expected=%d, function=%s.test_%s' % (
+ len(outbox),
+ expected['message_count'],
+ self.__class__.__name__,
+ test_key,
+ )
+ self.assertEqual(len(outbox), expected['message_count'], error_message)
+ if expected['message_count'] > 0:
+ if len(outbox) > 0:
+ self.assertEqual(
+ outbox[0].recipients()[0],
+ self.target_user.email,
+ error_message
+ )
+
+ def proto_post_answer_comment(self):
+ """base method for use in some tests
+ """
+ question = self.post_question(
+ author = self.other_user
+ )
+ answer = self.post_answer(
+ question = question,
+ author = self.target_user
+ )
+ comment = self.post_comment(
+ parent_post = answer,
+ author = self.other_user,
+ )
+ return comment
+
+ @email_alert_test
+ def test_answer_comment(self):
+ """target user posts answer and other user posts a comment
+ to the answer
+ """
+ self.proto_post_answer_comment()
+
+ @email_alert_test
+ def test_answer_delete_comment(self):
+ comment = self.proto_post_answer_comment()
+ comment.get_owner().delete_comment(comment = comment)
+
+ @email_alert_test
+ def test_question_edit(self):
+ question = self.post_question(
+ author = self.target_user
+ )
+ self.edit_post(
+ post = question,
+ author = self.other_user
+ )
+ self.question = question
+
+ @email_alert_test
+ def test_answer_edit(self):
+ question = self.post_question(
+ author = self.target_user
+ )
+ answer = self.post_answer(
+ question = question,
+ author = self.target_user
+ )
+ self.edit_post(
+ post = answer,
+ author = self.other_user
+ )
+ self.question = question
+
+ @email_alert_test
+ def test_question_and_answer_by_target(self):
+ question = self.post_question(
+ author = self.target_user
+ )
+ answer = self.post_answer(
+ question = question,
+ author = self.target_user
+ )
+ self.question = question
+
+ def proto_question_comment(self):
+ question = self.post_question(
+ author = self.target_user,
+ )
+ comment = self.post_comment(
+ author = self.other_user,
+ parent_post = question,
+ )
+ return comment
+
+ @email_alert_test
+ def test_question_comment(self):
+ """target user posts question other user posts a comment
+ target user does or does not receive email notification
+ depending on the setup parameters
+
+ in the base class user does not receive a notification
+ """
+ self.proto_question_comment()
+
+ @email_alert_test
+ def test_question_comment_delete(self):
+ """target user posts question other user posts a comment
+ target user does or does not receive email notification
+ depending on the setup parameters
+
+ in the base class user does not receive a notification
+ """
+ comment = self.proto_question_comment()
+ comment.get_owner().delete_comment(comment)
+
+ def proto_test_q_ask(self):
+ """base method for tests that
+ have name containing q_ask - i.e. target asks other answers
+ answer is returned
+ """
+ question = self.post_question(
+ author = self.target_user,
+ )
+ answer = self.post_answer(
+ question = question,
+ author = self.other_user,
+ )
+ return answer
+
+ @email_alert_test
+ def test_q_ask(self):
+ """target user posts question
+ other user answer the question
+ """
+ self.proto_test_q_ask()
+
+ @email_alert_test
+ def test_q_ask_delete_answer(self):
+ answer = self.proto_test_q_ask()
+ self.other_user.delete_post(answer)
+
+ @email_alert_test
+ def test_q_ans(self):
+ """other user posts question
+ target user post answer
+ """
+ question = self.post_question(
+ author = self.other_user,
+ )
+ self.post_answer(
+ question = question,
+ author = self.target_user
+ )
+ self.question = question
+
+ @email_alert_test
+ def test_q_ans_new_answer(self):
+ """other user posts question
+ target user post answer and other user
+ posts another answer
+ """
+ question = self.post_question(
+ author = self.other_user,
+ )
+ self.post_answer(
+ question = question,
+ author = self.target_user
+ )
+ self.post_answer(
+ question = question,
+ author = self.other_user
+ )
+ self.question = question
+
+ @email_alert_test
+ def test_mention_in_question(self):
+ question = self.post_question(
+ author = self.other_user,
+ body_text = 'hey @target get this'
+ )
+ self.question = question
+
+ @email_alert_test
+ def test_mention_in_answer(self):
+ question = self.post_question(
+ author = self.other_user,
+ )
+ self.post_answer(
+ question = question,
+ author = self.other_user,
+ body_text = 'hey @target check this out'
+ )
+ self.question = question
+
+class WeeklyQAskEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_ask'] = 'w'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
+ self.expected_results['q_ask'] = {'message_count': 1}
+ self.expected_results['q_ask_delete_answer'] = {'message_count': 0}
+ self.expected_results['question_edit'] = {'message_count': 1, }
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+
+ #local tests
+ self.expected_results['question_edit_reedited_recently'] = \
+ {'message_count': 1}
+ self.expected_results['answer_edit_reedited_recently'] = \
+ {'message_count': 1}
+
+ @email_alert_test
+ def test_question_edit_reedited_recently(self):
+ question = self.post_question(
+ author = self.target_user
+ )
+ self.edit_post(
+ post = question,
+ author = self.other_user,
+ )
+ self.edit_post(
+ post = question,
+ author = self.other_user,
+ timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ )
+
+ @email_alert_test
+ def test_answer_edit_reedited_recently(self):
+ question = self.post_question(
+ author = self.target_user
+ )
+ answer = self.post_answer(
+ question = question,
+ author = self.other_user,
+ )
+ self.edit_post(
+ post = answer,
+ author = self.other_user,
+ timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ )
+
+class WeeklyMentionsAndCommentsEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['m_and_c'] = 'w'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
+ self.expected_results['question_comment'] = {'message_count': 1, }
+ self.expected_results['question_comment_delete'] = {'message_count': 0, }
+ self.expected_results['answer_comment'] = {'message_count': 1, }
+ self.expected_results['answer_delete_comment'] = {'message_count': 0, }
+ self.expected_results['mention_in_question'] = {'message_count': 1, }
+ self.expected_results['mention_in_answer'] = {'message_count': 1, }
+
+class WeeklyQAnsEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_ans'] = 'w'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+ self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
+
+class InstantQAskEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_ask'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ self.expected_results['q_ask'] = {'message_count': 1}
+ self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
+ self.expected_results['question_edit'] = {'message_count': 1, }
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+
+class InstantWholeForumEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_all'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+
+ self.expected_results['q_ask'] = {'message_count': 1, }
+ self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
+ self.expected_results['question_comment'] = {'message_count': 1, }
+ self.expected_results['question_comment_delete'] = {'message_count': 1, }
+ self.expected_results['answer_comment'] = {'message_count': 2, }
+ self.expected_results['answer_delete_comment'] = {'message_count': 2, }
+ self.expected_results['mention_in_question'] = {'message_count': 1, }
+ self.expected_results['mention_in_answer'] = {'message_count': 2, }
+ self.expected_results['question_edit'] = {'message_count': 1, }
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+ self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
+ self.expected_results['q_ans'] = {'message_count': 1, }
+ self.expected_results['q_ans_new_answer'] = {'message_count': 2, }
+
+class BlankWeeklySelectedQuestionsEmailAlertTests(EmailAlertTests):
+ """blank means that this is testing for the absence of email
+ because questions are not followed as set by default in the
+ parent class
+ """
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_sel'] = 'w'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
+
+class BlankInstantSelectedQuestionsEmailAlertTests(EmailAlertTests):
+ """blank means that this is testing for the absence of email
+ because questions are not followed as set by default in the
+ parent class
+ """
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_sel'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+
+class LiveWeeklySelectedQuestionsEmailAlertTests(EmailAlertTests):
+ """live means that this is testing for the presence of email
+ as all questions are automatically followed by user here
+ """
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_sel'] = 'w'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(14)
+ self.follow_question = True
+
+ self.expected_results['q_ask'] = {'message_count': 1, }
+ self.expected_results['q_ask_delete_answer'] = {'message_count': 0}
+ self.expected_results['question_comment'] = {'message_count': 0, }
+ self.expected_results['question_comment_delete'] = {'message_count': 0, }
+ self.expected_results['answer_comment'] = {'message_count': 0, }
+ self.expected_results['answer_delete_comment'] = {'message_count': 0, }
+ self.expected_results['mention_in_question'] = {'message_count': 1, }
+ self.expected_results['mention_in_answer'] = {'message_count': 1, }
+ self.expected_results['question_edit'] = {'message_count': 1, }
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+ self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
+ self.expected_results['q_ans'] = {'message_count': 0, }
+ self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
+
+class LiveInstantSelectedQuestionsEmailAlertTests(EmailAlertTests):
+ """live means that this is testing for the presence of email
+ as all questions are automatically followed by user here
+ """
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_sel'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ self.follow_question = True
+
+ self.expected_results['q_ask'] = {'message_count': 1, }
+ self.expected_results['q_ask_delete_answer'] = {'message_count': 1}
+ self.expected_results['question_comment'] = {'message_count': 1, }
+ self.expected_results['question_comment_delete'] = {'message_count': 1, }
+ self.expected_results['answer_comment'] = {'message_count': 1, }
+ self.expected_results['answer_delete_comment'] = {'message_count': 1, }
+ self.expected_results['mention_in_question'] = {'message_count': 0, }
+ self.expected_results['mention_in_answer'] = {'message_count': 1, }
+ self.expected_results['question_edit'] = {'message_count': 1, }
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+ self.expected_results['question_and_answer_by_target'] = {'message_count': 0, }
+ self.expected_results['q_ans'] = {'message_count': 0, }
+ self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
+
+class InstantMentionsAndCommentsEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['m_and_c'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ self.expected_results['question_comment'] = {'message_count': 1, }
+ self.expected_results['question_comment_delete'] = {'message_count': 1, }
+ self.expected_results['answer_comment'] = {'message_count': 1, }
+ self.expected_results['answer_delete_comment'] = {'message_count': 1, }
+ self.expected_results['mention_in_question'] = {'message_count': 1, }
+ self.expected_results['mention_in_answer'] = {'message_count': 1, }
+
+ #specialized local test case
+ self.expected_results['question_edited_mention_stays'] = {'message_count': 1}
+
+ @email_alert_test
+ def test_question_edited_mention_stays(self):
+ question = self.post_question(
+ author = self.other_user,
+ body_text = 'hey @target check this one',
+ )
+ self.edit_post(
+ post = question,
+ author = self.other_user,
+ body_text = 'yoyo @target do look here'
+ )
+
+class InstantQAnsEmailAlertTests(EmailAlertTests):
+ @setup_email_alert_tests
+ def setUp(self):
+ self.notification_schedule['q_ans'] = 'i'
+ self.setup_timestamp = datetime.datetime.now() - datetime.timedelta(1)
+ self.expected_results['answer_edit'] = {'message_count': 1, }
+ self.expected_results['q_ans_new_answer'] = {'message_count': 1, }
diff --git a/askbot/tests/on_screen_notification_tests.py b/askbot/tests/on_screen_notification_tests.py
new file mode 100644
index 00000000..ea607810
--- /dev/null
+++ b/askbot/tests/on_screen_notification_tests.py
@@ -0,0 +1,709 @@
+"""
+.. _on_screen_notification_tests:
+
+:mod:`on_screen_notification_tests` -- Module for testing on-screen notifications
+=================================================================================
+
+.. automodule:: on_screen_notification_tests
+ .. moduleauthor:: Evgeny Fadeev <evgeny.fadeev@gmail.com>
+"""
+import datetime
+import time
+from django.test import TestCase
+from askbot import models
+from askbot import const
+from askbot.tests.utils import create_user
+
+
+def get_re_notif_after(timestamp):
+ """returns query set with response notifications
+ posted after the ``timestamp`` - a ``datetime.datetime`` instance
+ """
+ notifications = models.Activity.objects.filter(
+ activity_type__in = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY,
+ active_at__gte = timestamp
+ )
+ return notifications
+
+
+class OnScreenUpdateNotificationTests(TestCase):
+ """Test update notifications that are displayed on
+ screen in the user profile responses view
+ and "the red envelope"
+ """
+
+ def reset_response_counts(self):
+ self.reload_users()
+ for user in self.users:
+ user.response_count = 0
+ user.save()
+
+ def reload_users(self):
+ self.u11 = models.User.objects.get(id=self.u11.id)
+ self.u12 = models.User.objects.get(id=self.u12.id)
+ self.u13 = models.User.objects.get(id=self.u13.id)
+ self.u14 = models.User.objects.get(id=self.u14.id)
+ self.u21 = models.User.objects.get(id=self.u21.id)
+ self.u22 = models.User.objects.get(id=self.u22.id)
+ self.u23 = models.User.objects.get(id=self.u23.id)
+ self.u24 = models.User.objects.get(id=self.u24.id)
+ self.u31 = models.User.objects.get(id=self.u31.id)
+ self.u32 = models.User.objects.get(id=self.u32.id)
+ self.u33 = models.User.objects.get(id=self.u33.id)
+ self.u34 = models.User.objects.get(id=self.u34.id)
+ self.users = [
+ self.u11,
+ self.u12,
+ self.u13,
+ self.u14,
+ self.u21,
+ self.u22,
+ self.u23,
+ self.u24,
+ self.u31,
+ self.u32,
+ self.u33,
+ self.u34,
+ ]
+
+ def setUp(self):
+ #users for the question
+ self.u11 = create_user('user11', 'user11@example.com', status='m')
+ self.u12 = create_user('user12', 'user12@example.com', status='m')
+ self.u13 = create_user('user13', 'user13@example.com', status='m')
+ self.u14 = create_user('user14', 'user14@example.com', status='m')
+
+ #users for first answer
+ self.u21 = create_user('user21', 'user21@example.com', status='m')#post answer
+ self.u22 = create_user('user22', 'user22@example.com', status='m')#edit answer
+ self.u23 = create_user('user23', 'user23@example.com', status='m')
+ self.u24 = create_user('user24', 'user24@example.com', status='m')
+
+ #users for second answer
+ self.u31 = create_user('user31', 'user31@example.com', status='m')#post answer
+ self.u32 = create_user('user32', 'user32@example.com', status='m')#edit answer
+ self.u33 = create_user('user33', 'user33@example.com', status='m')
+ self.u34 = create_user('user34', 'user34@example.com', status='m')
+
+ #a hack to initialize .users list
+ self.reload_users()
+
+ #pre-populate askbot with some content
+ self.question = models.Question.objects.create_new(
+ title = 'test question',
+ author = self.u11,
+ added_at = datetime.datetime.now(),
+ wiki = False,
+ tagnames = 'test',
+ text = 'hey listen up',
+ )
+ self.comment12 = self.question.add_comment(
+ user = self.u12,
+ comment = 'comment12'
+ )
+ self.comment13 = self.question.add_comment(
+ user = self.u13,
+ comment = 'comment13'
+ )
+ self.answer1 = models.Answer.objects.create_new(
+ question = self.question,
+ author = self.u21,
+ added_at = datetime.datetime.now(),
+ text = 'answer1'
+ )
+ self.comment22 = self.answer1.add_comment(
+ user = self.u22,
+ comment = 'comment22'
+ )
+ self.comment23 = self.answer1.add_comment(
+ user = self.u23,
+ comment = 'comment23'
+ )
+ self.answer2 = models.Answer.objects.create_new(
+ question = self.question,
+ author = self.u31,
+ added_at = datetime.datetime.now(),
+ text = 'answer2'
+ )
+ self.comment32 = self.answer2.add_comment(
+ user = self.u32,
+ comment = 'comment32'
+ )
+ self.comment33 = self.answer2.add_comment(
+ user = self.u33,
+ comment = 'comment33'
+ )
+
+ def post_then_delete_answer_comment(self):
+ pass
+
+ def post_then_delete_answer(self):
+ pass
+
+ def post_then_delete_question_comment(self):
+ pass
+
+ def post_mention_in_question_then_delete(self):
+ pass
+
+ def post_mention_in_answer_then_delete(self):
+ pass
+
+ def post_mention_in_question_then_edit_out(self):
+ pass
+
+ def post_mention_in_answer_then_edit_out(self):
+ pass
+
+ def test_post_mention_in_question_comment_then_delete(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ comment = self.question.add_comment(
+ user = self.u11,
+ comment = '@user12 howyou doin?',
+ added_at = timestamp
+ )
+ comment.delete()
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 0)
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ comment = self.answer1.add_comment(
+ user = self.u21,
+ comment = 'hey @user22 blah',
+ added_at = timestamp
+ )
+ comment.delete()
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 0)
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_self_comments(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.add_comment(
+ user = self.u11,
+ comment = 'self-comment',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u12, self.u13]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer1.add_comment(
+ user = self.u21,
+ comment = 'self-comment 2',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u22, self.u23]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_self_mention_not_posting_in_comment_to_question1(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.add_comment(
+ user = self.u11,
+ comment = 'self-comment @user11',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u12, self.u13]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_self_mention_not_posting_in_comment_to_question2(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.add_comment(
+ user = self.u11,
+ comment = 'self-comment @user11 blah',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u12, self.u13]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_self_mention_not_posting_in_comment_to_answer(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer1.add_comment(
+ user = self.u21,
+ comment = 'self-comment 1 @user21',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u22, self.u23]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 0, 0, 0,
+ 0, 1, 1, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_comments_to_post_authors(self):
+ self.question.apply_edit(
+ edited_by = self.u14,
+ text = 'now much better',
+ comment = 'improved text'
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.add_comment(
+ user = self.u12,
+ comment = 'self-comment 1',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u11, self.u13, self.u14]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 1, 0, 1, 1,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+ self.answer1.apply_edit(
+ edited_by = self.u24,
+ text = 'now much better',
+ comment = 'improved text'
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer1.add_comment(
+ user = self.u22,
+ comment = 'self-comment 1',
+ added_at = timestamp
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u21, self.u23, self.u24]),
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 0, 0, 0,
+ 1, 0, 1, 1,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_question_edit(self):
+ """when question is edited
+ response receivers are question authors, commenters
+ and answer authors, but not answer commenters
+ """
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.apply_edit(
+ edited_by = self.u14,
+ text = 'waaay better question!',
+ comment = 'improved question',
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u11, self.u12, self.u13, self.u21, self.u31])
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 1, 1, 1, 0,
+ 1, 0, 0, 0,
+ 1, 0, 0, 0,
+ ]
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.question.apply_edit(
+ edited_by = self.u31,
+ text = 'waaay even better question!',
+ comment = 'improved question',
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set([self.u11, self.u12, self.u13, self.u14, self.u21])
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 1, 1, 1, 1,
+ 1, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+ def test_answer_edit(self):
+ """
+ """
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer1.apply_edit(
+ edited_by = self.u24,
+ text = 'waaay better answer!',
+ comment = 'improved answer1',
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set(
+ [
+ self.u11, self.u12, self.u13,
+ self.u21, self.u22, self.u23,
+ self.u31
+ ]
+ )
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 1, 1, 1, 0,
+ 1, 1, 1, 0,
+ 1, 0, 0, 0,
+ ]
+ )
+
+ def test_new_answer(self):
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer3 = models.Answer.objects.create_new(
+ question = self.question,
+ author = self.u11,
+ added_at = timestamp,
+ text = 'answer3'
+ )
+ time_end = datetime.datetime.now()
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set(
+ [
+ self.u12, self.u13,
+ self.u21, self.u31
+ ]
+ )
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 0, 1, 1, 0,
+ 1, 0, 0, 0,
+ 1, 0, 0, 0,
+ ]
+ )
+ self.reset_response_counts()
+ time.sleep(1)
+ timestamp = datetime.datetime.now()
+ self.answer3 = models.Answer.objects.create_new(
+ question = self.question,
+ author = self.u31,
+ added_at = timestamp,
+ text = 'answer4'
+ )
+ notifications = get_re_notif_after(timestamp)
+ self.assertEqual(len(notifications), 1)
+ self.assertEqual(
+ set(notifications[0].receiving_users.all()),
+ set(
+ [
+ self.u11, self.u12, self.u13,
+ self.u21
+ ]
+ )
+ )
+ self.reload_users()
+ self.assertEqual(
+ [
+ self.u11.response_count,
+ self.u12.response_count,
+ self.u13.response_count,
+ self.u14.response_count,
+ self.u21.response_count,
+ self.u22.response_count,
+ self.u23.response_count,
+ self.u24.response_count,
+ self.u31.response_count,
+ self.u32.response_count,
+ self.u33.response_count,
+ self.u34.response_count,
+ ],
+ [
+ 1, 1, 1, 0,
+ 1, 0, 0, 0,
+ 0, 0, 0, 0,
+ ]
+ )
+
+
diff --git a/askbot/tests/page_load_tests.py b/askbot/tests/page_load_tests.py
new file mode 100644
index 00000000..7a999a22
--- /dev/null
+++ b/askbot/tests/page_load_tests.py
@@ -0,0 +1,273 @@
+from django.test import TestCase
+from django.template import defaultfilters
+from django.core.urlresolvers import reverse
+from askbot import models
+
+class PageLoadTestCase(TestCase):
+ def try_url(
+ self,
+ url_name, status_code=200, template=None,
+ kwargs={}, redirect_url=None, follow=False,
+ data = {},
+ ):
+ url = reverse(url_name, kwargs = kwargs)
+ url_info = 'getting url %s' % url
+ if data:
+ url_info += '?' + '&'.join(['%s=%s' % (k, v) for k, v in data.iteritems()])
+ print url_info
+
+ r = self.client.get(url, data=data, follow=follow)
+ if hasattr(self.client, 'redirect_chain'):
+ print 'redirect chain: %s' % ','.join(self.client.redirect_chain)
+
+ self.assertEqual(r.status_code, status_code)
+
+ if template:
+ #asuming that there is more than one template
+ print 'templates are %s' % ','.join([t.name for t in r.template])
+ self.assertEqual(r.template[0].name, template)
+
+class PageLoadTests(PageLoadTestCase):
+ fixtures = ['tmp/fixture1.json', ]
+
+ def test_index(self):
+ #todo: merge this with all reader url tests
+ print 'trying to reverse index'
+ response = self.client.get(reverse('index'), follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.failUnless(len(response.redirect_chain) == 1)
+ self.failUnless(response.redirect_chain[0][0].endswith('/questions/'))
+ c = response.context[0]
+ t = response.template[0]
+ self.assertEqual(t.name, 'questions.html')
+ print 'index works'
+
+ def proto_test_non_user_urls(self):
+ """test all reader views thoroughly
+ on non-crashiness (no correcteness tests here)
+ """
+
+ self.try_url('sitemap')
+ self.try_url('feeds', kwargs={'url':'rss'})
+ self.try_url('about', template='about.html')
+ self.try_url('privacy', template='privacy.html')
+ self.try_url('logout', template='logout.html')
+ self.try_url('user_signin', template='authopenid/signin.html')
+ #todo: test different tabs
+ self.try_url('tags', template='tags.html')
+ self.try_url('tags', data={'sort':'name'}, template='tags.html')
+ self.try_url('tags', data={'sort':'used'}, template='tags.html')
+ self.try_url('badges', template='badges.html')
+ self.try_url(
+ 'answer_revisions',
+ template='revisions_answer.html',
+ kwargs={'id':38}
+ )
+ #todo: test different sort methods and scopes
+ self.try_url(
+ 'questions',
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'start_over':'true'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'unanswered'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'all'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'favorite'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'unanswered', 'sort':'latest'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'unanswered', 'sort':'oldest'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'unanswered', 'sort':'active'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'scope':'unanswered', 'sort':'inactive'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'sort':'hottest'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'sort':'coldest'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'sort':'mostvoted'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'questions',
+ data={'sort':'leastvoted'},
+ template='questions.html'
+ )
+ self.try_url(
+ 'question',
+ kwargs={'id':1},
+ )
+ self.try_url(
+ 'question',
+ kwargs={'id':2},
+ )
+ self.try_url(
+ 'question',
+ kwargs={'id':3},
+ )
+ self.try_url(
+ 'question_revisions',
+ kwargs={'id':17},
+ template='revisions_question.html'
+ )
+ self.try_url('users', template='users.html')
+ #todo: really odd naming conventions for sort methods
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'reputation'},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'newest'},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'last'},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'user'},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'reputation', 'page':2},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'newest', 'page':2},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'last', 'page':2},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'user', 'page':2},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'reputation', 'page':1},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'newest', 'page':1},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'last', 'page':1},
+ )
+ self.try_url(
+ 'users',
+ template='users.html',
+ data={'sort':'user', 'page':1},
+ )
+ self.try_url(
+ 'edit_user',
+ template='authopenid/signin.html',
+ kwargs={'id':4},
+ status_code=200,
+ follow=True,
+ )
+
+ def test_non_user_urls(self):
+ self.proto_test_non_user_urls()
+
+ #def test_non_user_urls_logged_in(self):
+ #user = User.objects.get(id=1)
+ #somehow login this user
+ #self.proto_test_non_user_urls()
+
+ def test_user_urls(self):
+ user = models.User.objects.get(id=2)
+ name_slug = defaultfilters.slugify(user.username)
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'stats'},
+ template='user_stats.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'recent'},
+ template='user_recent.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'responses'},
+ status_code=404,
+ template='404.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'reputation'},
+ template='user_reputation.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'votes'},
+ status_code=404,
+ template='404.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'favorites'},
+ template='user_favorites.html'
+ )
+ self.try_url(
+ 'user_profile',
+ kwargs={'id': 2, 'slug': name_slug},
+ data={'sort':'email_subscriptions'},
+ status_code=404,
+ template='404.html'
+ )
diff --git a/askbot/tests/permission_assertion_tests.py b/askbot/tests/permission_assertion_tests.py
new file mode 100644
index 00000000..dd84a85b
--- /dev/null
+++ b/askbot/tests/permission_assertion_tests.py
@@ -0,0 +1,426 @@
+from django.test import TestCase
+from django.core import exceptions
+from askbot.tests import utils
+from askbot.conf import settings as askbot_settings
+from askbot import models
+
+class PermissionAssertionTestCase(TestCase):
+ """base TestCase class for permission
+ assertion tests
+
+ subclass may redefine method extraSetUp
+ """
+
+ def setUp(self):
+ self.user = utils.create_user(
+ username = 'test',
+ email = 'test@test.com'
+ )
+ self.extraSetUp()
+
+ def extraSetUp(self):
+ pass
+
+ def create_other_user(self):
+ return utils.create_user(
+ username = 'other',
+ email = 'other@test.com'
+ )
+
+ def post_question(self, author = None):
+ if author is None:
+ author = self.user
+ return author.post_question(
+ title = 'test question title',
+ body_text = 'test question body',
+ tags = 'test'
+ )
+
+ def post_answer(self, question = None, author = None):
+ if author is None:
+ author = self.user
+ return author.post_answer(
+ question = question,
+ body_text = 'test answer'
+ )
+
+
+class CommentPermissionAssertionTests(PermissionAssertionTestCase):
+
+ def extraSetUp(self):
+ self.min_rep = askbot_settings.MIN_REP_TO_LEAVE_COMMENTS
+ self.other_user = self.create_other_user()
+
+ def test_blocked_user_cannot_comment_own_question(self):
+ question = self.post_question()
+
+ self.user.set_status('b')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.post_comment,
+ parent_post = question,
+ body_text = 'test comment'
+ )
+
+ def test_blocked_user_cannot_comment_own_answer(self):
+ question = self.post_question()
+ answer = self.post_answer(question)
+
+ self.user.set_status('b')
+
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.post_comment,
+ parent_post = answer,
+ body_text = 'test comment'
+ )
+
+ def test_blocked_user_cannot_delete_own_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.user.set_status('b')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.delete_post,
+ post = comment
+ )
+
+ def test_low_rep_user_cannot_delete_others_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ assert(
+ self.other_user.reputation < \
+ askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
+ )
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.other_user.delete_post,
+ post = comment
+ )
+
+ def test_high_rep_user_can_delete_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.other_user.reputation = \
+ askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
+
+ self.other_user.delete_comment(comment)
+
+ def test_low_rep_user_can_delete_own_comment(self):
+ question = self.post_question()
+ answer = self.other_user.post_answer(
+ question = question,
+ body_text = 'test answer'
+ )
+ comment = self.user.post_comment(
+ parent_post = answer,
+ body_text = 'test comment'
+ )
+ assert(
+ self.user.reputation < \
+ askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS
+ )
+ self.user.delete_comment(comment)
+
+ def test_moderator_can_delete_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.other_user.set_status('m')
+ self.other_user.delete_comment(comment)
+
+ def test_admin_can_delete_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.other_user.is_superuser = True
+ self.other_user.delete_comment(comment)
+
+ def test_high_rep_suspended_user_cannot_delete_others_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.other_user.reputation = \
+ askbot_settings.MIN_REP_TO_DELETE_OTHERS_COMMENTS + 1
+ self.other_user.set_status('s')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.other_user.delete_post,
+ post = comment
+ )
+
+ def test_suspended_user_can_delete_own_comment(self):
+ question = self.post_question()
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.user.set_status('s')
+ self.user.delete_comment(comment)
+
+ def test_low_rep_user_cannot_comment_others(self):
+ question = self.post_question(
+ author = self.other_user
+ )
+ assert(self.user.reputation < self.min_rep)
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.post_comment,
+ parent_post = question,
+ body_text = 'test comment'
+ )
+
+ def test_low_rep_user_can_comment_others_answer_to_own_question(self):
+ question = self.post_question()
+ assert(self.user.reputation < self.min_rep)
+ answer = self.other_user.post_answer(
+ question = question,
+ body_text = 'test answer'
+ )
+ comment = self.user.post_comment(
+ parent_post = answer,
+ body_text = 'test comment'
+ )
+ self.assertTrue(isinstance(comment, models.Comment))
+
+ def test_high_rep_user_can_comment(self):
+ question = self.post_question(
+ author = self.other_user
+ )
+ self.user.reputation = self.min_rep
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.assertTrue(isinstance(comment, models.Comment))
+
+ def test_suspended_user_cannot_comment_others_question(self):
+ question = self.post_question(author = self.other_user)
+ self.user.set_status('s')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.post_comment,
+ parent_post = question,
+ body_text = 'test comment'
+ )
+
+ def test_suspended_user_can_comment_own_question(self):
+ question = self.post_question()
+ self.user.set_status('s')
+ comment = self.user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.assertTrue(isinstance(comment, models.Comment))
+
+ def test_low_rep_admin_can_comment_others_question(self):
+ question = self.post_question()
+ self.other_user.is_superuser = True
+ assert(self.other_user.is_administrator())
+ assert(self.other_user.reputation < self.min_rep)
+ comment = self.other_user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.assertTrue(isinstance(comment, models.Comment))
+
+ def test_low_rep_moderator_can_comment_others_question(self):
+ question = self.post_question()
+ self.other_user.set_status('m')
+ assert(self.other_user.is_moderator())
+ assert(self.other_user.reputation < self.min_rep)
+ comment = self.other_user.post_comment(
+ parent_post = question,
+ body_text = 'test comment'
+ )
+ self.assertTrue(isinstance(comment, models.Comment))
+
+#def user_assert_can_post_comment(self, parent_post):
+#def user_assert_can_delete_comment(self, comment = None):
+
+#def user_assert_can_vote_for_post(
+#def user_assert_can_revoke_old_vote(self, vote):
+
+#def user_assert_can_flag_offensive(self):
+
+#def user_assert_can_upload_file(request_user):
+#def user_assert_can_post_question(self):
+#def user_assert_can_post_answer(self):
+#def user_assert_can_edit_post(self, post = None):
+#def user_assert_can_delete_Post(self, post = None):
+#def user_assert_can_close_question(self, question = None):
+#def user_assert_can_retag_questions(self):
+
+class VotePermissionAssertionTests(PermissionAssertionTestCase):
+ """Tests permission for voting
+ """
+ def extraSetUp(self):
+ self.min_rep_up = askbot_settings.MIN_REP_TO_VOTE_UP
+ self.min_rep_down = askbot_settings.MIN_REP_TO_VOTE_DOWN
+ self.other_user = self.create_other_user()
+
+ def assert_cannot_vote(self, user = None, dir = None):
+ assert(dir in ('up', 'down'))
+
+ vote_func = self.get_vote_function(
+ user = user,
+ dir = dir
+ )
+
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ vote_func,
+ self.question,
+ )
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ vote_func,
+ self.answer,
+ )
+
+ def prepare_data(self, status = 'a', rep = 1):
+ self.question = self.post_question()
+ self.answer = self.post_answer(question = self.question)
+ self.other_user.reputation = rep
+ self.other_user.set_status(status)
+
+ def bad_user_cannot_vote(self, status = 'a', rep = 1, dir = 'up'):
+ """dir - vote direction up/down
+ rep - reputation
+ """
+ self.prepare_data(status = status)
+
+ self.assert_cannot_vote(
+ user = self.other_user,
+ dir = dir
+ )
+
+ def get_vote_function(self, dir = None, user = None):
+
+ def vote_func(post):
+ user.assert_can_vote_for_post(post = post, direction = dir)
+
+ return vote_func
+
+
+ def good_user_can_vote(self, user = None, dir = 'up'):
+
+ if user is None:
+ user = self.other_user
+
+ vote_func = self.get_vote_function(dir = dir, user = user)
+
+ vote_func(self.question)
+ vote_func(self.answer)
+
+
+ def test_blocked_user_cannot_vote(self):
+ self.bad_user_cannot_vote(status = 'b')
+
+ def test_suspended_user_cannot_vote(self):
+ self.bad_user_cannot_vote(status = 's')
+
+ def test_low_rep_user_cannot_upvote(self):
+ self.bad_user_cannot_vote(dir = 'up')
+
+ def test_low_rep_user_cannot_downvote(self):
+ self.bad_user_cannot_vote(dir = 'down')
+
+ def test_high_rep_user_can_upvote(self):
+ self.prepare_data(rep = self.min_rep_up)
+ self.good_user_can_vote(dir = 'up')
+
+ def test_high_rep_user_can_downvote(self):
+ self.prepare_data(rep = self.min_rep_down)
+ self.good_user_can_vote(dir = 'down')
+
+ def test_low_rep_admins_can_upvote_others(self):
+ self.prepare_data()
+ self.other_user.set_status('m')
+ self.good_user_can_vote(dir = 'up')
+
+ def test_low_rep_admins_can_downvote_others(self):
+ self.prepare_data()
+ self.other_user.set_status('m')
+ self.good_user_can_vote(dir = 'down')
+
+ def test_admins_cannot_upvote_self(self):
+ self.prepare_data()
+ self.user.set_status('m')
+ self.assert_cannot_vote(
+ user = self.user,
+ dir = 'up'
+ )
+
+ def test_admins_cannot_downvote_self(self):
+ self.prepare_data()
+ self.user.set_status('m')
+ self.assert_cannot_vote(
+ user = self.user,
+ dir = 'down'
+ )
+
+class UploadPermissionAssertionTests(PermissionAssertionTestCase):
+ """Tests permissions for file uploads
+ """
+
+ def extraSetUp(self):
+ self.min_rep = askbot_settings.MIN_REP_TO_UPLOAD_FILES
+
+ def test_suspended_user_cannot_upload(self):
+ self.user.set_status('s')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.assert_can_upload_file
+ )
+
+ def test_blocked_user_cannot_upload(self):
+ self.user.set_status('b')
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.assert_can_upload_file
+ )
+ def test_low_rep_user_cannot_upload(self):
+ self.user.reputation = self.min_rep - 1
+ self.assertRaises(
+ exceptions.PermissionDenied,
+ self.user.assert_can_upload_file
+ )
+
+ def test_high_rep_user_can_upload(self):
+ self.user.reputation = self.min_rep
+ try:
+ self.user.assert_can_upload_file()
+ except exceptions.PermissionDenied:
+ self.fail('high rep user must be able to upload')
+
+ def test_low_rep_moderator_can_upload(self):
+ assert(self.user.reputation < self.min_rep)
+ self.user.set_status('m')
+ try:
+ self.user.assert_can_upload_file()
+ except exceptions.PermissionDenied:
+ self.fail('high rep user must be able to upload')
+
+ def test_low_rep_administrator_can_upload(self):
+ assert(self.user.reputation < self.min_rep)
+ self.user.is_superuser = True
+ try:
+ self.user.assert_can_upload_file()
+ except exceptions.PermissionDenied:
+ self.fail('high rep user must be able to upload')
diff --git a/askbot/tests/utils.py b/askbot/tests/utils.py
new file mode 100644
index 00000000..46d8451a
--- /dev/null
+++ b/askbot/tests/utils.py
@@ -0,0 +1,30 @@
+"""utility functions used by Askbot test cases
+"""
+from askbot import models
+
+def create_user(
+ username = None,
+ email = None,
+ notification_schedule = None,
+ date_joined = None,
+ status = 'a'
+ ):
+ """Creates a user and sets default update subscription
+ settings"""
+ user = models.User.objects.create_user(username, email)
+ if date_joined is not None:
+ user.date_joined = date_joined
+ user.save()
+ user.set_status(status)
+ if notification_schedule == None:
+ notification_schedule = models.EmailFeedSetting.NO_EMAIL_SCHEDULE
+
+ for feed_type, frequency in notification_schedule.items():
+ feed = models.EmailFeedSetting(
+ feed_type = feed_type,
+ frequency = frequency,
+ subscriber = user
+ )
+ feed.save()
+ return user
+
diff --git a/askbot/views/commands.py b/askbot/views/commands.py
index 07cc57a5..ca48c9bb 100644
--- a/askbot/views/commands.py
+++ b/askbot/views/commands.py
@@ -253,21 +253,26 @@ def vote(request, id):
item = FlaggedItem(user=request.user, content_object=post, flagged_at=datetime.datetime.now())
auth.onFlaggedItem(item, post, request.user)
response_data['count'] = post.offensive_flag_count
+
elif vote_type in ['9', '10']:
+ #delete question or answer
post = question
post_id = id
if vote_type == '10':
post_id = request.POST.get('postId')
post = get_object_or_404(Answer, id=post_id)
- if not auth.can_delete_post(request.user, post):
- response_data['allowed'] = -2
- elif post.deleted == True:
+ if post.deleted == True:
logging.debug('debug restoring post in view')
auth.onDeleteCanceled(post, request.user)
response_data['status'] = 1
else:
- auth.onDeleted(post, request.user)
+ try:
+ request.user.delete_post(post = post)
+ except exceptions.PermissionDenied, e:
+ response_data['allowed'] = -2
+ request.user.message_set.create(message = str(e))
+
elif vote_type == '11':#subscribe q updates
user = request.user
if user.is_authenticated():