def test_powerset(iterable, map_func, expected_powerset): assert powerset(iterable, map_func) == expected_powerset
class TestModel: @pytest.fixture(autouse=True) def mock_external_classes(self, mocker: Any) -> None: self.urlparse = mocker.patch('urllib.parse.urlparse') self.controller = mocker.patch('zulipterminal.core.' 'Controller', return_value=None) self.client = mocker.patch('zulipterminal.core.' 'Controller.client') self.client.base_url = 'chat.zulip.zulip' mocker.patch('zulipterminal.model.Model._start_presence_updates') @pytest.fixture def model(self, mocker, initial_data, user_profile): mocker.patch('zulipterminal.model.Model.get_messages') self.client.register.return_value = initial_data mocker.patch('zulipterminal.model.Model.get_all_users', return_value=[]) mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) # NOTE: PATCH WHERE USED NOT WHERE DEFINED self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) self.client.get_profile.return_value = user_profile model = Model(self.controller) return model def test_init(self, model, initial_data, user_profile): assert hasattr(model, 'controller') assert hasattr(model, 'client') assert model.msg_view is None assert model.msg_list is None assert model.narrow == [] assert model.found_newest is False assert model.stream_id == -1 assert model.stream_dict == {} assert model.recipients == frozenset() assert model.index == initial_index model.get_messages.assert_called_once_with(num_before=30, num_after=10, anchor=None) assert model.initial_data == initial_data assert model.user_id == user_profile['user_id'] assert model.user_full_name == user_profile['full_name'] assert model.user_email == user_profile['email'] # FIXME Add test here for model.server_url model.get_all_users.assert_called_once_with() assert model.users == [] (model._stream_info_from_subscriptions.assert_called_once_with( initial_data['subscriptions'])) assert model.pinned_streams == [] assert model.unpinned_streams == [] self.classify_unread_counts.assert_called_once_with(model) assert model.unread_counts == [] def test_register_initial_desired_events(self, mocker, initial_data): mocker.patch('zulipterminal.model.Model.get_messages') mocker.patch('zulipterminal.model.Model.get_all_users') mocker.patch('zulipterminal.model.Model.fetch_all_topics') self.client.register.return_value = initial_data model = Model(self.controller) event_types = [ 'message', 'update_message', 'reaction', 'subscription', 'typing', 'update_message_flags', ] fetch_event_types = [ 'realm', 'presence', 'subscription', 'message', 'update_message_flags', 'muted_topics', 'realm_user', ] model.client.register.assert_called_once_with( event_types=event_types, fetch_event_types=fetch_event_types, apply_markdown=True, client_gravatar=True) @pytest.mark.parametrize('msg_id', [1, 5, set()]) @pytest.mark.parametrize('narrow', [ [], [['stream', 'hello world']], [['stream', 'hello world'], ['topic', "what's it all about?"]], [['pm_with', '*****@*****.**']], [['pm_with', '[email protected], [email protected]']], [['is', 'private']], [['is', 'starred']], ]) def test_get_focus_in_current_narrow_individually(self, model, msg_id, narrow): model.index = {'pointer': {str(narrow): msg_id}} model.narrow = narrow assert model.get_focus_in_current_narrow() == msg_id @pytest.mark.parametrize('msg_id', [1, 5]) @pytest.mark.parametrize('narrow', [ [], [['stream', 'hello world']], [['stream', 'hello world'], ['topic', "what's it all about?"]], [['pm_with', '*****@*****.**']], [['pm_with', '[email protected], [email protected]']], [['is', 'private']], [['is', 'starred']], ]) def test_set_focus_in_current_narrow(self, mocker, model, narrow, msg_id): from collections import defaultdict model.index = dict(pointer=defaultdict(set)) model.narrow = narrow model.set_focus_in_current_narrow(msg_id) assert model.index['pointer'][str(narrow)] == msg_id @pytest.mark.parametrize('bad_args', [ dict(topic='some topic'), dict(stream='foo', search='text'), dict(topic='blah', search='text'), dict(pm_with='someone', topic='foo') ]) def test_set_narrow_bad_input(self, model, bad_args): with pytest.raises(RuntimeError): model.set_narrow(**bad_args) @pytest.mark.parametrize('narrow, good_args', [ ([], dict()), ([['stream', 'some stream']], dict(stream='some stream')), ([['stream', 'some stream'], ['topic', 'some topic'] ], dict(stream='some stream', topic='some topic')), ([['search', 'something interesting'] ], dict(search='something interesting')), ([['is', 'starred']], dict(starred=True)), ([['is', 'private']], dict(pms=True)), ([['pm_with', '*****@*****.**']], dict(pm_with='*****@*****.**')), ]) def test_set_narrow_already_set(self, model, narrow, good_args): model.narrow = narrow assert model.set_narrow(**good_args) assert model.narrow == narrow @pytest.mark.parametrize('initial_narrow, narrow, good_args', [ ([['stream', 'foo']], [], dict()), ([], [['stream', 'some stream']], dict(stream='some stream')), ([], [['stream', 'some stream'], ['topic', 'some topic'] ], dict(stream='some stream', topic='some topic')), ([], [['is', 'starred']], dict(starred=True)), ([], [['is', 'private']], dict(pms=True)), ([], [['pm_with', '*****@*****.**'] ], dict(pm_with='*****@*****.**')), ]) def test_set_narrow_not_already_set(self, model, initial_narrow, narrow, good_args, user_dict): model.narrow = initial_narrow model.user_dict = user_dict assert not model.set_narrow(**good_args) assert model.narrow != initial_narrow assert model.narrow == narrow # FIXME: Add assert for recipients being updated (other tests too?) @pytest.mark.parametrize( "narrow, index, current_ids", [ ([], { "all_msg_ids": {0, 1} }, {0, 1}), ([['stream', 'FOO']], { "stream_msg_ids_by_stream_id": { 1: {0, 1} } }, {0, 1}), ([['stream', 'FOO'], ['topic', 'BOO']], { 'topic_msg_ids': { 1: { 'BOO': {0, 1} } } }, {0, 1}), ( [ ['stream', 'FOO'], # Covers one empty-set case ['topic', 'BOOBOO'] ], { 'topic_msg_ids': { 1: { 'BOO': {0, 1} } } }, set()), ([['is', 'private']], { 'private_msg_ids': {0, 1} }, {0, 1}), ([['pm_with', '*****@*****.**']], { 'private_msg_ids_by_user_ids': { frozenset({1, 2}): {0, 1} } }, {0, 1}), ( [['pm_with', '*****@*****.**']], { # Covers recipient empty-set case 'private_msg_ids_by_user_ids': { frozenset({1, 3}): {0, 1} # NOTE {1,3} not {1,2} } }, set()), ([['search', 'FOO']], { 'search': {0, 1} }, {0, 1}), ([['is', 'starred']], { 'starred_msg_ids': {0, 1} }, {0, 1}) ]) def test_get_message_ids_in_current_narrow(self, mocker, model, narrow, index, current_ids): model.recipients = frozenset({1, 2}) model.stream_id = 1 model.narrow = narrow model.index = index assert current_ids == model.get_message_ids_in_current_narrow() @pytest.mark.parametrize("response, expected_index, return_value", [({ 'result': 'success', 'topics': [{ 'name': 'Foo' }, { 'name': 'Boo' }] }, { 23: ['Foo', 'Boo'] }, True), ({ 'result': 'success', 'topics': [] }, { 23: [] }, True), ({ 'result': 'failure', 'topics': [] }, { 23: [] }, False)]) def test_get_topics_in_streams(self, mocker, response, model, return_value, expected_index) -> None: self.client.get_stream_topics = mocker.Mock(return_value=response) result = model.get_topics_in_stream([23]) self.client.get_stream_topics.assert_called_once_with(23) assert model.index['topics'] == expected_index assert result == return_value @pytest.mark.parametrize("user_key", ['user_id', 'id']) @pytest.mark.parametrize("msg_id, existing_reactions, expected_method", [ (5, [], 'POST'), (5, [dict(user='******', emoji_code='1f44d')], 'DELETE'), (5, [dict(user='******', emoji_code='1f44d')], 'POST'), (5, [dict(user='******', emoji_code='1f614')], 'POST'), (5, [dict(user='******', emoji_code='1f614')], 'POST'), ]) def test_react_to_message_with_thumbs_up(self, model, user_key, msg_id, existing_reactions, expected_method): full_existing_reactions = [ dict(er, user={ user_key: (model.user_id if er['user'] == 'me' else model.user_id + 1) }) # non-match for er in existing_reactions ] message = dict(id=msg_id, reactions=full_existing_reactions) reaction_spec = dict(emoji_name='thumbs_up', reaction_type='unicode_emoji', emoji_code='1f44d', message_id=str(msg_id)) model.react_to_message(message, 'thumbs_up') if expected_method == 'POST': model.client.add_reaction.assert_called_once_with(reaction_spec) model.client.delete_reaction.assert_not_called() elif expected_method == 'DELETE': model.client.remove_reaction.assert_called_once_with(reaction_spec) model.client.add_reaction.assert_not_called() def test_react_to_message_for_not_thumbs_up(self, model): with pytest.raises(AssertionError): model.react_to_message(dict(), 'x') @pytest.mark.parametrize('response, return_value', [ ({ 'result': 'success' }, True), ({ 'result': 'some_failure' }, False), ]) def test_send_private_message(self, mocker, model, response, return_value, content="hi!", recipients="*****@*****.**"): self.client.send_message = mocker.Mock(return_value=response) result = model.send_private_message(recipients, content) req = dict(type='private', to=recipients, content=content) self.client.send_message.assert_called_once_with(req) assert result == return_value @pytest.mark.parametrize('response, return_value', [ ({ 'result': 'success' }, True), ({ 'result': 'some_failure' }, False), ]) def test_send_stream_message(self, mocker, model, response, return_value, content="hi!", stream="foo", topic="bar"): self.client.send_message = mocker.Mock(return_value=response) result = model.send_stream_message(stream, topic, content) req = dict(type='stream', to=stream, subject=topic, content=content) self.client.send_message.assert_called_once_with(req) assert result == return_value @pytest.mark.parametrize('response, return_value', [ ({ 'result': 'success' }, True), ({ 'result': 'some_failure' }, False), ]) def test_update_private_message(self, mocker, model, response, return_value, content="hi!", msg_id=1): self.client.update_message = mocker.Mock(return_value=response) result = model.update_private_message(msg_id, content) req = dict(message_id=msg_id, content=content) self.client.update_message.assert_called_once_with(req) assert result == return_value @pytest.mark.parametrize('response, return_value', [ ({ 'result': 'success' }, True), ({ 'result': 'some_failure' }, False), ]) def test_update_stream_message(self, mocker, model, response, return_value, content="hi!", subject='Hello', msg_id=1): self.client.update_message = mocker.Mock(return_value=response) result = model.update_stream_message(subject, msg_id, content) req = dict(subject=subject, propagate_mode="change_one", message_id=msg_id, content=content) self.client.update_message.assert_called_once_with(req) assert result == return_value # NOTE: This tests only getting next-unread, not a fixed anchor def test_success_get_messages(self, mocker, messages_successful_response, index_all_messages, initial_data, num_before=30, num_after=10): self.client.register.return_value = initial_data mocker.patch('zulipterminal.model.Model.get_all_users', return_value=[]) mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) # Setup mocks before calling get_messages self.client.get_messages.return_value = messages_successful_response mocker.patch('zulipterminal.model.index_messages', return_value=index_all_messages) model = Model(self.controller) request = { 'anchor': 0, 'num_before': num_before, 'num_after': num_after, 'apply_markdown': True, 'use_first_unread_anchor': True, 'client_gravatar': True, 'narrow': json.dumps(model.narrow), } (model.client.get_messages.assert_called_once_with( message_filters=request)) assert model.index == index_all_messages anchor = messages_successful_response['anchor'] if anchor < 10000000000000000: assert model.index['pointer'][str(model.narrow)] == anchor assert model.found_newest is True def test_get_message_false_first_anchor(self, mocker, messages_successful_response, index_all_messages, initial_data, num_before=30, num_after=10): # TEST FOR get_messages() with first_anchor=False # and anchor=0 # Initialize Model self.client.register.return_value = initial_data mocker.patch('zulipterminal.model.Model.get_all_users', return_value=[]) mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) # Setup mocks before calling get_messages messages_successful_response['anchor'] = 0 self.client.get_messages.return_value = messages_successful_response mocker.patch('zulipterminal.model.index_messages', return_value=index_all_messages) model = Model(self.controller) model.get_messages(num_before=num_before, num_after=num_after, anchor=0) self.client.get_messages.return_value = messages_successful_response # anchor should have remained the same anchor = messages_successful_response['anchor'] assert model.index['pointer'][str(model.narrow)] == 0 # TEST `query_range` < no of messages received model.found_newest = False # RESET model.found_newest value model.get_messages(num_after=0, num_before=0, anchor=0) assert model.found_newest is False # FIXME This only tests the case where the get_messages is in __init__ def test_fail_get_messages(self, mocker, error_response, initial_data, num_before=30, num_after=10): # Initialize Model self.client.register.return_value = initial_data mocker.patch('zulipterminal.model.Model.get_all_users', return_value=[]) mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) # Setup mock before calling get_messages # FIXME This has no influence on the result # self.client.do_api_query.return_value = error_response with pytest.raises(ServerConnectionFailure): model = Model(self.controller) @pytest.mark.parametrize('initial_muted_streams, value', [ ({315}, True), ({205, 315}, False), (set(), True), ({205}, False), ], ids=[ 'muting_205', 'unmuting_205', 'first_muted_205', 'last_unmuted_205' ]) def test_toggle_stream_muted_status(self, mocker, model, initial_muted_streams, value): model.muted_streams = initial_muted_streams model.client.update_subscription_settings.return_value = \ {'result': "success"} model.toggle_stream_muted_status(205) request = [{'stream_id': 205, 'property': 'is_muted', 'value': value}] model.client.update_subscription_settings.\ assert_called_once_with(request) @pytest.mark.parametrize('flags_before, expected_operator', [ ([], 'add'), (['starred'], 'remove'), (['read'], 'add'), (['read', 'starred'], 'remove'), (['starred', 'read'], 'remove'), ]) def test_toggle_message_star_status(self, mocker, model, flags_before, expected_operator): mocker.patch('zulip.Client') message = { 'id': 99, 'flags': flags_before, } model.toggle_message_star_status(message) request = { 'flag': 'starred', 'messages': [99], 'op': expected_operator } model.client.update_message_flags.assert_called_once_with(request) def test_mark_message_ids_as_read(self, model, mocker: Any) -> None: mock_api_query = mocker.patch('zulipterminal.core.Controller' '.client.update_message_flags') model.mark_message_ids_as_read([1, 2]) mock_api_query.assert_called_once_with( { 'flag': 'read', 'messages': [1, 2], 'op': 'add' }, ) def test_mark_message_ids_as_read_empty_msg_list(self, model) -> None: assert model.mark_message_ids_as_read([]) is None def test__update_initial_data(self, model, initial_data): assert model.initial_data == initial_data def test__update_initial_data_raises_exception(self, mocker, initial_data): # Initialize Model mocker.patch('zulipterminal.model.Model.get_messages') mocker.patch('zulipterminal.model.Model.get_all_users', return_value=[]) mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) # Setup mocks before calling get_messages self.client.register.return_value = initial_data self.client.get_members.return_value = { 'members': initial_data['realm_users'] } model = Model(self.controller) # Test if raises Exception self.client.register.side_effect = Exception() with pytest.raises(Exception): model._update_initial_data() def test_get_all_users(self, mocker, initial_data, user_list, user_dict, user_id): mocker.patch('zulipterminal.model.Model.get_messages') self.client.register.return_value = initial_data mocker.patch( 'zulipterminal.model.Model.' '_stream_info_from_subscriptions', return_value=({}, set(), [], [])) self.classify_unread_counts = mocker.patch( 'zulipterminal.model.classify_unread_counts', return_value=[]) model = Model(self.controller) assert model.user_dict == user_dict assert model.users == user_list @pytest.mark.parametrize('muted', powerset([1, 2, 99, 1000])) def test__stream_info_from_subscriptions(self, initial_data, streams, muted): subs = [ dict(entry, in_home_view=entry['stream_id'] not in muted) for entry in initial_data['subscriptions'] ] by_id, muted_streams, pinned, unpinned = ( Model._stream_info_from_subscriptions(subs)) assert len(by_id) assert all(msg_id == msg['stream_id'] for msg_id, msg in by_id.items()) assert muted_streams == muted assert pinned == [] # FIXME generalize/parametrize assert unpinned == streams # FIXME generalize/parametrize def test_append_message_with_Falsey_log(self, mocker, model): model.found_newest = True index_msg = mocker.patch('zulipterminal.model.index_messages', return_value={}) model.msg_list = mocker.Mock() create_msg_box_list = mocker.patch( 'zulipterminal.model.' 'create_msg_box_list', return_value=["msg_w"]) model.msg_list.log = [] event = {'message': {'id': 0}} model.append_message(event) assert len(model.msg_list.log) == 1 # Added "msg_w" element (create_msg_box_list.assert_called_once_with(model, [0], last_message=None)) def test_append_message_with_valid_log(self, mocker, model): model.found_newest = True index_msg = mocker.patch('zulipterminal.model.index_messages', return_value={}) model.msg_list = mocker.Mock() create_msg_box_list = mocker.patch( 'zulipterminal.model.' 'create_msg_box_list', return_value=["msg_w"]) model.msg_list.log = [mocker.Mock()] event = {'message': {'id': 0}} model.append_message(event) assert len(model.msg_list.log) == 2 # Added "msg_w" element # NOTE: So we expect the first element *was* the last_message parameter expected_last_msg = model.msg_list.log[0].original_widget.message (create_msg_box_list.assert_called_once_with( model, [0], last_message=expected_last_msg)) def test_append_message_event_flags(self, mocker, model): model.found_newest = True index_msg = mocker.patch('zulipterminal.model.index_messages', return_value={}) model.msg_list = mocker.Mock() create_msg_box_list = mocker.patch( 'zulipterminal.model.' 'create_msg_box_list', return_value=["msg_w"]) model.msg_list.log = [mocker.Mock()] set_count = mocker.patch('zulipterminal.model.set_count') # Test event with flags event = {'message': {'id': 0}, 'flags': ['read', 'mentioned']} model.append_message(event) # set count not called since 'read' flag present. set_count.assert_not_called() # Test event without flags model.msg_list.log = [mocker.Mock()] event = {'message': {'id': 0}} model.append_message(event) # set count called since the message is unread. set_count.assert_called_once_with([event['message']['id']], self.controller, 1) @pytest.mark.parametrize( 'response, narrow, recipients, log', [ ({ 'type': 'stream', 'id': 1 }, [], frozenset(), ['msg_w']), ({ 'type': 'private', 'id': 1 }, [['is', 'private']], frozenset(), ['msg_w']), ({ 'type': 'stream', 'id': 1, 'display_recipient': 'a' }, [['stream', 'a']], frozenset(), ['msg_w']), ({ 'type': 'stream', 'id': 1, 'subject': 'b', 'display_recipient': 'a' }, [['stream', 'a'], ['topic', 'b']], frozenset(), ['msg_w']), ({ 'type': 'stream', 'id': 1, 'subject': 'b', 'display_recipient': 'a' }, [['stream', 'c'], ['topic', 'b']], frozenset(), []), ({ 'type': 'private', 'id': 1, 'display_recipient': [{ 'id': 5827 }, { 'id': 5 }] }, [['pm_with', '*****@*****.**']], frozenset( {5827, 5}), ['msg_w']), ({ 'type': 'private', 'id': 1 }, [['is', 'search']], frozenset(), []), ({ 'type': 'private', 'id': 1, 'display_recipient': [{ 'id': 5827 }, { 'id': 3212 }] }, [['pm_with', '*****@*****.**']], frozenset( {5827, 5}), []), ], ids=[ 'stream_to_all_messages', 'private_to_all_private', 'stream_to_stream', 'stream_to_topic', 'stream_to_different_stream_same_topic', 'user_pm_x_appears_in_narrow_with_x', 'search', 'user_pm_x_does_not_appear_in_narrow_without_x' ]) def test_append_message(self, mocker, user_profile, response, narrow, recipients, model, log): model.found_newest = True index_msg = mocker.patch('zulipterminal.model.index_messages', return_value={}) create_msg_box_list = mocker.patch( 'zulipterminal.model.' 'create_msg_box_list', return_value=["msg_w"]) set_count = mocker.patch('zulipterminal.model.set_count') model.msg_list = mocker.Mock() model.msg_list.log = [] model.narrow = narrow model.recipients = recipients model.user_id = user_profile['user_id'] event = {'message': response} model.append_message(event) assert model.msg_list.log == log set_count.assert_called_once_with([response['id']], self.controller, 1) model.found_newest = False model.append_message(event) # LOG REMAINS THE SAME IF UPDATE IS FALSE assert model.msg_list.log == log @pytest.mark.parametrize('response, update_call_count, new_index', [ ({ # Only subject of 1 message is updated. 'message_id': 1, 'subject': 'new subject', 'message_ids': [1], }, 1, { 'messages': { 1: { 'id': 1, 'content': 'old content', 'subject': 'new subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'old subject' }}, 'edited_messages': {1} }), ({ # Subject of 2 messages is updated 'message_id': 1, 'subject': 'new subject', 'message_ids': [1, 2], }, 2, { 'messages': { 1: { 'id': 1, 'content': 'old content', 'subject': 'new subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'new subject' }}, 'edited_messages': {1} }), ({ # Message content is updated 'message_id': 1, 'rendered_content': '<p>new content</p>', }, 1, { 'messages': { 1: { 'id': 1, 'content': '<p>new content</p>', 'subject': 'old subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'old subject' }}, 'edited_messages': {1} }), ({ # Both message content and subject is updated. 'message_id': 1, 'rendered_content': '<p>new content</p>', 'subject': 'new subject', 'message_ids': [1], }, 2, { 'messages': { 1: { 'id': 1, 'content': '<p>new content</p>', 'subject': 'new subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'old subject' }}, 'edited_messages': {1} }), ({ # Some new type of update which we don't handle yet. 'message_id': 1, 'foo': 'boo', }, 0, { 'messages': { 1: { 'id': 1, 'content': 'old content', 'subject': 'old subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'old subject' }}, 'edited_messages': {1} }), ({ # message_id not present in index. 'message_id': 3, 'rendered_content': '<p>new content</p>', 'subject': 'new subject', 'message_ids': [3], }, 0, { 'messages': { 1: { 'id': 1, 'content': 'old content', 'subject': 'old subject' }, 2: { 'id': 2, 'content': 'old content', 'subject': 'old subject' }}, 'edited_messages': set() }), ]) def test_update_message(self, mocker, model, response, new_index, update_call_count): model.index = { 'messages': { message_id: { 'id': message_id, 'content': 'old content', 'subject': 'old subject', } for message_id in [1, 2] }, 'edited_messages': set() } mocker.patch('zulipterminal.model.Model.update_rendered_view') model.update_message(response) assert model.index == new_index assert model.update_rendered_view.call_count == update_call_count @pytest.mark.parametrize('subject, narrow, new_log_len', [ ('foo', [['stream', 'boo'], ['topic', 'foo']], 2), ('foo', [['stream', 'boo'], ['topic', 'not foo']], 1), ('foo', [], 2), ], ids=[ 'msgbox_updated_in_topic_narrow', 'msgbox_removed_due_to_topic_narrow_mismatch', 'msgbox_updated_in_all_messages_narrow', ]) def test_update_rendered_view(self, mocker, model, subject, narrow, new_log_len, msg_id=1): msg_w = mocker.Mock() other_msg_w = mocker.Mock() msg_w.original_widget.message = {'id': msg_id, 'subject': subject} model.narrow = narrow other_msg_w.original_widget.message = {'id': 2} model.msg_list = mocker.Mock() model.msg_list.log = [msg_w, other_msg_w] # New msg widget generated after updating index. new_msg_w = mocker.Mock() cmbl = mocker.patch('zulipterminal.model.create_msg_box_list', return_value=[new_msg_w]) model.update_rendered_view(msg_id) # If there are 2 msgs and first one is updated, next one is updated too if new_log_len == 2: other_msg_w = new_msg_w assert model.msg_list.log == [new_msg_w, other_msg_w][-new_log_len:] assert model.controller.update_screen.called @pytest.mark.parametrize( 'subject, narrow, narrow_changed', [ ('foo', [['stream', 'boo'], ['topic', 'foo']], False), ('foo', [['stream', 'boo'], ['topic', 'not foo']], True), ('foo', [], False), ], ids=[ 'same_topic_narrow', 'previous_topic_narrow_empty_so_change_narrow', 'same_all_messages_narrow', ]) def test_update_rendered_view_change_narrow(self, mocker, model, subject, narrow, narrow_changed, msg_id=1): msg_w = mocker.Mock() other_msg_w = mocker.Mock() msg_w.original_widget.message = {'id': msg_id, 'subject': subject} model.narrow = narrow model.msg_list = mocker.Mock() model.msg_list.log = [msg_w] # New msg widget generated after updating index. new_msg_w = mocker.Mock() cmbl = mocker.patch('zulipterminal.model.create_msg_box_list', return_value=[new_msg_w]) model.update_rendered_view(msg_id) assert model.controller.narrow_to_topic.called == narrow_changed assert model.controller.update_screen.called @pytest.mark.parametrize('response, index', [({ 'emoji_code': '1f44d', 'id': 2, 'user': { 'email': '*****@*****.**', 'user_id': 5140, 'full_name': 'Foo Boo' }, 'reaction_type': 'unicode_emoji', 'message_id': 1, 'emoji_name': 'thumbs_up', 'type': 'reaction', 'op': 'add' }, { 'messages': { 1: { 'id': 1, 'content': 'Boo is Foo', 'reactions': [{ 'user': { 'email': '*****@*****.**', 'user_id': 1, 'full_name': 'Foo Boo' }, 'reaction_type': 'unicode_emoji', 'emoji_code': '1232', 'emoji_name': 'thumbs_up' }], }, 2: { 'id': 2, 'content': "Boo is not Foo", 'reactions': [], } } })]) def test_update_reaction(self, mocker, model, response, index): model.index = index model.msg_list = mocker.Mock() mock_msg = mocker.Mock() another_msg = mocker.Mock() model.msg_list.log = [mock_msg, another_msg] mock_msg.original_widget.message = index['messages'][1] another_msg.original_widget.message = index['messages'][2] mocker.patch('zulipterminal.model.create_msg_box_list', return_value=[mock_msg]) model.update_reaction(response) update_emoji = model.index['messages'][1]['reactions'][1]['emoji_code'] assert update_emoji == response['emoji_code'] self.controller.update_screen.assert_called_once_with() # TEST FOR FALSE CASES model.index['messages'][1] = {} model.update_reaction(response) # If there was no message earlier then don't update assert model.index['messages'][1] == {} @pytest.mark.parametrize('response, index', [({ 'emoji_code': '1f44d', 'id': 2, 'user': { 'email': '*****@*****.**', 'user_id': 5140, 'full_name': 'Foo Boo' }, 'reaction_type': 'unicode_emoji', 'message_id': 1, 'emoji_name': 'thumbs_up', 'type': 'reaction', 'op': 'add' }, { 'messages': { 1: { 'id': 1, 'content': 'Boo is Foo', 'reactions': [{ 'user': { 'email': '*****@*****.**', 'user_id': 1, 'full_name': 'Foo Boo' }, 'reaction_type': 'unicode_emoji', 'emoji_code': '1232', 'emoji_name': 'thumbs_up' }], }, 2: { 'id': 2, 'content': "Boo is not Foo", 'reactions': [], } } })]) def test_update_reaction_remove_reaction(self, mocker, model, response, index): model.index = index model.msg_list = mocker.Mock() mock_msg = mocker.Mock() another_msg = mocker.Mock() model.msg_list.log = [mock_msg, another_msg] mock_msg.original_widget.message = index['messages'][1] another_msg.original_widget.message = index['messages'][2] mocker.patch('zulipterminal.model.create_msg_box_list', return_value=[mock_msg]) # Test removing of reaction. response['op'] = 'remove' model.update_reaction(response) assert len(model.index['messages'][1]['reactions']) == 1 def test_update_star_status_no_index(self, mocker, model): model.index = dict(messages={}) # Not indexed event = dict(messages=[1], flag='starred', all=False) mocker.patch('zulipterminal.model.Model.update_rendered_view') model.update_message_flag_status(event) assert model.index == dict(messages={}) model.update_rendered_view.assert_not_called() def test_update_star_status_invalid_operation(self, mocker, model): model.index = dict(messages={1: {'flags': None}}) # Minimal event = { 'messages': [1], 'type': 'update_message_flags', 'flag': 'starred', 'operation': 'OTHER', # not 'add' or 'remove' 'all': False, } mocker.patch('zulipterminal.model.Model.update_rendered_view') with pytest.raises(RuntimeError): model.update_message_flag_status(event) model.update_rendered_view.assert_not_called() @pytest.mark.parametrize('event_message_ids, indexed_ids', [ ([1], [1]), ([1, 2], [1]), ([1, 2], [1, 2]), ([1], [1, 2]), ([], [1, 2]), ([1, 2], []), ]) @pytest.mark.parametrize('event_op, flags_before, flags_after', [ ('add', [], ['starred']), ('add', ['read'], ['read', 'starred']), ('add', ['starred'], ['starred']), ('add', ['read', 'starred'], ['read', 'starred']), ('remove', [], []), ('remove', ['read'], ['read']), ('remove', ['starred'], []), ('remove', ['read', 'starred'], ['read']), ('remove', ['starred', 'read'], ['read']), ]) def test_update_star_status(self, mocker, model, event_op, event_message_ids, indexed_ids, flags_before, flags_after): model.index = dict(messages={ msg_id: { 'flags': flags_before } for msg_id in indexed_ids }) event = { 'messages': event_message_ids, 'type': 'update_message_flags', 'flag': 'starred', 'operation': event_op, 'all': False, } mocker.patch('zulipterminal.model.Model.update_rendered_view') model.update_message_flag_status(event) changed_ids = set(indexed_ids) & set(event_message_ids) for changed_id in changed_ids: assert model.index['messages'][changed_id]['flags'] == flags_after (model.update_rendered_view.has_calls( [mocker.call(changed_id) for changed_id in changed_ids])) for unchanged_id in (set(indexed_ids) - set(event_message_ids)): assert ( model.index['messages'][unchanged_id]['flags'] == flags_before) @pytest.mark.parametrize( 'narrow, event, called', [ # Not in PM Narrow ([], {}, False), # Not in PM Narrow with sender ( [['pm_with', '*****@*****.**']], { 'type': 'typing', 'op': 'start', 'sender': { 'user_id': 4, 'email': '*****@*****.**' }, 'recipients': [{ 'user_id': 4, 'email': '*****@*****.**' }, { 'user_id': 5, 'email': '*****@*****.**' }], 'id': 0 }, False, ), # In PM narrow with the sender, OP - 'start' ( [['pm_with', '*****@*****.**']], { 'type': 'typing', 'op': 'start', 'sender': { 'user_id': 4, 'email': '*****@*****.**' }, 'recipients': [{ 'user_id': 4, 'email': '*****@*****.**' }, { 'user_id': 5, 'email': '*****@*****.**' }], 'id': 0 }, True, ), # OP - 'stop' ( [['pm_with', '*****@*****.**']], { 'type': 'typing', 'op': 'stop', 'sender': { 'user_id': 4, 'email': '*****@*****.**' }, 'recipients': [{ 'user_id': 4, 'email': '*****@*****.**' }, { 'user_id': 5, 'email': '*****@*****.**' }], 'id': 0 }, True, ) ], ids=[ 'not_in_pm_narrow', 'not_in_pm_narrow_with_sender', 'start', 'stop' ]) def test_handle_typing_event(self, mocker, model, narrow, event, called): mocker.patch('zulipterminal.ui.View.set_footer_text') model.narrow = narrow model.user_dict = {'*****@*****.**': {'full_name': 'hamlet'}} model.handle_typing_event(event) assert model.controller.view.set_footer_text.called == called @pytest.mark.parametrize('event, final_muted_streams, ', [({ 'property': 'in_home_view', 'stream_id': 19, 'value': True }, {15}), ({ 'property': 'in_home_view', 'stream_id': 30, 'value': False }, {15, 19, 30})], ids=['remove_19', 'add_30']) def test_update_subscription(self, model, mocker, event, stream_button, final_muted_streams): model.muted_streams = {15, 19} model.controller.view.stream_id_to_button = { event['stream_id']: stream_button # stream id is known } mark_muted = mocker.patch( 'zulipterminal.ui_tools.buttons.StreamButton.mark_muted') mark_unmuted = mocker.patch( 'zulipterminal.ui_tools.buttons.StreamButton.mark_unmuted') model.update_subscription(event) assert model.muted_streams == final_muted_streams if event['value']: mark_unmuted.assert_called_once_with() else: mark_muted.assert_called_once_with() model.controller.update_screen.assert_called_once_with()
def test_powerset( iterable: Iterable[Any], map_func: Callable[[Any], Any], expected_powerset: List[Any], ) -> None: assert powerset(iterable, map_func) == expected_powerset