Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tests.py 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. from decimal import Decimal
  2. import django
  3. from django import forms
  4. from django.core.serializers import deserialize, serialize
  5. from django.core.serializers.base import DeserializationError
  6. from django.contrib.contenttypes.fields import GenericForeignKey
  7. from django.contrib.contenttypes.models import ContentType
  8. from django.db import models
  9. from django.test import TestCase
  10. try:
  11. import json
  12. except ImportError:
  13. from django.utils import simplejson as json
  14. from .fields import JSONField, JSONCharField
  15. try:
  16. from django.forms.utils import ValidationError
  17. except ImportError:
  18. from django.forms.util import ValidationError
  19. from django.utils.six import string_types
  20. from collections import OrderedDict
  21. class JsonModel(models.Model):
  22. json = JSONField()
  23. default_json = JSONField(default={"check": 12})
  24. complex_default_json = JSONField(default=[{"checkcheck": 1212}])
  25. empty_default = JSONField(default={})
  26. class GenericForeignKeyObj(models.Model):
  27. name = models.CharField('Foreign Obj', max_length=255, null=True)
  28. class JSONModelWithForeignKey(models.Model):
  29. json = JSONField(null=True)
  30. foreign_obj = GenericForeignKey()
  31. object_id = models.PositiveIntegerField(blank=True, null=True, db_index=True)
  32. content_type = models.ForeignKey(ContentType, blank=True, null=True,
  33. on_delete=models.CASCADE)
  34. class JsonCharModel(models.Model):
  35. json = JSONCharField(max_length=100)
  36. default_json = JSONCharField(max_length=100, default={"check": 34})
  37. class ComplexEncoder(json.JSONEncoder):
  38. def default(self, obj):
  39. if isinstance(obj, complex):
  40. return {
  41. '__complex__': True,
  42. 'real': obj.real,
  43. 'imag': obj.imag,
  44. }
  45. return json.JSONEncoder.default(self, obj)
  46. def as_complex(dct):
  47. if '__complex__' in dct:
  48. return complex(dct['real'], dct['imag'])
  49. return dct
  50. class JSONModelCustomEncoders(models.Model):
  51. # A JSON field that can store complex numbers
  52. json = JSONField(
  53. dump_kwargs={'cls': ComplexEncoder, "indent": 4},
  54. load_kwargs={'object_hook': as_complex},
  55. )
  56. class JSONModelWithForeignKeyTestCase(TestCase):
  57. def test_object_create(self):
  58. foreign_obj = GenericForeignKeyObj.objects.create(name='Brain')
  59. JSONModelWithForeignKey.objects.create(foreign_obj=foreign_obj)
  60. class JSONFieldTest(TestCase):
  61. """JSONField Wrapper Tests"""
  62. json_model = JsonModel
  63. def test_json_field_create(self):
  64. """Test saving a JSON object in our JSONField"""
  65. json_obj = {
  66. "item_1": "this is a json blah",
  67. "blergh": "hey, hey, hey"}
  68. obj = self.json_model.objects.create(json=json_obj)
  69. new_obj = self.json_model.objects.get(id=obj.id)
  70. self.assertEqual(new_obj.json, json_obj)
  71. def test_string_in_json_field(self):
  72. """Test saving an ordinary Python string in our JSONField"""
  73. json_obj = 'blah blah'
  74. obj = self.json_model.objects.create(json=json_obj)
  75. new_obj = self.json_model.objects.get(id=obj.id)
  76. self.assertEqual(new_obj.json, json_obj)
  77. def test_float_in_json_field(self):
  78. """Test saving a Python float in our JSONField"""
  79. json_obj = 1.23
  80. obj = self.json_model.objects.create(json=json_obj)
  81. new_obj = self.json_model.objects.get(id=obj.id)
  82. self.assertEqual(new_obj.json, json_obj)
  83. def test_int_in_json_field(self):
  84. """Test saving a Python integer in our JSONField"""
  85. json_obj = 1234567
  86. obj = self.json_model.objects.create(json=json_obj)
  87. new_obj = self.json_model.objects.get(id=obj.id)
  88. self.assertEqual(new_obj.json, json_obj)
  89. def test_decimal_in_json_field(self):
  90. """Test saving a Python Decimal in our JSONField"""
  91. json_obj = Decimal(12.34)
  92. obj = self.json_model.objects.create(json=json_obj)
  93. new_obj = self.json_model.objects.get(id=obj.id)
  94. # here we must know to convert the returned string back to Decimal,
  95. # since json does not support that format
  96. self.assertEqual(Decimal(new_obj.json), json_obj)
  97. def test_json_field_modify(self):
  98. """Test modifying a JSON object in our JSONField"""
  99. json_obj_1 = {'a': 1, 'b': 2}
  100. json_obj_2 = {'a': 3, 'b': 4}
  101. obj = self.json_model.objects.create(json=json_obj_1)
  102. self.assertEqual(obj.json, json_obj_1)
  103. obj.json = json_obj_2
  104. self.assertEqual(obj.json, json_obj_2)
  105. obj.save()
  106. self.assertEqual(obj.json, json_obj_2)
  107. self.assertTrue(obj)
  108. def test_json_field_load(self):
  109. """Test loading a JSON object from the DB"""
  110. json_obj_1 = {'a': 1, 'b': 2}
  111. obj = self.json_model.objects.create(json=json_obj_1)
  112. new_obj = self.json_model.objects.get(id=obj.id)
  113. self.assertEqual(new_obj.json, json_obj_1)
  114. def test_json_list(self):
  115. """Test storing a JSON list"""
  116. json_obj = ["my", "list", "of", 1, "objs", {"hello": "there"}]
  117. obj = self.json_model.objects.create(json=json_obj)
  118. new_obj = self.json_model.objects.get(id=obj.id)
  119. self.assertEqual(new_obj.json, json_obj)
  120. def test_empty_objects(self):
  121. """Test storing empty objects"""
  122. for json_obj in [{}, [], 0, '', False]:
  123. obj = self.json_model.objects.create(json=json_obj)
  124. new_obj = self.json_model.objects.get(id=obj.id)
  125. self.assertEqual(json_obj, obj.json)
  126. self.assertEqual(json_obj, new_obj.json)
  127. def test_custom_encoder(self):
  128. """Test encoder_cls and object_hook"""
  129. value = 1 + 3j # A complex number
  130. obj = JSONModelCustomEncoders.objects.create(json=value)
  131. new_obj = JSONModelCustomEncoders.objects.get(pk=obj.pk)
  132. self.assertEqual(value, new_obj.json)
  133. def test_django_serializers(self):
  134. """Test serializing/deserializing jsonfield data"""
  135. for json_obj in [{}, [], 0, '', False, {'key': 'value', 'num': 42,
  136. 'ary': list(range(5)),
  137. 'dict': {'k': 'v'}}]:
  138. obj = self.json_model.objects.create(json=json_obj)
  139. new_obj = self.json_model.objects.get(id=obj.id)
  140. self.assert_(new_obj)
  141. queryset = self.json_model.objects.all()
  142. ser = serialize('json', queryset)
  143. for dobj in deserialize('json', ser):
  144. obj = dobj.object
  145. pulled = self.json_model.objects.get(id=obj.pk)
  146. self.assertEqual(obj.json, pulled.json)
  147. def test_default_parameters(self):
  148. """Test providing a default value to the model"""
  149. model = JsonModel()
  150. model.json = {"check": 12}
  151. self.assertEqual(model.json, {"check": 12})
  152. self.assertEqual(type(model.json), dict)
  153. self.assertEqual(model.default_json, {"check": 12})
  154. self.assertEqual(type(model.default_json), dict)
  155. def test_invalid_json(self):
  156. # invalid json data {] in the json and default_json fields
  157. ser = '[{"pk": 1, "model": "jsonfield.jsoncharmodel", ' \
  158. '"fields": {"json": "{]", "default_json": "{]"}}]'
  159. with self.assertRaises(DeserializationError) as cm:
  160. next(deserialize('json', ser))
  161. # Django 2.0+ uses PEP 3134 exception chaining
  162. if django.VERSION < (2, 0,):
  163. inner = cm.exception.args[0]
  164. else:
  165. inner = cm.exception.__context__
  166. self.assertTrue(isinstance(inner, ValidationError))
  167. self.assertEqual('Enter valid JSON', inner.messages[0])
  168. def test_integer_in_string_in_json_field(self):
  169. """Test saving the Python string '123' in our JSONField"""
  170. json_obj = '123'
  171. obj = self.json_model.objects.create(json=json_obj)
  172. new_obj = self.json_model.objects.get(id=obj.id)
  173. self.assertEqual(new_obj.json, json_obj)
  174. def test_boolean_in_string_in_json_field(self):
  175. """Test saving the Python string 'true' in our JSONField"""
  176. json_obj = 'true'
  177. obj = self.json_model.objects.create(json=json_obj)
  178. new_obj = self.json_model.objects.get(id=obj.id)
  179. self.assertEqual(new_obj.json, json_obj)
  180. def test_pass_by_reference_pollution(self):
  181. """Make sure the default parameter is copied rather than passed by reference"""
  182. model = JsonModel()
  183. model.default_json["check"] = 144
  184. model.complex_default_json[0]["checkcheck"] = 144
  185. self.assertEqual(model.default_json["check"], 144)
  186. self.assertEqual(model.complex_default_json[0]["checkcheck"], 144)
  187. # Make sure when we create a new model, it resets to the default value
  188. # and not to what we just set it to (it would be if it were passed by reference)
  189. model = JsonModel()
  190. self.assertEqual(model.default_json["check"], 12)
  191. self.assertEqual(model.complex_default_json[0]["checkcheck"], 1212)
  192. def test_normal_regex_filter(self):
  193. """Make sure JSON model can filter regex"""
  194. JsonModel.objects.create(json={"boom": "town"})
  195. JsonModel.objects.create(json={"move": "town"})
  196. JsonModel.objects.create(json={"save": "town"})
  197. self.assertEqual(JsonModel.objects.count(), 3)
  198. self.assertEqual(JsonModel.objects.filter(json__regex=r"boom").count(), 1)
  199. self.assertEqual(JsonModel.objects.filter(json__regex=r"town").count(), 3)
  200. def test_save_blank_object(self):
  201. """Test that JSON model can save a blank object as none"""
  202. model = JsonModel()
  203. self.assertEqual(model.empty_default, {})
  204. model.save()
  205. self.assertEqual(model.empty_default, {})
  206. model1 = JsonModel(empty_default={"hey": "now"})
  207. self.assertEqual(model1.empty_default, {"hey": "now"})
  208. model1.save()
  209. self.assertEqual(model1.empty_default, {"hey": "now"})
  210. class JSONCharFieldTest(JSONFieldTest):
  211. json_model = JsonCharModel
  212. class OrderedJsonModel(models.Model):
  213. json = JSONField(load_kwargs={'object_pairs_hook': OrderedDict})
  214. class OrderedDictSerializationTest(TestCase):
  215. def setUp(self):
  216. self.ordered_dict = OrderedDict([
  217. ('number', [1, 2, 3, 4]),
  218. ('notes', True),
  219. ('alpha', True),
  220. ('romeo', True),
  221. ('juliet', True),
  222. ('bravo', True),
  223. ])
  224. self.expected_key_order = ['number', 'notes', 'alpha', 'romeo', 'juliet', 'bravo']
  225. def test_ordered_dict_differs_from_normal_dict(self):
  226. self.assertEqual(list(self.ordered_dict.keys()), self.expected_key_order)
  227. self.assertNotEqual(dict(self.ordered_dict).keys(), self.expected_key_order)
  228. def test_default_behaviour_loses_sort_order(self):
  229. mod = JsonModel.objects.create(json=self.ordered_dict)
  230. self.assertEqual(list(mod.json.keys()), self.expected_key_order)
  231. mod_from_db = JsonModel.objects.get(id=mod.id)
  232. # mod_from_db lost ordering information during json.loads()
  233. self.assertNotEqual(mod_from_db.json.keys(), self.expected_key_order)
  234. def test_load_kwargs_hook_does_not_lose_sort_order(self):
  235. mod = OrderedJsonModel.objects.create(json=self.ordered_dict)
  236. self.assertEqual(list(mod.json.keys()), self.expected_key_order)
  237. mod_from_db = OrderedJsonModel.objects.get(id=mod.id)
  238. self.assertEqual(list(mod_from_db.json.keys()), self.expected_key_order)
  239. class JsonNotRequiredModel(models.Model):
  240. json = JSONField(blank=True, null=True)
  241. class JsonNotRequiredForm(forms.ModelForm):
  242. class Meta:
  243. model = JsonNotRequiredModel
  244. fields = '__all__'
  245. class JsonModelFormTest(TestCase):
  246. def test_blank_form(self):
  247. form = JsonNotRequiredForm(data={'json': ''})
  248. self.assertFalse(form.has_changed())
  249. def test_form_with_data(self):
  250. form = JsonNotRequiredForm(data={'json': '{}'})
  251. self.assertTrue(form.has_changed())
  252. class TestFieldAPIMethods(TestCase):
  253. def test_get_db_prep_value_method_with_null(self):
  254. json_field_instance = JSONField(null=True)
  255. value = {'a': 1}
  256. prepared_value = json_field_instance.get_db_prep_value(
  257. value, connection=None, prepared=False)
  258. self.assertIsInstance(prepared_value, string_types)
  259. self.assertDictEqual(value, json.loads(prepared_value))
  260. self.assertIs(json_field_instance.get_db_prep_value(
  261. None, connection=None, prepared=True), None)
  262. self.assertIs(json_field_instance.get_db_prep_value(
  263. None, connection=None, prepared=False), None)
  264. def test_get_db_prep_value_method_with_not_null(self):
  265. json_field_instance = JSONField(null=False)
  266. value = {'a': 1}
  267. prepared_value = json_field_instance.get_db_prep_value(
  268. value, connection=None, prepared=False)
  269. self.assertIsInstance(prepared_value, string_types)
  270. self.assertDictEqual(value, json.loads(prepared_value))
  271. self.assertIs(json_field_instance.get_db_prep_value(
  272. None, connection=None, prepared=True), None)
  273. self.assertEqual(json_field_instance.get_db_prep_value(
  274. None, connection=None, prepared=False), 'null')
  275. def test_get_db_prep_value_method_skips_prepared_values(self):
  276. json_field_instance = JSONField(null=False)
  277. value = {'a': 1}
  278. prepared_value = json_field_instance.get_db_prep_value(
  279. value, connection=None, prepared=True)
  280. self.assertIs(prepared_value, value)
  281. def test_get_prep_value_always_json_dumps_if_not_null(self):
  282. json_field_instance = JSONField(null=False)
  283. value = {'a': 1}
  284. prepared_value = json_field_instance.get_prep_value(value)
  285. self.assertIsInstance(prepared_value, string_types)
  286. self.assertDictEqual(value, json.loads(prepared_value))
  287. already_json = json.dumps(value)
  288. double_prepared_value = json_field_instance.get_prep_value(
  289. already_json)
  290. self.assertDictEqual(value,
  291. json.loads(json.loads(double_prepared_value)))
  292. self.assertEqual(json_field_instance.get_prep_value(None), 'null')
  293. def test_get_prep_value_can_return_none_if_null(self):
  294. json_field_instance = JSONField(null=True)
  295. value = {'a': 1}
  296. prepared_value = json_field_instance.get_prep_value(value)
  297. self.assertIsInstance(prepared_value, string_types)
  298. self.assertDictEqual(value, json.loads(prepared_value))
  299. already_json = json.dumps(value)
  300. double_prepared_value = json_field_instance.get_prep_value(
  301. already_json)
  302. self.assertDictEqual(value,
  303. json.loads(json.loads(double_prepared_value)))
  304. self.assertIs(json_field_instance.get_prep_value(None), None)