/actstream/tests.py

https://bitbucket.org/rafacouto/agora-ciudadana-fork · Python · 369 lines · 280 code · 63 blank · 26 comment · 16 complexity · 839eca24b1adf1d259c1edad6e0be846 MD5 · raw file

  1. from random import choice
  2. from django.db import connection
  3. from django.db.models import get_model
  4. from django.test import TestCase
  5. from django.conf import settings
  6. from django.contrib.auth.models import User, AnonymousUser, Group
  7. from django.contrib.contenttypes.models import ContentType
  8. from django.contrib.sites.models import Site
  9. from django.template.loader import Template, Context
  10. from actstream.models import Action, Follow, model_stream, user_stream,\
  11. setup_generic_relations
  12. from actstream.actions import follow, unfollow
  13. from actstream.exceptions import ModelNotActionable
  14. from actstream.signals import action
  15. from actstream import settings as actstream_settings
  16. class ActivityBaseTestCase(TestCase):
  17. actstream_models = ()
  18. def setUp(self):
  19. self.old_MODELS = actstream_settings.MODELS
  20. actstream_settings.MODELS = {}
  21. for model in self.actstream_models:
  22. actstream_settings.MODELS[model.lower()] = \
  23. get_model(*model.split('.'))
  24. setup_generic_relations()
  25. def tearDown(self):
  26. actstream_settings.MODELS = self.old_MODELS
  27. class ActivityTestCase(ActivityBaseTestCase):
  28. urls = 'actstream.urls'
  29. actstream_models = ('auth.User', 'auth.Group', 'sites.Site')
  30. def setUp(self):
  31. super(ActivityTestCase, self).setUp()
  32. self.group = Group.objects.create(name='CoolGroup')
  33. self.user1 = User.objects.get_or_create(username='admin')[0]
  34. self.user1.set_password('admin')
  35. self.user1.is_superuser = self.user1.is_staff = True
  36. self.user1.save()
  37. self.user2 = User.objects.get_or_create(username='Two')[0]
  38. # User1 joins group
  39. self.user1.groups.add(self.group)
  40. action.send(self.user1, verb='joined', target=self.group)
  41. # User1 follows User2
  42. follow(self.user1, self.user2)
  43. # User2 joins group
  44. self.user2.groups.add(self.group)
  45. action.send(self.user2, verb='joined', target=self.group)
  46. # User2 follows group
  47. follow(self.user2, self.group)
  48. # User1 comments on group
  49. # Use a site object here and predict the "__unicode__ method output"
  50. action.send(self.user1, verb='commented on', target=self.group)
  51. self.comment = Site.objects.create(
  52. domain="admin: Sweet Group!...")
  53. # Group responds to comment
  54. action.send(self.group, verb='responded to', target=self.comment)
  55. def test_aauser1(self):
  56. self.assertEqual(map(unicode, self.user1.actor_actions.all()), [
  57. u'admin commented on CoolGroup 0 minutes ago',
  58. u'admin started following Two 0 minutes ago',
  59. u'admin joined CoolGroup 0 minutes ago',
  60. ])
  61. def test_user2(self):
  62. self.assertEqual(map(unicode, Action.objects.actor(self.user2)), [
  63. u'Two started following CoolGroup 0 minutes ago',
  64. u'Two joined CoolGroup 0 minutes ago',
  65. ])
  66. def test_group(self):
  67. self.assertEqual(map(unicode, Action.objects.actor(self.group)),
  68. [u'CoolGroup responded to admin: Sweet Group!... 0 minutes ago'])
  69. def test_empty_follow_stream(self):
  70. unfollow(self.user1, self.user2)
  71. self.assert_(not user_stream(self.user1))
  72. def test_stream(self):
  73. self.assertEqual(map(unicode, Action.objects.user(self.user1)), [
  74. u'Two started following CoolGroup 0 minutes ago',
  75. u'Two joined CoolGroup 0 minutes ago',
  76. ])
  77. self.assertEqual(map(unicode, Action.objects.user(self.user2)),
  78. [u'CoolGroup responded to admin: Sweet Group!... 0 minutes ago'])
  79. def test_stream_stale_follows(self):
  80. """
  81. Action.objects.user() should ignore Follow objects with stale actor
  82. references.
  83. """
  84. self.user2.delete()
  85. self.assert_(not 'Two' in str(Action.objects.user(self.user1)))
  86. def test_rss(self):
  87. rss = self.client.get('/feed/').content
  88. self.assert_(rss.startswith('<?xml version="1.0" encoding="utf-8"?>\n'
  89. '<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">'))
  90. self.assert_(rss.find('Activity feed for your followed actors') > -1)
  91. def test_atom(self):
  92. atom = self.client.get('/feed/atom/').content
  93. self.assert_(atom.startswith('<?xml version="1.0" encoding="utf-8"?>\n'
  94. '<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="%s">' %
  95. settings.LANGUAGE_CODE))
  96. self.assert_(atom.find('Activity feed for your followed actors') > -1)
  97. def test_action_object(self):
  98. action.send(self.user1, verb='created comment',
  99. action_object=self.comment, target=self.group)
  100. created_action = Action.objects.get(verb='created comment')
  101. self.assertEqual(created_action.actor, self.user1)
  102. self.assertEqual(created_action.action_object, self.comment)
  103. self.assertEqual(created_action.target, self.group)
  104. self.assertEqual(unicode(created_action),
  105. u'admin created comment admin: Sweet Group!... on CoolGroup 0 '
  106. 'minutes ago')
  107. def test_doesnt_generate_duplicate_follow_records(self):
  108. g = Group.objects.get_or_create(name='DupGroup')[0]
  109. s = User.objects.get_or_create(username='dupuser')[0]
  110. f1 = follow(s, g)
  111. self.assertTrue(f1 is not None, "Should have received a new follow "
  112. "record")
  113. self.assertTrue(isinstance(f1, Follow), "Returns a Follow object")
  114. self.assertEquals(1, Follow.objects.filter(user=s, object_id=g.pk,
  115. content_type=ContentType.objects.get_for_model(g)).count(),
  116. "Should only have 1 follow record here")
  117. f2 = follow(s, g)
  118. self.assertEquals(1, Follow.objects.filter(user=s, object_id=g.pk,
  119. content_type=ContentType.objects.get_for_model(g)).count(),
  120. "Should still only have 1 follow record here")
  121. self.assertTrue(f2 is not None, "Should have received a Follow object")
  122. self.assertTrue(isinstance(f2, Follow), "Returns a Follow object")
  123. self.assertEquals(f1, f2, "Should have received the same Follow "
  124. "object that I first submitted")
  125. def test_zzzz_no_orphaned_actions(self):
  126. actions = self.user1.actor_actions.count()
  127. self.user2.delete()
  128. self.assertEqual(actions, self.user1.actor_actions.count() + 1)
  129. def test_generic_relation_accessors(self):
  130. self.assertEqual(self.user2.actor_actions.count(), 2)
  131. self.assertEqual(self.user2.target_actions.count(), 1)
  132. self.assertEqual(self.user2.action_object_actions.count(), 0)
  133. def test_bad_actionable_model(self):
  134. self.assertRaises(ModelNotActionable, follow, self.user1,
  135. ContentType.objects.get_for_model(self.user1))
  136. def test_hidden_action(self):
  137. action = self.user1.actor_actions.all()[0]
  138. action.public = False
  139. action.save()
  140. self.assert_(not action in self.user1.actor_actions.public())
  141. def test_tag_follow_url(self):
  142. src = '{% load activity_tags %}{% activity_follow_url user %}'
  143. output = Template(src).render(Context({'user': self.user1}))
  144. ct = ContentType.objects.get_for_model(User)
  145. self.assertEqual(output, '/follow/%s/%s/' % (ct.pk, self.user1.pk))
  146. def test_tag_follow_label(self):
  147. src = '{% load activity_tags %}'\
  148. '{% activity_follow_label other_user yup nope %}'
  149. # Anonymous.
  150. output = Template(src).render(Context({'other_user': self.user1}))
  151. self.assertEqual(output, 'nope')
  152. output = Template(src).render(Context({'user': AnonymousUser(),
  153. 'other_user': self.user1}))
  154. self.assertEqual(output, 'nope')
  155. # Non follower (user2 does not follow user1).
  156. output = Template(src).render(Context({'user': self.user2,
  157. 'other_user': self.user1}))
  158. self.assertEqual(output, 'nope')
  159. # Follower (user1 follows user2).
  160. output = Template(src).render(Context({'user': self.user1,
  161. 'other_user': self.user2}))
  162. self.assertEqual(output, 'yup')
  163. def test_model_actions_with_kwargs(self):
  164. """
  165. Testing the model_actions method of the ActionManager
  166. by passing kwargs
  167. """
  168. self.assertEqual(map(unicode, model_stream(self.user1, verb='commented on')), [
  169. u'admin commented on CoolGroup 0 minutes ago',
  170. ])
  171. def test_user_stream_with_kwargs(self):
  172. """
  173. Testing the user method of the ActionManager by passing additional
  174. filters in kwargs
  175. """
  176. self.assertEqual(map(unicode, Action.objects.user(self.user1, verb='joined')), [
  177. u'Two joined CoolGroup 0 minutes ago',
  178. ])
  179. def test_is_following_filter(self):
  180. src = '{% load activity_tags %}{% if user|is_following:group %}yup{% endif %}'
  181. self.assertEqual(Template(src).render(Context({
  182. 'user': self.user2, 'group': self.group
  183. })), u'yup')
  184. self.assertEqual(Template(src).render(Context({
  185. 'user': self.user1, 'group': self.group
  186. })), u'')
  187. class ZombieTest(ActivityBaseTestCase):
  188. actstream_models = ('auth.User',)
  189. human = 10
  190. zombie = 1
  191. def setUp(self):
  192. super(ZombieTest, self).setUp()
  193. settings.DEBUG = True
  194. player_generator = lambda n, count: [User.objects.create(
  195. username='%s%d' % (n, i)) for i in range(count)]
  196. self.humans = player_generator('human', self.human)
  197. self.zombies = player_generator('zombie', self.zombie)
  198. self.zombie_apocalypse()
  199. def tearDown(self):
  200. settings.DEBUG = False
  201. super(ZombieTest, self).tearDown()
  202. def zombie_apocalypse(self):
  203. humans = self.humans[:]
  204. zombies = self.zombies[:]
  205. while humans:
  206. for z in self.zombies:
  207. victim = choice(humans)
  208. humans.remove(victim)
  209. zombies.append(victim)
  210. action.send(z, verb='killed', target=victim)
  211. if not humans:
  212. break
  213. def check_query_count(self, queryset):
  214. ci = len(connection.queries)
  215. result = list([map(unicode, (x.actor, x.target, x.action_object))
  216. for x in queryset])
  217. self.assertTrue(len(connection.queries) - ci <= 4,
  218. 'Too many queries, got %d expected no more than 4' %
  219. len(connection.queries))
  220. return result
  221. def test_query_count(self):
  222. queryset = model_stream(User)
  223. result = self.check_query_count(queryset)
  224. self.assertEqual(len(result), 10)
  225. def test_query_count_sliced(self):
  226. queryset = model_stream(User)[:5]
  227. result = self.check_query_count(queryset)
  228. self.assertEqual(len(result), 5)
  229. class GFKManagerTestCase(TestCase):
  230. def setUp(self):
  231. self.user_ct = ContentType.objects.get_for_model(User)
  232. self.group_ct = ContentType.objects.get_for_model(Group)
  233. self.group, _ = Group.objects.get_or_create(name='CoolGroup')
  234. self.user1, _ = User.objects.get_or_create(username='admin')
  235. self.user2, _ = User.objects.get_or_create(username='Two')
  236. self.user3, _ = User.objects.get_or_create(username='Three')
  237. self.user4, _ = User.objects.get_or_create(username='Four')
  238. Action.objects.get_or_create(
  239. actor_content_type=self.user_ct,
  240. actor_object_id=self.user1.id,
  241. verb='followed',
  242. target_content_type=self.user_ct,
  243. target_object_id=self.user2.id
  244. )
  245. Action.objects.get_or_create(
  246. actor_content_type=self.user_ct,
  247. actor_object_id=self.user1.id,
  248. verb='followed',
  249. target_content_type=self.user_ct,
  250. target_object_id=self.user3.id
  251. )
  252. Action.objects.get_or_create(
  253. actor_content_type=self.user_ct,
  254. actor_object_id=self.user1.id,
  255. verb='followed',
  256. target_content_type=self.user_ct,
  257. target_object_id=self.user4.id
  258. )
  259. Action.objects.get_or_create(
  260. actor_content_type=self.user_ct,
  261. actor_object_id=self.user1.id,
  262. verb='joined',
  263. target_content_type=self.group_ct,
  264. target_object_id=self.group.id
  265. )
  266. def test_fetch_generic_relations(self):
  267. # baseline without fetch_generic_relations
  268. _actions = Action.objects.filter(actor_content_type=self.user_ct,
  269. actor_object_id=self.user1.id)
  270. actions = lambda: _actions._clone()
  271. num_content_types = len(set(actions().values_list(
  272. 'target_content_type_id', flat=True)))
  273. n = actions().count()
  274. # compare to fetching only 1 generic relation
  275. self.assertNumQueries(n + 1,
  276. lambda: [a.target for a in actions()])
  277. self.assertNumQueries(num_content_types + 2,
  278. lambda: [a.target for a in
  279. actions().fetch_generic_relations('target')])
  280. action_targets = [(a.id, a.target) for a in actions()]
  281. action_targets_fetch_generic = [(a.id, a.target) for a in
  282. actions().fetch_generic_relations('target')]
  283. self.assertEqual(action_targets, action_targets_fetch_generic)
  284. # compare to fetching all generic relations
  285. num_content_types = len(set(sum(actions().values_list(
  286. 'actor_content_type_id', 'target_content_type_id'), ())))
  287. self.assertNumQueries(2 * n + 1,
  288. lambda: [(a.actor, a.target) for a in actions()])
  289. self.assertNumQueries(num_content_types + 2,
  290. lambda: [(a.actor, a.target) for a in
  291. actions().fetch_generic_relations()])
  292. action_actor_targets = [(a.id, a.actor, a.target) for a in actions()]
  293. action_actor_targets_fetch_generic_all = [
  294. (a.id, a.actor, a.target) for a in
  295. actions().fetch_generic_relations()]
  296. self.assertEqual(action_actor_targets,
  297. action_actor_targets_fetch_generic_all)
  298. # fetch only 1 generic relation, but access both gfks
  299. self.assertNumQueries(n + num_content_types + 2,
  300. lambda: [(a.actor, a.target) for a in
  301. actions().fetch_generic_relations('target')])
  302. action_actor_targets_fetch_generic_target = [
  303. (a.id, a.actor, a.target) for a in
  304. actions().fetch_generic_relations('target')]
  305. self.assertEqual(action_actor_targets,
  306. action_actor_targets_fetch_generic_target)