from unittest.mock import patch from channels import DEFAULT_CHANNEL_LAYER from channels.asgi import channel_layers from channels.tests import ChannelTestCase from django.contrib.auth.models import Group from openslides.assignments.models import Assignment from openslides.topics.models import Topic from openslides.utils.autoupdate import ( inform_changed_data, inform_deleted_data, ) @patch('openslides.utils.autoupdate.transaction.on_commit', lambda func: func()) class TestsInformChangedData(ChannelTestCase): # on_commit does not work with Djangos TestCase, see: # https://docs.djangoproject.com/en/1.10/topics/db/transactions/#use-in-tests # In this case it is also not possible to use a TransactionTestCase because # we have to use the ChannelTestCase. # The patch in the class decorator changes on_commit, so the given callable # is called immediately. This is the same behavior as if there would be # no transaction at all. def test_change_one_element(self): topic = Topic.objects.create(title='test_topic') channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_changed_data(topic) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 1) self.assertEqual( channel_message['elements'][0]['collection_string'], 'topics/topic') def test_change_many_elements(self): topics = ( Topic.objects.create(title='test_topic1'), Topic.objects.create(title='test_topic2'), Topic.objects.create(title='test_topic3')) channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_changed_data(topics) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 3) def test_change_with_non_root_rest_elements(self): """ Tests that if an root_rest_element is called together with one of its child elements, then there is only the root_rest_element in the channel message. """ assignment = Assignment.objects.create(title='test_assignment', open_posts=1) poll = assignment.create_poll() channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_changed_data((assignment, poll)) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 1) def test_change_only_non_root_rest_element(self): """ Tests that if only a non root_rest_element is called, then only the root_rest_element is in the channel. """ assignment = Assignment.objects.create(title='test_assignment', open_posts=1) poll = assignment.create_poll() channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_changed_data(poll) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 1) def test_change_no_autoupdate_model(self): """ Tests that if inform_changed_data() is called with a model that does not support autoupdate, nothing happens. We use the django Group for this (not the OpenSlides Group) """ group = Group.objects.create(name='test_group') channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_changed_data(group) with self.assertRaises(AssertionError): # self.get_next_message() with require=True raises a AssertionError # if there is no message in the channel self.get_next_message('autoupdate.send_data', require=True) def test_delete_one_element(self): channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_deleted_data('topics/topic', 1) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 1) self.assertTrue(channel_message['elements'][0]['deleted']) def test_delete_many_elements(self): channel_layers[DEFAULT_CHANNEL_LAYER].flush() inform_deleted_data('topics/topic', 1, 'topics/topic', 2, 'testmodule/model', 1) channel_message = self.get_next_message('autoupdate.send_data', require=True) self.assertEqual(len(channel_message['elements']), 3) def test_delete_no_element(self): with self.assertRaises(ValueError): inform_deleted_data() def test_delete_wrong_arguments(self): with self.assertRaises(ValueError): inform_deleted_data('testmodule/model') with self.assertRaises(ValueError): inform_deleted_data('testmodule/model', 5, 'testmodule/model')