2018-10-19 16:32:48 +02:00
|
|
|
import asyncio
|
2018-07-09 23:22:26 +02:00
|
|
|
from importlib import import_module
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
from asgiref.sync import sync_to_async
|
|
|
|
from channels.testing import WebsocketCommunicator
|
|
|
|
from django.conf import settings
|
|
|
|
from django.contrib.auth import (
|
|
|
|
BACKEND_SESSION_KEY,
|
|
|
|
HASH_SESSION_KEY,
|
|
|
|
SESSION_KEY,
|
|
|
|
)
|
|
|
|
|
|
|
|
from openslides.asgi import application
|
|
|
|
from openslides.core.config import config
|
|
|
|
from openslides.utils.autoupdate import inform_deleted_data
|
|
|
|
from openslides.utils.cache import element_cache
|
|
|
|
|
|
|
|
from ...unit.utils.cache_provider import (
|
|
|
|
Collection1,
|
|
|
|
Collection2,
|
|
|
|
get_cachable_provider,
|
|
|
|
)
|
|
|
|
from ..helpers import TConfig, TUser, set_config
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
2018-09-01 08:00:00 +02:00
|
|
|
async def prepare_element_cache(settings):
|
2018-07-09 23:22:26 +02:00
|
|
|
"""
|
|
|
|
Resets the element cache.
|
|
|
|
|
|
|
|
Uses a cacheable_provider for tests with example data.
|
|
|
|
"""
|
2018-09-01 08:00:00 +02:00
|
|
|
await element_cache.cache_provider.clear_cache()
|
2018-07-09 23:22:26 +02:00
|
|
|
orig_cachable_provider = element_cache.cachable_provider
|
|
|
|
element_cache.cachable_provider = get_cachable_provider([Collection1(), Collection2(), TConfig(), TUser()])
|
|
|
|
element_cache._cachables = None
|
2018-09-01 08:00:00 +02:00
|
|
|
await sync_to_async(element_cache.ensure_cache)()
|
2018-07-09 23:22:26 +02:00
|
|
|
yield
|
|
|
|
# Reset the cachable_provider
|
|
|
|
element_cache.cachable_provider = orig_cachable_provider
|
|
|
|
element_cache._cachables = None
|
2018-09-01 08:00:00 +02:00
|
|
|
await element_cache.cache_provider.clear_cache()
|
2018-07-09 23:22:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2018-10-19 16:32:48 +02:00
|
|
|
async def get_communicator():
|
|
|
|
communicator: WebsocketCommunicator = None
|
|
|
|
|
|
|
|
def get_communicator(query_string=''):
|
|
|
|
nonlocal communicator # use the outer communicator variable
|
|
|
|
if query_string:
|
|
|
|
query_string = "?{}".format(query_string)
|
|
|
|
communicator = WebsocketCommunicator(application, "/ws/site/{}".format(query_string))
|
|
|
|
return communicator
|
|
|
|
|
|
|
|
yield get_communicator
|
|
|
|
if communicator:
|
|
|
|
await communicator.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
async def communicator(get_communicator):
|
|
|
|
yield get_communicator()
|
2018-07-09 23:22:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2018-10-19 16:32:48 +02:00
|
|
|
async def test_normal_connection(get_communicator):
|
2018-07-09 23:22:26 +02:00
|
|
|
await set_config('general_system_enable_anonymous', True)
|
2018-10-19 16:32:48 +02:00
|
|
|
connected, __ = await get_communicator().connect()
|
|
|
|
assert connected
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_connection_with_change_id(get_communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
communicator = get_communicator('change_id=0')
|
2018-07-09 23:22:26 +02:00
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
2018-08-22 16:50:23 +02:00
|
|
|
type = response.get('type')
|
|
|
|
content = response.get('content')
|
|
|
|
assert type == 'autoupdate'
|
2018-10-14 08:26:51 +02:00
|
|
|
assert 'changed' in content
|
|
|
|
assert 'deleted' in content
|
2018-10-19 16:32:48 +02:00
|
|
|
assert 'from_change_id' in content
|
|
|
|
assert 'to_change_id' in content
|
2018-10-14 08:26:51 +02:00
|
|
|
assert Collection1().get_collection_string() in content['changed']
|
|
|
|
assert Collection2().get_collection_string() in content['changed']
|
|
|
|
assert TConfig().get_collection_string() in content['changed']
|
|
|
|
assert TUser().get_collection_string() in content['changed']
|
2018-07-09 23:22:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2018-10-19 16:32:48 +02:00
|
|
|
async def test_connection_with_invalid_change_id(get_communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
communicator = get_communicator('change_id=invalid')
|
|
|
|
connected, __ = await communicator.connect()
|
|
|
|
|
|
|
|
assert connected is False
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_changed_data_autoupdate_off(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
# Change a config value
|
|
|
|
await set_config('general_event_name', 'Test Event')
|
|
|
|
assert await communicator.receive_nothing()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_changed_data_autoupdate_on(get_communicator):
|
2018-07-09 23:22:26 +02:00
|
|
|
await set_config('general_system_enable_anonymous', True)
|
2018-10-19 16:32:48 +02:00
|
|
|
communicator = get_communicator('autoupdate=on')
|
2018-07-09 23:22:26 +02:00
|
|
|
await communicator.connect()
|
|
|
|
|
2018-10-19 16:32:48 +02:00
|
|
|
# Change a config value
|
2018-07-09 23:22:26 +02:00
|
|
|
await set_config('general_event_name', 'Test Event')
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
id = config.get_key_to_id()['general_event_name']
|
2018-08-22 16:50:23 +02:00
|
|
|
type = response.get('type')
|
|
|
|
content = response.get('content')
|
|
|
|
assert type == 'autoupdate'
|
2018-10-14 08:26:51 +02:00
|
|
|
assert content['changed'] == {
|
|
|
|
'core/config': [{'id': id, 'key': 'general_event_name', 'value': 'Test Event'}]}
|
2018-07-09 23:22:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_anonymous_disabled(communicator):
|
|
|
|
connected, __ = await communicator.connect()
|
|
|
|
|
|
|
|
assert not connected
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_with_user():
|
|
|
|
# login user with id 1
|
|
|
|
engine = import_module(settings.SESSION_ENGINE)
|
|
|
|
session = engine.SessionStore() # type: ignore
|
|
|
|
session[SESSION_KEY] = '1'
|
|
|
|
session[HASH_SESSION_KEY] = '362d4f2de1463293cb3aaba7727c967c35de43ee' # see helpers.TUser
|
|
|
|
session[BACKEND_SESSION_KEY] = 'django.contrib.auth.backends.ModelBackend'
|
|
|
|
session.save()
|
|
|
|
scn = settings.SESSION_COOKIE_NAME
|
|
|
|
cookies = (b'cookie', '{}={}'.format(scn, session.session_key).encode())
|
|
|
|
communicator = WebsocketCommunicator(application, "/ws/site/", headers=[cookies])
|
|
|
|
|
|
|
|
connected, __ = await communicator.connect()
|
|
|
|
|
|
|
|
assert connected
|
|
|
|
|
|
|
|
await communicator.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2018-10-19 16:32:48 +02:00
|
|
|
async def test_receive_deleted_data(get_communicator):
|
2018-07-09 23:22:26 +02:00
|
|
|
await set_config('general_system_enable_anonymous', True)
|
2018-10-19 16:32:48 +02:00
|
|
|
communicator = get_communicator('autoupdate=on')
|
2018-07-09 23:22:26 +02:00
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
# Delete test element
|
|
|
|
await sync_to_async(inform_deleted_data)([(Collection1().get_collection_string(), 1)])
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
2018-08-22 16:50:23 +02:00
|
|
|
type = response.get('type')
|
|
|
|
content = response.get('content')
|
|
|
|
assert type == 'autoupdate'
|
2018-10-14 08:26:51 +02:00
|
|
|
assert content['deleted'] == {Collection1().get_collection_string(): [1]}
|
2018-07-09 23:22:26 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
2018-10-26 15:37:29 +02:00
|
|
|
async def test_send_notify(communicator):
|
2018-07-09 23:22:26 +02:00
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
2018-08-22 16:50:23 +02:00
|
|
|
await communicator.send_json_to({'type': 'notify', 'content': [{'testmessage': 'foobar, what else.'}], 'id': 'test'})
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
content = response['content']
|
|
|
|
assert isinstance(content, list)
|
|
|
|
assert len(content) == 1
|
|
|
|
assert content[0]['testmessage'] == 'foobar, what else.'
|
|
|
|
assert 'senderReplyChannelName' in content[0]
|
|
|
|
assert content[0]['senderUserId'] == 0
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_invalid_websocket_message_type(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to([])
|
|
|
|
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
assert response['type'] == 'error'
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_invalid_websocket_message_no_id(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'test', 'content': 'foobar'})
|
|
|
|
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
assert response['type'] == 'error'
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_unknown_type(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'if_you_add_this_type_to_openslides_I_will_be_sad', 'content': True, 'id': 'test_id'})
|
|
|
|
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
assert response['type'] == 'error'
|
|
|
|
assert response['in_response'] == 'test_id'
|
2018-08-30 09:07:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_request_constants(communicator, settings):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'constants', 'content': '', 'id': 'test_id'})
|
|
|
|
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
assert response['type'] == 'constants'
|
|
|
|
# See conftest.py for the content of 'content'
|
|
|
|
assert response['content'] == {'constant1': 'value1', 'constant2': 'value2'}
|
2018-10-19 16:32:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_get_elements(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {}, 'id': 'test_id'})
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
type = response.get('type')
|
|
|
|
content = response.get('content')
|
|
|
|
assert type == 'autoupdate'
|
|
|
|
assert 'changed' in content
|
|
|
|
assert 'deleted' in content
|
|
|
|
assert 'from_change_id' in content
|
|
|
|
assert 'to_change_id' in content
|
|
|
|
assert Collection1().get_collection_string() in content['changed']
|
|
|
|
assert Collection2().get_collection_string() in content['changed']
|
|
|
|
assert TConfig().get_collection_string() in content['changed']
|
|
|
|
assert TUser().get_collection_string() in content['changed']
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_get_elements_to_big_change_id(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': 1_000_000_000_000}, 'id': 'test_id'})
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
type = response.get('type')
|
|
|
|
assert type == 'error'
|
|
|
|
assert response.get('in_response') == 'test_id'
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_get_elements_to_small_change_id(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': 1}, 'id': 'test_id'})
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
type = response.get('type')
|
|
|
|
assert type == 'autoupdate'
|
|
|
|
assert response.get('in_response') == 'test_id'
|
|
|
|
assert response.get('content')['all_data']
|
|
|
|
|
|
|
|
|
2018-10-27 14:41:27 +02:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_connect_twice_with_clear_change_id_cache(communicator):
|
|
|
|
"""
|
|
|
|
Test, that a second request with change_id+1 from the first request, returns
|
|
|
|
an error.
|
|
|
|
"""
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
element_cache.cache_provider.change_id_data = {} # type: ignore
|
|
|
|
await communicator.connect()
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': 0}, 'id': 'test_id'})
|
|
|
|
response1 = await communicator.receive_json_from()
|
|
|
|
first_change_id = response1.get('content')['to_change_id']
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': first_change_id+1}, 'id': 'test_id'})
|
|
|
|
response2 = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
assert response2['type'] == 'error'
|
|
|
|
assert response2.get('content') == 'Requested change_id is higher this highest change_id.'
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_connect_twice_with_clear_change_id_cache_same_change_id_then_first_request(communicator):
|
|
|
|
"""
|
|
|
|
Test, that a second request with the change_id from the first request, returns
|
|
|
|
all data.
|
|
|
|
|
|
|
|
A client should not do this but request for change_id+1
|
|
|
|
"""
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
element_cache.cache_provider.change_id_data = {} # type: ignore
|
|
|
|
await communicator.connect()
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': 0}, 'id': 'test_id'})
|
|
|
|
response1 = await communicator.receive_json_from()
|
|
|
|
first_change_id = response1.get('content')['to_change_id']
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': first_change_id}, 'id': 'test_id'})
|
|
|
|
response2 = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
assert response2['type'] == 'autoupdate'
|
|
|
|
assert response2.get('content')['all_data']
|
|
|
|
|
|
|
|
|
2018-10-19 16:32:48 +02:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_send_invalid_get_elements(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'getElements', 'content': {'change_id': 'some value'}, 'id': 'test_id'})
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
type = response.get('type')
|
|
|
|
assert type == 'error'
|
|
|
|
assert response.get('in_response') == 'test_id'
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_turn_on_autoupdate(communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'autoupdate', 'content': 'on', 'id': 'test_id'})
|
|
|
|
await asyncio.sleep(0.01)
|
|
|
|
# Change a config value
|
|
|
|
await set_config('general_event_name', 'Test Event')
|
|
|
|
response = await communicator.receive_json_from()
|
|
|
|
|
|
|
|
id = config.get_key_to_id()['general_event_name']
|
|
|
|
type = response.get('type')
|
|
|
|
content = response.get('content')
|
|
|
|
assert type == 'autoupdate'
|
|
|
|
assert content['changed'] == {
|
|
|
|
'core/config': [{'id': id, 'key': 'general_event_name', 'value': 'Test Event'}]}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
async def test_turn_off_autoupdate(get_communicator):
|
|
|
|
await set_config('general_system_enable_anonymous', True)
|
|
|
|
communicator = get_communicator('autoupdate=on')
|
|
|
|
await communicator.connect()
|
|
|
|
|
|
|
|
await communicator.send_json_to({'type': 'autoupdate', 'content': False, 'id': 'test_id'})
|
|
|
|
await asyncio.sleep(0.01)
|
|
|
|
# Change a config value
|
|
|
|
await set_config('general_event_name', 'Test Event')
|
|
|
|
assert await communicator.receive_nothing()
|