from django.test import TestCase, override_settings from test_app.models import JSONModel, NullableFieldModel, RelatedModel, SimpleModel from auditlog import get_logentry_model from auditlog.registry import AuditlogModelRegistry LogEntry = get_logentry_model() class JSONForChangesTest(TestCase): def setUp(self): self.test_auditlog = AuditlogModelRegistry() @override_settings(AUDITLOG_STORE_JSON_CHANGES="str") def test_wrong_setting_type(self): with self.assertRaisesMessage( TypeError, "Setting 'AUDITLOG_STORE_JSON_CHANGES' must be a boolean" ): self.test_auditlog.register_from_settings() @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_with_simplemodel(self): self.test_auditlog.register_from_settings() smm = SimpleModel() smm.save() changes_dict = smm.history.latest().changes_dict # compare the id, text, boolean and datetime fields id_field_changes = changes_dict["id"] self.assertIsNone(id_field_changes[0]) self.assertIsInstance( id_field_changes[1], int ) # the id depends on state of the database text_field_changes = changes_dict["text"] self.assertEqual(text_field_changes, [None, ""]) boolean_field_changes = changes_dict["boolean"] self.assertEqual(boolean_field_changes, [None, False]) # datetime should be serialized to string datetime_field_changes = changes_dict["datetime"] self.assertIsNone(datetime_field_changes[0]) self.assertIsInstance(datetime_field_changes[1], str) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_with_jsonmodel(self): self.test_auditlog.register_from_settings() json_model = JSONModel() json_model.json = {"test_key": "test_value"} json_model.save() changes_dict = json_model.history.latest().changes_dict id_field_changes = changes_dict["json"] self.assertEqual(id_field_changes, [None, {"test_key": "test_value"}]) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_with_jsonmodel_with_empty_list(self): self.test_auditlog.register_from_settings() json_model = JSONModel() json_model.json = [] json_model.save() changes_dict = json_model.history.latest().changes_dict id_field_changes = changes_dict["json"] self.assertEqual(id_field_changes, [None, []]) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_with_jsonmodel_with_complex_data(self): self.test_auditlog.register_from_settings() json_model = JSONModel() json_model.json = { "key": "test_value", "key_dict": {"inner_key": "inner_value"}, "key_tuple": ("item1", "item2", "item3"), } json_model.save() changes_dict = json_model.history.latest().changes_dict id_field_changes = changes_dict["json"] self.assertEqual( id_field_changes, [ None, { "key": "test_value", "key_dict": {"inner_key": "inner_value"}, "key_tuple": [ "item1", "item2", "item3", ], # tuple is converted to list, that's ok }, ], ) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_with_jsonmodel_with_related_model(self): self.test_auditlog.register_from_settings() simple = SimpleModel.objects.create() one_simple = SimpleModel.objects.create() related_model = RelatedModel.objects.create( one_to_one=simple, related=one_simple ) related_model.save() changes_dict = related_model.history.latest().changes_dict field_related_changes = changes_dict["related"] self.assertEqual(field_related_changes, [None, one_simple.id]) field_one_to_one_changes = changes_dict["one_to_one"] self.assertEqual(field_one_to_one_changes, [None, simple.id]) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_update(self): self.test_auditlog.register_from_settings() simple = SimpleModel(text="original") simple.save() simple.text = "new" simple.save() changes_dict = simple.history.latest().changes_dict text_changes = changes_dict["text"] self.assertEqual(text_changes, ["original", "new"]) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_use_json_for_changes_delete(self): self.test_auditlog.register_from_settings() simple = SimpleModel() simple.save() simple.delete() history = LogEntry.objects.all() self.assertEqual(history.count(), 1, '"DELETE" record is always retained') changes_dict = history.first().changes_dict self.assertTrue( all(v[1] is None for k, v in changes_dict.items()), 'all values in the changes dict should None, not "None"', ) @override_settings(AUDITLOG_STORE_JSON_CHANGES=False) def test_nullable_field_with_none_not_logged(self): self.test_auditlog.register_from_settings() obj = NullableFieldModel.objects.create(time=None, optional_text=None) changes_dict = obj.history.latest().changes_dict # None → None should NOT be logged as a change self.assertNotIn("time", changes_dict) self.assertNotIn("optional_text", changes_dict) @override_settings(AUDITLOG_STORE_JSON_CHANGES=False) def test_nullable_field_with_value_logged(self): self.test_auditlog.register_from_settings() obj = NullableFieldModel.objects.create(optional_text="something") changes_dict = obj.history.latest().changes_dict # None → "something" should be logged self.assertIn("optional_text", changes_dict) self.assertEqual(changes_dict["optional_text"], ["None", "something"]) @override_settings(AUDITLOG_STORE_JSON_CHANGES=True) def test_nullable_field_with_none_not_logged_json_mode(self): self.test_auditlog.register_from_settings() obj = NullableFieldModel.objects.create(time=None, optional_text=None) changes_dict = obj.history.latest().changes_dict # None → None should NOT be logged self.assertNotIn("time", changes_dict) self.assertNotIn("optional_text", changes_dict) @override_settings(AUDITLOG_STORE_JSON_CHANGES=False) def test_nullable_field_update_none_to_value(self): self.test_auditlog.register_from_settings() obj = NullableFieldModel.objects.create(optional_text=None) obj.optional_text = "updated" obj.save() changes_dict = obj.history.latest().changes_dict # None → "updated" should be logged self.assertIn("optional_text", changes_dict) self.assertEqual(changes_dict["optional_text"], ["None", "updated"])