From 123b4c1877a7fe4e8dce0303f38e07db0e8d947b Mon Sep 17 00:00:00 2001 From: Tim Abbott Date: Wed, 8 Mar 2017 03:07:51 -0800 Subject: [PATCH] tests: Extract zerver/tests/test_bots.py. --- zerver/tests/test_bots.py | 822 ++++++++++++++++++++++++++++++++++++++ zerver/tests/tests.py | 806 +------------------------------------ 2 files changed, 823 insertions(+), 805 deletions(-) create mode 100644 zerver/tests/test_bots.py diff --git a/zerver/tests/test_bots.py b/zerver/tests/test_bots.py new file mode 100644 index 0000000000..2474f5ab5d --- /dev/null +++ b/zerver/tests/test_bots.py @@ -0,0 +1,822 @@ +from __future__ import absolute_import +from __future__ import print_function + +import filecmp +import os +import ujson + +from django.core import mail +from django.http import HttpResponse +from django.test import override_settings +from mock import patch +from typing import Any, Dict, List + +from zerver.lib.actions import do_change_stream_invite_only +from zerver.models import get_realm, get_stream, get_user_profile_by_email, \ + Realm, Stream, UserProfile +from zerver.lib.test_classes import ZulipTestCase +from zerver.lib.test_helpers import ( + avatar_disk_path, get_test_image_file, tornado_redirected_to_list, +) + +class BotTest(ZulipTestCase): + def assert_num_bots_equal(self, count): + # type: (int) -> None + result = self.client_get("/json/bots") + self.assert_json_success(result) + json = ujson.loads(result.content) + self.assertEqual(count, len(json['bots'])) + + def create_bot(self, **extras): + # type: (**Any) -> Dict[str, Any] + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + bot_info.update(extras) + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + return ujson.loads(result.content) + + def deactivate_bot(self): + # type: () -> None + result = self.client_delete("/json/bots/hambot-bot@zulip.com") + self.assert_json_success(result) + + def test_add_bot_with_bad_username(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + bot_info = dict( + full_name='', + short_name='', + ) + result = self.client_post("/json/bots", bot_info) + self.assert_json_error(result, 'Bad name or username') + self.assert_num_bots_equal(0) + + def test_add_bot(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + events = [] # type: List[Dict[str, Any]] + with tornado_redirected_to_list(events): + result = self.create_bot() + self.assert_num_bots_equal(1) + + bot = get_user_profile_by_email('hambot-bot@zulip.com') + + event = [e for e in events if e['event']['type'] == 'realm_bot'][0] + self.assertEqual( + dict( + type='realm_bot', + op='add', + bot=dict(email='hambot-bot@zulip.com', + user_id=bot.id, + full_name='The Bot of Hamlet', + is_active=True, + api_key=result['api_key'], + avatar_url=result['avatar_url'], + default_sending_stream=None, + default_events_register_stream=None, + default_all_public_streams=False, + owner='hamlet@zulip.com') + ), + event['event'] + ) + + users_result = self.client_get('/json/users') + members = ujson.loads(users_result.content)['members'] + bots = [m for m in members if m['email'] == 'hambot-bot@zulip.com'] + self.assertEqual(len(bots), 1) + bot = bots[0] + self.assertEqual(bot['bot_owner'], 'hamlet@zulip.com') + self.assertEqual(bot['user_id'], get_user_profile_by_email('hambot-bot@zulip.com').id) + + def test_add_bot_with_username_in_use(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + result = self.create_bot() + self.assert_num_bots_equal(1) + + bot_info = dict( + full_name='Duplicate', + short_name='hambot', + ) + result = self.client_post("/json/bots", bot_info) + self.assert_json_error(result, 'Username already in use') + + def test_add_bot_with_user_avatar(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + with get_test_image_file('img.png') as fp: + self.create_bot(file=fp) + profile = get_user_profile_by_email('hambot-bot@zulip.com') + # Make sure that avatar image that we've uploaded is same with avatar image in the server + self.assertTrue(filecmp.cmp(fp.name, + os.path.splitext(avatar_disk_path(profile))[0] + + ".original")) + self.assert_num_bots_equal(1) + + self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER) + self.assertTrue(os.path.exists(avatar_disk_path(profile))) + + def test_add_bot_with_too_many_files(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + with get_test_image_file('img.png') as fp1, \ + get_test_image_file('img.gif') as fp2: + bot_info = dict( + full_name='whatever', + short_name='whatever', + file1=fp1, + file2=fp2, + ) + result = self.client_post("/json/bots", bot_info) + self.assert_json_error(result, 'You may only upload one file at a time') + self.assert_num_bots_equal(0) + + def test_add_bot_with_default_sending_stream(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + result = self.create_bot(default_sending_stream='Denmark') + self.assert_num_bots_equal(1) + self.assertEqual(result['default_sending_stream'], 'Denmark') + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.default_sending_stream.name, 'Denmark') + + def test_add_bot_with_default_sending_stream_not_subscribed(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + result = self.create_bot(default_sending_stream='Rome') + self.assert_num_bots_equal(1) + self.assertEqual(result['default_sending_stream'], 'Rome') + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.default_sending_stream.name, 'Rome') + + def test_bot_add_subscription(self): + # type: () -> None + """ + Calling POST /json/users/me/subscriptions should successfully add + streams, and a stream to the + list of subscriptions and confirm the right number of events + are generated. + When 'principals' has a bot, no notification message event or invitation email + is sent when add_subscriptions_backend is called in the above api call. + """ + self.login("hamlet@zulip.com") + + # Normal user i.e. not a bot. + request_data = { + 'principals': '["iago@zulip.com"]' + } + events = [] # type: List[Dict[str, Any]] + with tornado_redirected_to_list(events): + result = self.common_subscribe_to_streams("hamlet@zulip.com", ['Rome'], request_data) + self.assert_json_success(result) + + msg_event = [e for e in events if e['event']['type'] == 'message'] + self.assert_length(msg_event, 1) # Notification message event is sent. + + # Create a bot. + self.assert_num_bots_equal(0) + result = self.create_bot() + self.assert_num_bots_equal(1) + + # A bot + bot_request_data = { + 'principals': '["hambot-bot@zulip.com"]' + } + events_bot = [] # type: List[Dict[str, Any]] + with tornado_redirected_to_list(events_bot): + result = self.common_subscribe_to_streams("hamlet@zulip.com", ['Rome'], bot_request_data) + self.assert_json_success(result) + + # No notification message event or invitation email is sent because of bot. + msg_event = [e for e in events_bot if e['event']['type'] == 'message'] + self.assert_length(msg_event, 0) + self.assertEqual(len(events_bot), len(events) - 1) + + # Test runner automatically redirects all sent email to a dummy 'outbox'. + self.assertEqual(len(mail.outbox), 0) + + def test_add_bot_with_default_sending_stream_private_allowed(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = get_stream("Denmark", user_profile.realm) + self.subscribe_to_stream(user_profile.email, stream.name) + do_change_stream_invite_only(stream, True) + + self.assert_num_bots_equal(0) + events = [] # type: List[Dict[str, Any]] + with tornado_redirected_to_list(events): + result = self.create_bot(default_sending_stream='Denmark') + self.assert_num_bots_equal(1) + self.assertEqual(result['default_sending_stream'], 'Denmark') + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.default_sending_stream.name, 'Denmark') + + event = [e for e in events if e['event']['type'] == 'realm_bot'][0] + self.assertEqual( + dict( + type='realm_bot', + op='add', + bot=dict(email='hambot-bot@zulip.com', + user_id=profile.id, + full_name='The Bot of Hamlet', + is_active=True, + api_key=result['api_key'], + avatar_url=result['avatar_url'], + default_sending_stream='Denmark', + default_events_register_stream=None, + default_all_public_streams=False, + owner='hamlet@zulip.com') + ), + event['event'] + ) + self.assertEqual(event['users'], (user_profile.id,)) + + def test_add_bot_with_default_sending_stream_private_denied(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = get_stream("Denmark", user_profile.realm) + self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") + do_change_stream_invite_only(stream, True) + + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + 'default_sending_stream': 'Denmark', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_error(result, "Invalid stream name 'Denmark'") + + def test_add_bot_with_default_events_register_stream(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + result = self.create_bot(default_events_register_stream='Denmark') + self.assert_num_bots_equal(1) + self.assertEqual(result['default_events_register_stream'], 'Denmark') + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.default_events_register_stream.name, 'Denmark') + + def test_add_bot_with_default_events_register_stream_private_allowed(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = self.subscribe_to_stream(user_profile.email, 'Denmark') + do_change_stream_invite_only(stream, True) + + self.assert_num_bots_equal(0) + events = [] # type: List[Dict[str, Any]] + with tornado_redirected_to_list(events): + result = self.create_bot(default_events_register_stream='Denmark') + self.assert_num_bots_equal(1) + self.assertEqual(result['default_events_register_stream'], 'Denmark') + + bot_profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(bot_profile.default_events_register_stream.name, 'Denmark') + + event = [e for e in events if e['event']['type'] == 'realm_bot'][0] + self.assertEqual( + dict( + type='realm_bot', + op='add', + bot=dict(email='hambot-bot@zulip.com', + full_name='The Bot of Hamlet', + user_id=bot_profile.id, + is_active=True, + api_key=result['api_key'], + avatar_url=result['avatar_url'], + default_sending_stream=None, + default_events_register_stream='Denmark', + default_all_public_streams=False, + owner='hamlet@zulip.com') + ), + event['event'] + ) + self.assertEqual(event['users'], (user_profile.id,)) + + def test_add_bot_with_default_events_register_stream_private_denied(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = get_stream("Denmark", user_profile.realm) + self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") + do_change_stream_invite_only(stream, True) + + self.assert_num_bots_equal(0) + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + 'default_events_register_stream': 'Denmark', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_error(result, "Invalid stream name 'Denmark'") + + def test_add_bot_with_default_all_public_streams(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + result = self.create_bot(default_all_public_streams=ujson.dumps(True)) + self.assert_num_bots_equal(1) + self.assertTrue(result['default_all_public_streams']) + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.default_all_public_streams, True) + + def test_deactivate_bot(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + self.create_bot() + self.assert_num_bots_equal(1) + self.deactivate_bot() + # You can deactivate the same bot twice. + self.deactivate_bot() + self.assert_num_bots_equal(0) + + def test_deactivate_bogus_bot(self): + # type: () -> None + """Deleting a bogus bot will succeed silently.""" + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + self.create_bot() + self.assert_num_bots_equal(1) + result = self.client_delete("/json/bots/bogus-bot@zulip.com") + self.assert_json_error(result, 'No such bot') + self.assert_num_bots_equal(1) + + def test_bot_deactivation_attacks(self): + # type: () -> None + """You cannot deactivate somebody else's bot.""" + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + self.create_bot() + self.assert_num_bots_equal(1) + + # Have Othello try to deactivate both Hamlet and + # Hamlet's bot. + self.login("othello@zulip.com") + + # Can not deactivate a user as a bot + result = self.client_delete("/json/bots/hamlet@zulip.com") + self.assert_json_error(result, 'No such bot') + + result = self.client_delete("/json/bots/hambot-bot@zulip.com") + self.assert_json_error(result, 'Insufficient permission') + + # But we don't actually deactivate the other person's bot. + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(1) + + # Can not deactivate a bot as a user + result = self.client_delete("/json/users/hambot-bot@zulip.com") + self.assert_json_error(result, 'No such user') + self.assert_num_bots_equal(1) + + def test_bot_permissions(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.assert_num_bots_equal(0) + self.create_bot() + self.assert_num_bots_equal(1) + + # Have Othello try to mess with Hamlet's bots. + self.login("othello@zulip.com") + + result = self.client_post("/json/bots/hambot-bot@zulip.com/api_key/regenerate") + self.assert_json_error(result, 'Insufficient permission') + + bot_info = { + 'full_name': 'Fred', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_error(result, 'Insufficient permission') + + def get_bot(self): + # type: () -> Dict[str, Any] + result = self.client_get("/json/bots") + bots = ujson.loads(result.content)['bots'] + return bots[0] + + def test_update_api_key(self): + # type: () -> None + self.login("hamlet@zulip.com") + self.create_bot() + bot = self.get_bot() + old_api_key = bot['api_key'] + result = self.client_post('/json/bots/hambot-bot@zulip.com/api_key/regenerate') + self.assert_json_success(result) + new_api_key = ujson.loads(result.content)['api_key'] + self.assertNotEqual(old_api_key, new_api_key) + bot = self.get_bot() + self.assertEqual(new_api_key, bot['api_key']) + + def test_update_api_key_for_invalid_user(self): + # type: () -> None + self.login("hamlet@zulip.com") + result = self.client_post('/json/bots/nonexistentuser@zulip.com/api_key/regenerate') + self.assert_json_error(result, 'No such user') + + def test_patch_bot_full_name(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'full_name': 'Fred', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + full_name = ujson.loads(result.content)['full_name'] + self.assertEqual('Fred', full_name) + + bot = self.get_bot() + self.assertEqual('Fred', bot['full_name']) + + def test_patch_bot_owner(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'bot_owner': 'othello@zulip.com', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + # Test bot's owner has been changed successfully. + bot_owner = ujson.loads(result.content)['bot_owner'] + self.assertEqual(bot_owner, 'othello@zulip.com') + + self.login('othello@zulip.com') + bot = self.get_bot() + self.assertEqual('The Bot of Hamlet', bot['full_name']) + + @override_settings(LOCAL_UPLOADS_DIR='var/bot_avatar') + def test_patch_bot_avatar(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR) + + # Try error case first (too many files): + with get_test_image_file('img.png') as fp1, \ + get_test_image_file('img.gif') as fp2: + result = self.client_patch_multipart( + '/json/bots/hambot-bot@zulip.com', + dict(file1=fp1, file2=fp2)) + self.assert_json_error(result, 'You may only upload one file at a time') + + profile = get_user_profile_by_email("hambot-bot@zulip.com") + self.assertEqual(profile.avatar_version, 1) + + # HAPPY PATH + with get_test_image_file('img.png') as fp: + result = self.client_patch_multipart( + '/json/bots/hambot-bot@zulip.com', + dict(file=fp)) + profile = get_user_profile_by_email('hambot-bot@zulip.com') + self.assertEqual(profile.avatar_version, 2) + # Make sure that avatar image that we've uploaded is same with avatar image in the server + self.assertTrue(filecmp.cmp(fp.name, + os.path.splitext(avatar_disk_path(profile))[0] + + ".original")) + self.assert_json_success(result) + + self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER) + self.assertTrue(os.path.exists(avatar_disk_path(profile))) + + def test_patch_bot_to_stream(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_sending_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_sending_stream = ujson.loads(result.content)['default_sending_stream'] + self.assertEqual('Denmark', default_sending_stream) + + bot = self.get_bot() + self.assertEqual('Denmark', bot['default_sending_stream']) + + def test_patch_bot_to_stream_not_subscribed(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_sending_stream': 'Rome', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_sending_stream = ujson.loads(result.content)['default_sending_stream'] + self.assertEqual('Rome', default_sending_stream) + + bot = self.get_bot() + self.assertEqual('Rome', bot['default_sending_stream']) + + def test_patch_bot_to_stream_none(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_sending_stream': '', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_sending_stream = get_user_profile_by_email( + "hambot-bot@zulip.com").default_sending_stream + self.assertEqual(None, default_sending_stream) + + bot = self.get_bot() + self.assertEqual(None, bot['default_sending_stream']) + + def test_patch_bot_to_stream_private_allowed(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = self.subscribe_to_stream(user_profile.email, "Denmark") + do_change_stream_invite_only(stream, True) + + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + + bot_info = { + 'default_sending_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_sending_stream = ujson.loads(result.content)['default_sending_stream'] + self.assertEqual('Denmark', default_sending_stream) + + bot = self.get_bot() + self.assertEqual('Denmark', bot['default_sending_stream']) + + def test_patch_bot_to_stream_private_denied(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = get_stream("Denmark", user_profile.realm) + self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") + do_change_stream_invite_only(stream, True) + + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + + bot_info = { + 'default_sending_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_error(result, "Invalid stream name 'Denmark'") + + def test_patch_bot_to_stream_not_found(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_sending_stream': 'missing', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_error(result, "Invalid stream name 'missing'") + + def test_patch_bot_events_register_stream(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_events_register_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_events_register_stream = ujson.loads(result.content)['default_events_register_stream'] + self.assertEqual('Denmark', default_events_register_stream) + + bot = self.get_bot() + self.assertEqual('Denmark', bot['default_events_register_stream']) + + def test_patch_bot_events_register_stream_allowed(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = self.subscribe_to_stream(user_profile.email, "Denmark") + do_change_stream_invite_only(stream, True) + + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_events_register_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_events_register_stream = ujson.loads(result.content)['default_events_register_stream'] + self.assertEqual('Denmark', default_events_register_stream) + + bot = self.get_bot() + self.assertEqual('Denmark', bot['default_events_register_stream']) + + def test_patch_bot_events_register_stream_denied(self): + # type: () -> None + self.login("hamlet@zulip.com") + user_profile = get_user_profile_by_email("hamlet@zulip.com") + stream = get_stream("Denmark", user_profile.realm) + self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") + do_change_stream_invite_only(stream, True) + + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_events_register_stream': 'Denmark', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_error(result, "Invalid stream name 'Denmark'") + + def test_patch_bot_events_register_stream_none(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_events_register_stream': '', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_events_register_stream = get_user_profile_by_email( + "hambot-bot@zulip.com").default_events_register_stream + self.assertEqual(None, default_events_register_stream) + + bot = self.get_bot() + self.assertEqual(None, bot['default_events_register_stream']) + + def test_patch_bot_events_register_stream_not_found(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_events_register_stream': 'missing', + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_error(result, "Invalid stream name 'missing'") + + def test_patch_bot_default_all_public_streams_true(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_all_public_streams': ujson.dumps(True), + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_events_register_stream = ujson.loads(result.content)['default_all_public_streams'] + self.assertEqual(default_events_register_stream, True) + + bot = self.get_bot() + self.assertEqual(bot['default_all_public_streams'], True) + + def test_patch_bot_default_all_public_streams_false(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'default_all_public_streams': ujson.dumps(False), + } + result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + default_events_register_stream = ujson.loads(result.content)['default_all_public_streams'] + self.assertEqual(default_events_register_stream, False) + + bot = self.get_bot() + self.assertEqual(bot['default_all_public_streams'], False) + + def test_patch_bot_via_post(self): + # type: () -> None + self.login("hamlet@zulip.com") + bot_info = { + 'full_name': 'The Bot of Hamlet', + 'short_name': 'hambot', + } + result = self.client_post("/json/bots", bot_info) + self.assert_json_success(result) + bot_info = { + 'full_name': 'Fred', + 'method': 'PATCH' + } + result = self.client_post("/json/bots/hambot-bot@zulip.com", bot_info) + self.assert_json_success(result) + + full_name = ujson.loads(result.content)['full_name'] + self.assertEqual('Fred', full_name) + + bot = self.get_bot() + self.assertEqual('Fred', bot['full_name']) + + def test_patch_bogus_bot(self): + # type: () -> None + """Deleting a bogus bot will succeed silently.""" + self.login("hamlet@zulip.com") + self.create_bot() + bot_info = { + 'full_name': 'Fred', + } + result = self.client_patch("/json/bots/nonexistent-bot@zulip.com", bot_info) + self.assert_json_error(result, 'No such user') + self.assert_num_bots_equal(1) diff --git a/zerver/tests/tests.py b/zerver/tests/tests.py index dc7f67ecd6..226135048c 100644 --- a/zerver/tests/tests.py +++ b/zerver/tests/tests.py @@ -32,8 +32,7 @@ from zerver.lib.email_mirror import create_missed_message_address from zerver.lib.actions import \ get_emails_from_user_ids, do_deactivate_user, do_reactivate_user, \ do_change_is_admin, extract_recipients, \ - do_set_realm_name, do_deactivate_realm, \ - do_change_stream_invite_only + do_set_realm_name, do_deactivate_realm from zerver.lib.notifications import handle_missedmessage_emails, \ send_missedmessage_email from zerver.lib.sessions import get_session_dict_user @@ -51,7 +50,6 @@ import sys import time import ujson import random -import filecmp import subprocess K = TypeVar('K') @@ -702,808 +700,6 @@ class ActivateTest(ZulipTestCase): result = self.client_post('/json/users/hamlet@zulip.com/reactivate') self.assert_json_error(result, 'Insufficient permission') -class BotTest(ZulipTestCase): - def assert_num_bots_equal(self, count): - # type: (int) -> None - result = self.client_get("/json/bots") - self.assert_json_success(result) - json = ujson.loads(result.content) - self.assertEqual(count, len(json['bots'])) - - def create_bot(self, **extras): - # type: (**Any) -> Dict[str, Any] - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - bot_info.update(extras) - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - return ujson.loads(result.content) - - def deactivate_bot(self): - # type: () -> None - result = self.client_delete("/json/bots/hambot-bot@zulip.com") - self.assert_json_success(result) - - def test_add_bot_with_bad_username(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - bot_info = dict( - full_name='', - short_name='', - ) - result = self.client_post("/json/bots", bot_info) - self.assert_json_error(result, 'Bad name or username') - self.assert_num_bots_equal(0) - - def test_add_bot(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - events = [] # type: List[Dict[str, Any]] - with tornado_redirected_to_list(events): - result = self.create_bot() - self.assert_num_bots_equal(1) - - bot = get_user_profile_by_email('hambot-bot@zulip.com') - - event = [e for e in events if e['event']['type'] == 'realm_bot'][0] - self.assertEqual( - dict( - type='realm_bot', - op='add', - bot=dict(email='hambot-bot@zulip.com', - user_id=bot.id, - full_name='The Bot of Hamlet', - is_active=True, - api_key=result['api_key'], - avatar_url=result['avatar_url'], - default_sending_stream=None, - default_events_register_stream=None, - default_all_public_streams=False, - owner='hamlet@zulip.com') - ), - event['event'] - ) - - users_result = self.client_get('/json/users') - members = ujson.loads(users_result.content)['members'] - bots = [m for m in members if m['email'] == 'hambot-bot@zulip.com'] - self.assertEqual(len(bots), 1) - bot = bots[0] - self.assertEqual(bot['bot_owner'], 'hamlet@zulip.com') - self.assertEqual(bot['user_id'], get_user_profile_by_email('hambot-bot@zulip.com').id) - - def test_add_bot_with_username_in_use(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - result = self.create_bot() - self.assert_num_bots_equal(1) - - bot_info = dict( - full_name='Duplicate', - short_name='hambot', - ) - result = self.client_post("/json/bots", bot_info) - self.assert_json_error(result, 'Username already in use') - - def test_add_bot_with_user_avatar(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - with get_test_image_file('img.png') as fp: - self.create_bot(file=fp) - profile = get_user_profile_by_email('hambot-bot@zulip.com') - # Make sure that avatar image that we've uploaded is same with avatar image in the server - self.assertTrue(filecmp.cmp(fp.name, - os.path.splitext(avatar_disk_path(profile))[0] + - ".original")) - self.assert_num_bots_equal(1) - - self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER) - self.assertTrue(os.path.exists(avatar_disk_path(profile))) - - def test_add_bot_with_too_many_files(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - with get_test_image_file('img.png') as fp1, \ - get_test_image_file('img.gif') as fp2: - bot_info = dict( - full_name='whatever', - short_name='whatever', - file1=fp1, - file2=fp2, - ) - result = self.client_post("/json/bots", bot_info) - self.assert_json_error(result, 'You may only upload one file at a time') - self.assert_num_bots_equal(0) - - def test_add_bot_with_default_sending_stream(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - result = self.create_bot(default_sending_stream='Denmark') - self.assert_num_bots_equal(1) - self.assertEqual(result['default_sending_stream'], 'Denmark') - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.default_sending_stream.name, 'Denmark') - - def test_add_bot_with_default_sending_stream_not_subscribed(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - result = self.create_bot(default_sending_stream='Rome') - self.assert_num_bots_equal(1) - self.assertEqual(result['default_sending_stream'], 'Rome') - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.default_sending_stream.name, 'Rome') - - def test_bot_add_subscription(self): - # type: () -> None - """ - Calling POST /json/users/me/subscriptions should successfully add - streams, and a stream to the - list of subscriptions and confirm the right number of events - are generated. - When 'principals' has a bot, no notification message event or invitation email - is sent when add_subscriptions_backend is called in the above api call. - """ - self.login("hamlet@zulip.com") - - # Normal user i.e. not a bot. - request_data = { - 'principals': '["iago@zulip.com"]' - } - events = [] # type: List[Dict[str, Any]] - with tornado_redirected_to_list(events): - result = self.common_subscribe_to_streams("hamlet@zulip.com", ['Rome'], request_data) - self.assert_json_success(result) - - msg_event = [e for e in events if e['event']['type'] == 'message'] - self.assert_length(msg_event, 1) # Notification message event is sent. - - # Create a bot. - self.assert_num_bots_equal(0) - result = self.create_bot() - self.assert_num_bots_equal(1) - - # A bot - bot_request_data = { - 'principals': '["hambot-bot@zulip.com"]' - } - events_bot = [] # type: List[Dict[str, Any]] - with tornado_redirected_to_list(events_bot): - result = self.common_subscribe_to_streams("hamlet@zulip.com", ['Rome'], bot_request_data) - self.assert_json_success(result) - - # No notification message event or invitation email is sent because of bot. - msg_event = [e for e in events_bot if e['event']['type'] == 'message'] - self.assert_length(msg_event, 0) - self.assertEqual(len(events_bot), len(events) - 1) - - # Test runner automatically redirects all sent email to a dummy 'outbox'. - self.assertEqual(len(mail.outbox), 0) - - def test_add_bot_with_default_sending_stream_private_allowed(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = get_stream("Denmark", user_profile.realm) - self.subscribe_to_stream(user_profile.email, stream.name) - do_change_stream_invite_only(stream, True) - - self.assert_num_bots_equal(0) - events = [] # type: List[Dict[str, Any]] - with tornado_redirected_to_list(events): - result = self.create_bot(default_sending_stream='Denmark') - self.assert_num_bots_equal(1) - self.assertEqual(result['default_sending_stream'], 'Denmark') - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.default_sending_stream.name, 'Denmark') - - event = [e for e in events if e['event']['type'] == 'realm_bot'][0] - self.assertEqual( - dict( - type='realm_bot', - op='add', - bot=dict(email='hambot-bot@zulip.com', - user_id=profile.id, - full_name='The Bot of Hamlet', - is_active=True, - api_key=result['api_key'], - avatar_url=result['avatar_url'], - default_sending_stream='Denmark', - default_events_register_stream=None, - default_all_public_streams=False, - owner='hamlet@zulip.com') - ), - event['event'] - ) - self.assertEqual(event['users'], (user_profile.id,)) - - def test_add_bot_with_default_sending_stream_private_denied(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = get_stream("Denmark", user_profile.realm) - self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") - do_change_stream_invite_only(stream, True) - - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - 'default_sending_stream': 'Denmark', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_error(result, "Invalid stream name 'Denmark'") - - def test_add_bot_with_default_events_register_stream(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - result = self.create_bot(default_events_register_stream='Denmark') - self.assert_num_bots_equal(1) - self.assertEqual(result['default_events_register_stream'], 'Denmark') - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.default_events_register_stream.name, 'Denmark') - - def test_add_bot_with_default_events_register_stream_private_allowed(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = self.subscribe_to_stream(user_profile.email, 'Denmark') - do_change_stream_invite_only(stream, True) - - self.assert_num_bots_equal(0) - events = [] # type: List[Dict[str, Any]] - with tornado_redirected_to_list(events): - result = self.create_bot(default_events_register_stream='Denmark') - self.assert_num_bots_equal(1) - self.assertEqual(result['default_events_register_stream'], 'Denmark') - - bot_profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(bot_profile.default_events_register_stream.name, 'Denmark') - - event = [e for e in events if e['event']['type'] == 'realm_bot'][0] - self.assertEqual( - dict( - type='realm_bot', - op='add', - bot=dict(email='hambot-bot@zulip.com', - full_name='The Bot of Hamlet', - user_id=bot_profile.id, - is_active=True, - api_key=result['api_key'], - avatar_url=result['avatar_url'], - default_sending_stream=None, - default_events_register_stream='Denmark', - default_all_public_streams=False, - owner='hamlet@zulip.com') - ), - event['event'] - ) - self.assertEqual(event['users'], (user_profile.id,)) - - def test_add_bot_with_default_events_register_stream_private_denied(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = get_stream("Denmark", user_profile.realm) - self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") - do_change_stream_invite_only(stream, True) - - self.assert_num_bots_equal(0) - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - 'default_events_register_stream': 'Denmark', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_error(result, "Invalid stream name 'Denmark'") - - def test_add_bot_with_default_all_public_streams(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - result = self.create_bot(default_all_public_streams=ujson.dumps(True)) - self.assert_num_bots_equal(1) - self.assertTrue(result['default_all_public_streams']) - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.default_all_public_streams, True) - - def test_deactivate_bot(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - self.create_bot() - self.assert_num_bots_equal(1) - self.deactivate_bot() - # You can deactivate the same bot twice. - self.deactivate_bot() - self.assert_num_bots_equal(0) - - def test_deactivate_bogus_bot(self): - # type: () -> None - """Deleting a bogus bot will succeed silently.""" - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - self.create_bot() - self.assert_num_bots_equal(1) - result = self.client_delete("/json/bots/bogus-bot@zulip.com") - self.assert_json_error(result, 'No such bot') - self.assert_num_bots_equal(1) - - def test_bot_deactivation_attacks(self): - # type: () -> None - """You cannot deactivate somebody else's bot.""" - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - self.create_bot() - self.assert_num_bots_equal(1) - - # Have Othello try to deactivate both Hamlet and - # Hamlet's bot. - self.login("othello@zulip.com") - - # Can not deactivate a user as a bot - result = self.client_delete("/json/bots/hamlet@zulip.com") - self.assert_json_error(result, 'No such bot') - - result = self.client_delete("/json/bots/hambot-bot@zulip.com") - self.assert_json_error(result, 'Insufficient permission') - - # But we don't actually deactivate the other person's bot. - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(1) - - # Can not deactivate a bot as a user - result = self.client_delete("/json/users/hambot-bot@zulip.com") - self.assert_json_error(result, 'No such user') - self.assert_num_bots_equal(1) - - def test_bot_permissions(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.assert_num_bots_equal(0) - self.create_bot() - self.assert_num_bots_equal(1) - - # Have Othello try to mess with Hamlet's bots. - self.login("othello@zulip.com") - - result = self.client_post("/json/bots/hambot-bot@zulip.com/api_key/regenerate") - self.assert_json_error(result, 'Insufficient permission') - - bot_info = { - 'full_name': 'Fred', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_error(result, 'Insufficient permission') - - def get_bot(self): - # type: () -> Dict[str, Any] - result = self.client_get("/json/bots") - bots = ujson.loads(result.content)['bots'] - return bots[0] - - def test_update_api_key(self): - # type: () -> None - self.login("hamlet@zulip.com") - self.create_bot() - bot = self.get_bot() - old_api_key = bot['api_key'] - result = self.client_post('/json/bots/hambot-bot@zulip.com/api_key/regenerate') - self.assert_json_success(result) - new_api_key = ujson.loads(result.content)['api_key'] - self.assertNotEqual(old_api_key, new_api_key) - bot = self.get_bot() - self.assertEqual(new_api_key, bot['api_key']) - - def test_update_api_key_for_invalid_user(self): - # type: () -> None - self.login("hamlet@zulip.com") - result = self.client_post('/json/bots/nonexistentuser@zulip.com/api_key/regenerate') - self.assert_json_error(result, 'No such user') - - def test_patch_bot_full_name(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'full_name': 'Fred', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - full_name = ujson.loads(result.content)['full_name'] - self.assertEqual('Fred', full_name) - - bot = self.get_bot() - self.assertEqual('Fred', bot['full_name']) - - def test_patch_bot_owner(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'bot_owner': 'othello@zulip.com', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - # Test bot's owner has been changed successfully. - bot_owner = ujson.loads(result.content)['bot_owner'] - self.assertEqual(bot_owner, 'othello@zulip.com') - - self.login('othello@zulip.com') - bot = self.get_bot() - self.assertEqual('The Bot of Hamlet', bot['full_name']) - - @override_settings(LOCAL_UPLOADS_DIR='var/bot_avatar') - def test_patch_bot_avatar(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR) - - # Try error case first (too many files): - with get_test_image_file('img.png') as fp1, \ - get_test_image_file('img.gif') as fp2: - result = self.client_patch_multipart( - '/json/bots/hambot-bot@zulip.com', - dict(file1=fp1, file2=fp2)) - self.assert_json_error(result, 'You may only upload one file at a time') - - profile = get_user_profile_by_email("hambot-bot@zulip.com") - self.assertEqual(profile.avatar_version, 1) - - # HAPPY PATH - with get_test_image_file('img.png') as fp: - result = self.client_patch_multipart( - '/json/bots/hambot-bot@zulip.com', - dict(file=fp)) - profile = get_user_profile_by_email('hambot-bot@zulip.com') - self.assertEqual(profile.avatar_version, 2) - # Make sure that avatar image that we've uploaded is same with avatar image in the server - self.assertTrue(filecmp.cmp(fp.name, - os.path.splitext(avatar_disk_path(profile))[0] + - ".original")) - self.assert_json_success(result) - - self.assertEqual(profile.avatar_source, UserProfile.AVATAR_FROM_USER) - self.assertTrue(os.path.exists(avatar_disk_path(profile))) - - def test_patch_bot_to_stream(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_sending_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_sending_stream = ujson.loads(result.content)['default_sending_stream'] - self.assertEqual('Denmark', default_sending_stream) - - bot = self.get_bot() - self.assertEqual('Denmark', bot['default_sending_stream']) - - def test_patch_bot_to_stream_not_subscribed(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_sending_stream': 'Rome', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_sending_stream = ujson.loads(result.content)['default_sending_stream'] - self.assertEqual('Rome', default_sending_stream) - - bot = self.get_bot() - self.assertEqual('Rome', bot['default_sending_stream']) - - def test_patch_bot_to_stream_none(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_sending_stream': '', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_sending_stream = get_user_profile_by_email( - "hambot-bot@zulip.com").default_sending_stream - self.assertEqual(None, default_sending_stream) - - bot = self.get_bot() - self.assertEqual(None, bot['default_sending_stream']) - - def test_patch_bot_to_stream_private_allowed(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = self.subscribe_to_stream(user_profile.email, "Denmark") - do_change_stream_invite_only(stream, True) - - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - - bot_info = { - 'default_sending_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_sending_stream = ujson.loads(result.content)['default_sending_stream'] - self.assertEqual('Denmark', default_sending_stream) - - bot = self.get_bot() - self.assertEqual('Denmark', bot['default_sending_stream']) - - def test_patch_bot_to_stream_private_denied(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = get_stream("Denmark", user_profile.realm) - self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") - do_change_stream_invite_only(stream, True) - - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - - bot_info = { - 'default_sending_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_error(result, "Invalid stream name 'Denmark'") - - def test_patch_bot_to_stream_not_found(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_sending_stream': 'missing', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_error(result, "Invalid stream name 'missing'") - - def test_patch_bot_events_register_stream(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_events_register_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_events_register_stream = ujson.loads(result.content)['default_events_register_stream'] - self.assertEqual('Denmark', default_events_register_stream) - - bot = self.get_bot() - self.assertEqual('Denmark', bot['default_events_register_stream']) - - def test_patch_bot_events_register_stream_allowed(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = self.subscribe_to_stream(user_profile.email, "Denmark") - do_change_stream_invite_only(stream, True) - - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_events_register_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_events_register_stream = ujson.loads(result.content)['default_events_register_stream'] - self.assertEqual('Denmark', default_events_register_stream) - - bot = self.get_bot() - self.assertEqual('Denmark', bot['default_events_register_stream']) - - def test_patch_bot_events_register_stream_denied(self): - # type: () -> None - self.login("hamlet@zulip.com") - user_profile = get_user_profile_by_email("hamlet@zulip.com") - stream = get_stream("Denmark", user_profile.realm) - self.unsubscribe_from_stream("hamlet@zulip.com", "Denmark") - do_change_stream_invite_only(stream, True) - - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_events_register_stream': 'Denmark', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_error(result, "Invalid stream name 'Denmark'") - - def test_patch_bot_events_register_stream_none(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_events_register_stream': '', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_events_register_stream = get_user_profile_by_email( - "hambot-bot@zulip.com").default_events_register_stream - self.assertEqual(None, default_events_register_stream) - - bot = self.get_bot() - self.assertEqual(None, bot['default_events_register_stream']) - - def test_patch_bot_events_register_stream_not_found(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_events_register_stream': 'missing', - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_error(result, "Invalid stream name 'missing'") - - def test_patch_bot_default_all_public_streams_true(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_all_public_streams': ujson.dumps(True), - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_events_register_stream = ujson.loads(result.content)['default_all_public_streams'] - self.assertEqual(default_events_register_stream, True) - - bot = self.get_bot() - self.assertEqual(bot['default_all_public_streams'], True) - - def test_patch_bot_default_all_public_streams_false(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'default_all_public_streams': ujson.dumps(False), - } - result = self.client_patch("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - default_events_register_stream = ujson.loads(result.content)['default_all_public_streams'] - self.assertEqual(default_events_register_stream, False) - - bot = self.get_bot() - self.assertEqual(bot['default_all_public_streams'], False) - - def test_patch_bot_via_post(self): - # type: () -> None - self.login("hamlet@zulip.com") - bot_info = { - 'full_name': 'The Bot of Hamlet', - 'short_name': 'hambot', - } - result = self.client_post("/json/bots", bot_info) - self.assert_json_success(result) - bot_info = { - 'full_name': 'Fred', - 'method': 'PATCH' - } - result = self.client_post("/json/bots/hambot-bot@zulip.com", bot_info) - self.assert_json_success(result) - - full_name = ujson.loads(result.content)['full_name'] - self.assertEqual('Fred', full_name) - - bot = self.get_bot() - self.assertEqual('Fred', bot['full_name']) - - def test_patch_bogus_bot(self): - # type: () -> None - """Deleting a bogus bot will succeed silently.""" - self.login("hamlet@zulip.com") - self.create_bot() - bot_info = { - 'full_name': 'Fred', - } - result = self.client_patch("/json/bots/nonexistent-bot@zulip.com", bot_info) - self.assert_json_error(result, 'No such user') - self.assert_num_bots_equal(1) - class ChangeSettingsTest(ZulipTestCase): def check_well_formed_change_settings_response(self, result):