tests.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import json
  2. import warnings
  3. from django.core import mail
  4. from django.core.signing import BadSignature
  5. from django.contrib.auth.models import User
  6. from django.test import TestCase, Client, override_settings
  7. from contribmap.models import Contrib
  8. from contribmap.forms import PublicContribForm
  9. from contribmap.tokens import ContribTokenManager, URLTokenManager
  10. class APITestClient(Client):
  11. def json_get(self, *args, **kwargs):
  12. """ Annotate the response with a .data containing parsed JSON
  13. """
  14. response = super().get(*args, **kwargs)
  15. response.data = json.loads(response.content.decode('utf-8'))
  16. return response
  17. class APITestCase(TestCase):
  18. def setUp(self):
  19. super().setUp()
  20. self.client = APITestClient()
  21. class TestContrib(TestCase):
  22. def test_comma_separatedcharfield(self):
  23. co = Contrib(name='foo', orientations=['SO', 'NE'],
  24. contrib_type=Contrib.CONTRIB_CONNECT,
  25. latitude=0.5, longitude=0.5,
  26. )
  27. co.save()
  28. self.assertEqual(
  29. Contrib.objects.get(name='foo').orientations,
  30. ['SO', 'NE'])
  31. co.orientations = ['S']
  32. co.save()
  33. class TestContribPrivacy(TestCase):
  34. def test_always_private_field(self):
  35. c = Contrib.objects.create(
  36. name='John',
  37. phone='010101010101',
  38. contrib_type=Contrib.CONTRIB_CONNECT,
  39. latitude=0.5,
  40. longitude=0.5,
  41. )
  42. self.assertEqual(c.get_public_field('phone'), None)
  43. def test_public_field(self):
  44. c = Contrib.objects.create(
  45. name='John',
  46. phone='010101010101',
  47. contrib_type=Contrib.CONTRIB_CONNECT,
  48. privacy_name=True,
  49. latitude=0.5,
  50. longitude=0.5,
  51. )
  52. self.assertEqual(c.get_public_field('name'), 'John')
  53. def test_public_callable_field(self):
  54. c = Contrib.objects.create(
  55. name='John',
  56. phone='010101010101',
  57. orientations=['N'],
  58. contrib_type=Contrib.CONTRIB_CONNECT,
  59. privacy_name=True,
  60. latitude=0.5,
  61. longitude=0.5,
  62. )
  63. self.assertEqual(c.get_public_field('angles'), [[-23, 22]])
  64. def test_private_field(self):
  65. c = Contrib.objects.create(
  66. name='John',
  67. phone='010101010101',
  68. contrib_type=Contrib.CONTRIB_CONNECT,
  69. latitude=0.5,
  70. longitude=0.5,
  71. )
  72. self.assertEqual(c.privacy_name, False)
  73. self.assertEqual(c.get_public_field('name'), None)
  74. class TestViews(APITestCase):
  75. def mk_contrib_post_data(self, *args, **kwargs):
  76. post_data = {
  77. 'roof': True,
  78. 'privacy_place_details': True,
  79. 'privacy_coordinates': True,
  80. 'phone': '0202020202',
  81. 'orientations': ('N', 'NO', 'O', 'SO', 'S', 'SE', 'E', 'NE'),
  82. 'orientation': 'all',
  83. 'name': 'JohnCleese',
  84. 'longitude': -1.553621,
  85. 'latitude': 47.218371,
  86. 'floor_total': '2',
  87. 'floor': 1,
  88. 'email': 'coucou@example.com',
  89. 'contrib_type': 'connect',
  90. 'connect_local': 'on',
  91. }
  92. post_data.update(kwargs)
  93. return post_data
  94. def test_public_json(self):
  95. response = self.client.json_get('/map/public.json')
  96. self.assertEqual(response.status_code, 200)
  97. self.assertEqual(len(response.data['features']), 0)
  98. Contrib.objects.create(
  99. name='John',
  100. phone='010101010101',
  101. contrib_type=Contrib.CONTRIB_CONNECT,
  102. privacy_coordinates=True,
  103. latitude=0.5,
  104. longitude=0.5,
  105. )
  106. response = self.client.json_get('/map/public.json')
  107. self.assertEqual(response.status_code, 200)
  108. self.assertEqual(len(response.data['features']), 1)
  109. def test_private_json(self):
  110. self.client.force_login(
  111. User.objects.create(username='foo', is_staff=False))
  112. response = self.client.get('/map/private.json')
  113. self.assertEqual(response.status_code, 403)
  114. def test_private_json_staff(self):
  115. self.client.force_login(
  116. User.objects.create(username='foo', is_staff=True))
  117. response = self.client.get('/map/private.json')
  118. self.assertEqual(response.status_code, 200)
  119. @override_settings(NOTIFICATION_EMAILS=['foo@example.com'])
  120. def test_add_contrib_sends_moderator_email(self):
  121. post_data = self.mk_contrib_post_data({'name': 'JohnCleese'})
  122. del post_data['email']
  123. response = self.client.post('/map/contribute', post_data)
  124. self.assertEqual(response.status_code, 302)
  125. self.assertEqual(len(mail.outbox), 1)
  126. self.assertIn('JohnCleese', mail.outbox[0].subject)
  127. self.assertIn('JohnCleese', mail.outbox[0].body)
  128. class TestForms(TestCase):
  129. valid_data = {
  130. 'roof': True,
  131. 'privacy_place_details': True,
  132. 'privacy_coordinates': True,
  133. 'orientations': ['N'],
  134. 'orientation': 'all',
  135. 'name': 'JohnCleese',
  136. 'longitude': -1.553621,
  137. 'email': 'foo@example.com',
  138. 'phone': '0202020202',
  139. 'latitude': 47.218371,
  140. 'floor_total': '2',
  141. 'floor': 1,
  142. 'contrib_type': 'connect',
  143. 'connect_local': 'on',
  144. }
  145. def test_contact_validation(self):
  146. no_contact, phone_contact, email_contact, both_contact = [
  147. self.valid_data.copy() for i in range(4)]
  148. del phone_contact['email']
  149. del email_contact['phone']
  150. del no_contact['phone']
  151. del no_contact['email']
  152. both_contact.update(phone_contact)
  153. both_contact.update(email_contact)
  154. self.assertFalse(PublicContribForm(no_contact).is_valid())
  155. self.assertTrue(PublicContribForm(phone_contact).is_valid())
  156. self.assertTrue(PublicContribForm(email_contact).is_valid())
  157. self.assertTrue(PublicContribForm(both_contact).is_valid())
  158. def test_floors_validation(self):
  159. invalid_floors = self.valid_data.copy()
  160. invalid_floors['floor'] = 2
  161. invalid_floors['floor_total'] = 1
  162. self.assertFalse(PublicContribForm(invalid_floors).is_valid())
  163. self.assertTrue(PublicContribForm(self.valid_data).is_valid())
  164. invalid_floors['floor'] = None
  165. invalid_floors['floor_total'] = None
  166. self.assertTrue(PublicContribForm(invalid_floors).is_valid())
  167. def test_share_fields_validation(self):
  168. data = self.valid_data.copy()
  169. data['contrib_type'] = 'share'
  170. self.assertFalse(PublicContribForm(data).is_valid())
  171. data['access_type'] = 'cable'
  172. self.assertTrue(PublicContribForm(data).is_valid())
  173. @override_settings(NOTIFICATION_EMAILS=['foo@example.com'])
  174. def test_add_contrib_like_a_robot(self):
  175. robot_data = self.valid_data.copy()
  176. robot_data['human_field'] = 'should contain no value'
  177. response = self.client.post('/map/contribute', robot_data)
  178. self.assertEqual(response.status_code, 403)
  179. self.assertEqual(len(mail.outbox), 0)
  180. class TestDataImport(TestCase):
  181. fixtures = ['bottle_data.yaml']
  182. @classmethod
  183. def setUpClass(cls, *args, **kwargs):
  184. # Silence the warnings about naive datetimes contained in the yaml.
  185. with warnings.catch_warnings(): # Scope warn catch to this block
  186. warnings.simplefilter('ignore', RuntimeWarning)
  187. return super().setUpClass(*args, **kwargs)
  188. def test_re_save(self):
  189. for contrib in Contrib.objects.all():
  190. contrib.full_clean()
  191. contrib.save()
  192. class URLTokenManagerTests(TestCase):
  193. def test_sign_unsign_ok(self):
  194. input_data = {'foo': 12}
  195. at = URLTokenManager().sign(input_data)
  196. output_data = URLTokenManager().unsign(at)
  197. self.assertEqual(input_data, output_data)
  198. def test_sign_unsign_wrong_sig(self):
  199. with self.assertRaises(BadSignature):
  200. URLTokenManager().unsign(
  201. b'eyJmb28iOiAxfTpvUFZ1Q3FsSldtQ2htMXJBMmx5VFV0ZWxDLWM')
  202. class ContribTokenManagerTests(TestCase):
  203. def test_sign_unsign_ok(self):
  204. Contrib.objects.create(
  205. name='John2',
  206. phone='010101020101',
  207. contrib_type=Contrib.CONTRIB_CONNECT,
  208. privacy_coordinates=True,
  209. latitude=0.1,
  210. longitude=0.12,
  211. )
  212. contrib = Contrib.objects.all().first()
  213. manager = ContribTokenManager()
  214. token = manager.mk_token(contrib)
  215. self.assertEqual(
  216. manager.get_instance_if_allowed(token, contrib.pk),
  217. contrib)