2017-05-25 19:48:36 +02:00
import mock
from typing import Any , Union , Mapping , Callable
2017-10-31 11:20:18 +01:00
from django . conf import settings
from django . test import override_settings
2017-09-26 23:55:15 +02:00
from zerver . lib . actions import (
do_create_user ,
get_service_bot_events ,
)
2019-01-07 16:20:21 +01:00
from zerver . lib . bot_lib import StateHandler , EmbeddedBotHandler , \
EmbeddedBotEmptyRecipientsList
2017-10-31 11:10:56 +01:00
from zerver . lib . bot_storage import StateError
2018-01-07 19:21:04 +01:00
from zerver . lib . bot_config import set_bot_config , ConfigError , load_bot_config_template
2017-05-25 19:48:36 +02:00
from zerver . lib . test_classes import ZulipTestCase
from zerver . models import (
2017-08-25 05:20:26 +02:00
get_realm ,
2017-05-25 19:48:36 +02:00
UserProfile ,
Recipient ,
)
2017-11-20 14:40:51 +01:00
import ujson
2017-05-25 20:01:31 +02:00
BOT_TYPE_TO_QUEUE_NAME = {
UserProfile . OUTGOING_WEBHOOK_BOT : ' outgoing_webhooks ' ,
UserProfile . EMBEDDED_BOT : ' embedded_bots ' ,
}
2017-09-26 23:55:15 +02:00
class TestServiceBotBasics ( ZulipTestCase ) :
2017-11-05 10:51:25 +01:00
def _get_outgoing_bot ( self ) - > UserProfile :
2017-09-26 23:55:15 +02:00
outgoing_bot = do_create_user (
email = " bar-bot@zulip.com " ,
password = " test " ,
realm = get_realm ( " zulip " ) ,
full_name = " BarBot " ,
short_name = ' bb ' ,
bot_type = UserProfile . OUTGOING_WEBHOOK_BOT ,
bot_owner = self . example_user ( ' cordelia ' ) ,
)
return outgoing_bot
2017-11-05 10:51:25 +01:00
def test_service_events_for_pms ( self ) - > None :
2017-09-26 23:55:15 +02:00
sender = self . example_user ( ' hamlet ' )
assert ( not sender . is_bot )
outgoing_bot = self . _get_outgoing_bot ( )
event_dict = get_service_bot_events (
sender = sender ,
service_bot_tuples = [
( outgoing_bot . id , outgoing_bot . bot_type ) ,
] ,
2017-10-24 19:25:50 +02:00
active_user_ids = { outgoing_bot . id } ,
2017-09-26 23:55:15 +02:00
mentioned_user_ids = set ( ) ,
recipient_type = Recipient . PERSONAL ,
)
expected = dict (
outgoing_webhooks = [
dict ( trigger = ' private_message ' , user_profile_id = outgoing_bot . id ) ,
] ,
)
self . assertEqual ( event_dict , expected )
2018-08-13 16:21:10 +02:00
def test_spurious_mentions ( self ) - > None :
sender = self . example_user ( ' hamlet ' )
assert ( not sender . is_bot )
outgoing_bot = self . _get_outgoing_bot ( )
# If outgoing_bot is not in mentioned_user_ids,
# we will skip over it. This tests an anomaly
# of the code that our query for bots can include
# bots that may not actually be mentioned, and it's
# easiest to just filter them in get_service_bot_events.
event_dict = get_service_bot_events (
sender = sender ,
service_bot_tuples = [
( outgoing_bot . id , outgoing_bot . bot_type ) ,
] ,
active_user_ids = { outgoing_bot . id } ,
mentioned_user_ids = set ( ) ,
recipient_type = Recipient . STREAM ,
)
self . assertEqual ( len ( event_dict ) , 0 )
2017-11-05 10:51:25 +01:00
def test_service_events_for_stream_mentions ( self ) - > None :
2017-09-26 23:55:15 +02:00
sender = self . example_user ( ' hamlet ' )
assert ( not sender . is_bot )
outgoing_bot = self . _get_outgoing_bot ( )
2018-08-13 16:21:10 +02:00
cordelia = self . example_user ( ' cordelia ' )
red_herring_bot = self . create_test_bot (
short_name = ' whatever ' ,
user_profile = cordelia ,
)
2017-09-26 23:55:15 +02:00
event_dict = get_service_bot_events (
sender = sender ,
service_bot_tuples = [
( outgoing_bot . id , outgoing_bot . bot_type ) ,
2018-08-13 16:21:10 +02:00
( red_herring_bot . id , UserProfile . OUTGOING_WEBHOOK_BOT ) ,
2017-09-26 23:55:15 +02:00
] ,
2017-10-24 19:25:50 +02:00
active_user_ids = set ( ) ,
2017-09-26 23:55:15 +02:00
mentioned_user_ids = { outgoing_bot . id } ,
recipient_type = Recipient . STREAM ,
)
2017-09-27 14:01:12 +02:00
expected = dict (
outgoing_webhooks = [
dict ( trigger = ' mention ' , user_profile_id = outgoing_bot . id ) ,
] ,
)
self . assertEqual ( event_dict , expected )
2018-06-04 07:24:21 +02:00
def test_service_events_for_private_mentions ( self ) - > None :
""" Service bots should not get access to mentions if they aren ' t a
direct recipient . """
sender = self . example_user ( ' hamlet ' )
assert ( not sender . is_bot )
outgoing_bot = self . _get_outgoing_bot ( )
event_dict = get_service_bot_events (
sender = sender ,
service_bot_tuples = [
( outgoing_bot . id , outgoing_bot . bot_type ) ,
] ,
active_user_ids = set ( ) ,
mentioned_user_ids = { outgoing_bot . id } ,
recipient_type = Recipient . PERSONAL ,
)
self . assertEqual ( len ( event_dict ) , 0 )
2018-08-13 14:54:45 +02:00
def test_service_events_with_unexpected_bot_type ( self ) - > None :
hamlet = self . example_user ( ' hamlet ' )
cordelia = self . example_user ( ' cordelia ' )
bot = self . create_test_bot (
short_name = ' whatever ' ,
user_profile = cordelia ,
)
wrong_bot_type = UserProfile . INCOMING_WEBHOOK_BOT
bot . bot_type = wrong_bot_type
bot . save ( )
with mock . patch ( ' logging.error ' ) as log_mock :
event_dict = get_service_bot_events (
sender = hamlet ,
service_bot_tuples = [
( bot . id , wrong_bot_type ) ,
] ,
active_user_ids = set ( ) ,
mentioned_user_ids = { bot . id } ,
recipient_type = Recipient . PERSONAL ,
)
self . assertEqual ( len ( event_dict ) , 0 )
arg = log_mock . call_args_list [ 0 ] [ 0 ] [ 0 ]
self . assertIn ( ' Unexpected bot_type ' , arg )
2017-10-16 18:38:46 +02:00
class TestServiceBotStateHandler ( ZulipTestCase ) :
2017-11-05 10:51:25 +01:00
def setUp ( self ) - > None :
2019-10-19 20:47:00 +02:00
super ( ) . setUp ( )
2017-10-16 18:38:46 +02:00
self . user_profile = self . example_user ( " othello " )
self . bot_profile = do_create_user ( email = " embedded-bot-1@zulip.com " ,
password = " test " ,
realm = get_realm ( " zulip " ) ,
full_name = " EmbeddedBo1 " ,
short_name = " embedded-bot-1 " ,
bot_type = UserProfile . EMBEDDED_BOT ,
bot_owner = self . user_profile )
self . second_bot_profile = do_create_user ( email = " embedded-bot-2@zulip.com " ,
password = " test " ,
realm = get_realm ( " zulip " ) ,
full_name = " EmbeddedBot2 " ,
short_name = " embedded-bot-2 " ,
bot_type = UserProfile . EMBEDDED_BOT ,
bot_owner = self . user_profile )
2017-11-05 10:51:25 +01:00
def test_basic_storage_and_retrieval ( self ) - > None :
2017-10-20 17:42:57 +02:00
storage = StateHandler ( self . bot_profile )
2017-10-20 18:47:06 +02:00
storage . put ( ' some key ' , ' some value ' )
storage . put ( ' some other key ' , ' some other value ' )
self . assertEqual ( storage . get ( ' some key ' ) , ' some value ' )
self . assertEqual ( storage . get ( ' some other key ' ) , ' some other value ' )
self . assertTrue ( storage . contains ( ' some key ' ) )
self . assertFalse ( storage . contains ( ' nonexistent key ' ) )
2017-10-30 16:21:01 +01:00
self . assertRaisesMessage ( StateError ,
2017-11-20 14:49:25 +01:00
" Key does not exist. " ,
2017-10-30 16:21:01 +01:00
lambda : storage . get ( ' nonexistent key ' ) )
2017-10-30 22:10:07 +01:00
storage . put ( ' some key ' , ' a new value ' )
self . assertEqual ( storage . get ( ' some key ' ) , ' a new value ' )
2017-10-20 17:42:57 +02:00
second_storage = StateHandler ( self . second_bot_profile )
2017-10-30 16:21:01 +01:00
self . assertRaises ( StateError , lambda : second_storage . get ( ' some key ' ) )
2017-10-20 18:47:06 +02:00
second_storage . put ( ' some key ' , ' yet another value ' )
2017-10-30 22:10:07 +01:00
self . assertEqual ( storage . get ( ' some key ' ) , ' a new value ' )
2017-10-20 18:47:06 +02:00
self . assertEqual ( second_storage . get ( ' some key ' ) , ' yet another value ' )
2017-10-16 18:38:46 +02:00
2017-11-05 10:51:25 +01:00
def test_marshaling ( self ) - > None :
2017-10-20 17:42:57 +02:00
storage = StateHandler ( self . bot_profile )
2017-10-20 17:24:09 +02:00
serializable_obj = { ' foo ' : ' bar ' , ' baz ' : [ 42 , ' cux ' ] }
2020-04-22 04:13:37 +02:00
storage . put ( ' some key ' , serializable_obj ) # type: ignore[arg-type] # Ignore for testing.
2017-10-20 18:47:06 +02:00
self . assertEqual ( storage . get ( ' some key ' ) , serializable_obj )
2017-10-20 17:24:09 +02:00
2017-11-05 10:51:25 +01:00
def test_invalid_calls ( self ) - > None :
2017-10-20 17:42:57 +02:00
storage = StateHandler ( self . bot_profile )
storage . marshal = lambda obj : obj
storage . demarshal = lambda obj : obj
2017-10-20 17:24:09 +02:00
serializable_obj = { ' foo ' : ' bar ' , ' baz ' : [ 42 , ' cux ' ] }
2017-11-20 14:49:25 +01:00
with self . assertRaisesMessage ( StateError , " Value type is <class ' dict ' >, but should be str. " ) :
2020-04-22 04:13:37 +02:00
storage . put ( ' some key ' , serializable_obj ) # type: ignore[arg-type] # We intend to test an invalid type.
2017-11-20 14:49:25 +01:00
with self . assertRaisesMessage ( StateError , " Key type is <class ' dict ' >, but should be str. " ) :
2020-04-22 04:13:37 +02:00
storage . put ( serializable_obj , ' some value ' ) # type: ignore[arg-type] # We intend to test an invalid type.
2017-10-20 17:24:09 +02:00
2017-11-24 10:18:29 +01:00
# Reduce maximal storage size for faster test string construction.
2017-10-31 11:20:18 +01:00
@override_settings ( USER_STATE_SIZE_LIMIT = 100 )
2017-11-05 10:51:25 +01:00
def test_storage_limit ( self ) - > None :
2017-10-20 17:42:57 +02:00
storage = StateHandler ( self . bot_profile )
2017-10-31 11:20:18 +01:00
# Disable marshaling for storing a string whose size is
# equivalent to the size of the stored object.
2017-10-31 11:03:05 +01:00
storage . marshal = lambda obj : obj
storage . demarshal = lambda obj : obj
2017-10-31 11:20:18 +01:00
2017-10-16 18:38:46 +02:00
key = ' capacity-filling entry '
2017-10-31 11:20:18 +01:00
storage . put ( key , ' x ' * ( settings . USER_STATE_SIZE_LIMIT - len ( key ) ) )
2017-10-16 18:38:46 +02:00
2017-11-20 14:49:25 +01:00
with self . assertRaisesMessage ( StateError , " Request exceeds storage limit by 32 characters. "
" The limit is 100 characters. " ) :
2017-10-20 18:47:06 +02:00
storage . put ( ' too much data ' , ' a few bits too long ' )
2017-10-16 18:38:46 +02:00
2017-10-20 17:42:57 +02:00
second_storage = StateHandler ( self . second_bot_profile )
2017-10-31 11:20:18 +01:00
second_storage . put ( ' another big entry ' , ' x ' * ( settings . USER_STATE_SIZE_LIMIT - 40 ) )
2017-10-20 18:47:06 +02:00
second_storage . put ( ' normal entry ' , ' abcd ' )
2017-10-16 18:38:46 +02:00
2017-11-05 10:51:25 +01:00
def test_entry_removal ( self ) - > None :
2017-10-26 16:02:35 +02:00
storage = StateHandler ( self . bot_profile )
storage . put ( ' some key ' , ' some value ' )
storage . put ( ' another key ' , ' some value ' )
self . assertTrue ( storage . contains ( ' some key ' ) )
self . assertTrue ( storage . contains ( ' another key ' ) )
storage . remove ( ' some key ' )
self . assertFalse ( storage . contains ( ' some key ' ) )
self . assertTrue ( storage . contains ( ' another key ' ) )
2017-11-02 10:27:28 +01:00
self . assertRaises ( StateError , lambda : storage . remove ( ' some key ' ) )
2017-10-26 16:02:35 +02:00
python: Convert function type annotations to Python 3 style.
Generated by com2ann (slightly patched to avoid also converting
assignment type annotations, which require Python 3.6), followed by
some manual whitespace adjustment, and six fixes for runtime issues:
- def __init__(self, token: Token, parent: Optional[Node]) -> None:
+ def __init__(self, token: Token, parent: "Optional[Node]") -> None:
-def main(options: argparse.Namespace) -> NoReturn:
+def main(options: argparse.Namespace) -> "NoReturn":
-def fetch_request(url: str, callback: Any, **kwargs: Any) -> Generator[Callable[..., Any], Any, None]:
+def fetch_request(url: str, callback: Any, **kwargs: Any) -> "Generator[Callable[..., Any], Any, None]":
-def assert_server_running(server: subprocess.Popen[bytes], log_file: Optional[str]) -> None:
+def assert_server_running(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> None:
-def server_is_up(server: subprocess.Popen[bytes], log_file: Optional[str]) -> bool:
+def server_is_up(server: "subprocess.Popen[bytes]", log_file: Optional[str]) -> bool:
- method_kwarg_pairs: List[FuncKwargPair],
+ method_kwarg_pairs: "List[FuncKwargPair]",
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-19 03:48:37 +02:00
def test_internal_endpoint ( self ) - > None :
2020-03-06 18:40:46 +01:00
self . login_user ( self . user_profile )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Store some data.
initial_dict = { ' key 1 ' : ' value 1 ' , ' key 2 ' : ' value 2 ' , ' key 3 ' : ' value 3 ' }
params = {
2017-11-24 10:18:29 +01:00
' storage ' : ujson . dumps ( initial_dict )
2017-11-20 14:40:51 +01:00
}
2017-11-24 10:18:29 +01:00
result = self . client_put ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert the stored data for some keys.
params = {
' keys ' : ujson . dumps ( [ ' key 1 ' , ' key 3 ' ] )
}
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , { ' key 3 ' : ' value 3 ' , ' key 1 ' : ' value 1 ' } )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert the stored data for all keys.
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , initial_dict )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Store some more data; update an entry and store a new entry
dict_update = { ' key 1 ' : ' new value ' , ' key 4 ' : ' value 4 ' }
params = {
2017-11-24 10:18:29 +01:00
' storage ' : ujson . dumps ( dict_update )
2017-11-20 14:40:51 +01:00
}
2017-11-24 10:18:29 +01:00
result = self . client_put ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert the data was updated.
updated_dict = initial_dict . copy ( )
updated_dict . update ( dict_update )
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , updated_dict )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert errors on invalid requests.
2019-11-13 03:24:14 +01:00
params = {
2020-04-22 04:13:37 +02:00
' keys ' : [ " This is a list, but should be a serialized string. " ] # type: ignore[dict-item] # Ignore 'incompatible type "str": "List[str]"; expected "str": "str"' for testing
2017-11-20 14:40:51 +01:00
}
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_error ( result , ' Argument " keys " is not valid JSON. ' )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
params = {
' keys ' : ujson . dumps ( [ " key 1 " , " nonexistent key " ] )
}
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_error ( result , " Key does not exist. " )
2017-11-24 11:26:12 +01:00
2017-11-25 19:05:12 +01:00
params = {
2017-11-24 10:18:29 +01:00
' storage ' : ujson . dumps ( { ' foo ' : [ 1 , 2 , 3 ] } )
2017-11-20 14:40:51 +01:00
}
2017-11-24 10:18:29 +01:00
result = self . client_put ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_error ( result , " Value type is <class ' list ' >, but should be str. " )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Remove some entries.
keys_to_remove = [ ' key 1 ' , ' key 2 ' ]
params = {
' keys ' : ujson . dumps ( keys_to_remove )
}
2017-11-24 10:18:29 +01:00
result = self . client_delete ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert the entries were removed.
for key in keys_to_remove :
updated_dict . pop ( key )
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , updated_dict )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Try to remove an existing and a nonexistent key.
params = {
' keys ' : ujson . dumps ( [ ' key 3 ' , ' nonexistent key ' ] )
}
2017-11-24 10:18:29 +01:00
result = self . client_delete ( ' /json/bot_storage ' , params )
2017-11-20 14:40:51 +01:00
self . assert_json_error ( result , " Key does not exist. " )
2017-11-24 11:26:12 +01:00
2017-11-20 14:40:51 +01:00
# Assert an error has been thrown and no entries were removed.
2017-11-24 10:18:29 +01:00
result = self . client_get ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , updated_dict )
2017-11-24 11:26:12 +01:00
2017-11-24 10:18:29 +01:00
# Remove the entire storage.
result = self . client_delete ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 11:26:12 +01:00
2017-11-24 10:18:29 +01:00
# Assert the entire storage has been removed.
result = self . client_get ( ' /json/bot_storage ' )
2017-11-20 14:40:51 +01:00
self . assert_json_success ( result )
2017-11-24 10:18:29 +01:00
self . assertEqual ( result . json ( ) [ ' storage ' ] , { } )
2017-11-20 14:40:51 +01:00
2017-11-01 20:51:12 +01:00
class TestServiceBotConfigHandler ( ZulipTestCase ) :
2017-11-05 10:51:25 +01:00
def setUp ( self ) - > None :
2019-10-19 20:47:00 +02:00
super ( ) . setUp ( )
2017-11-01 20:51:12 +01:00
self . user_profile = self . example_user ( " othello " )
2018-01-30 17:05:14 +01:00
self . bot_profile = self . create_test_bot ( ' embedded ' , self . user_profile ,
full_name = ' Embedded bot ' ,
bot_type = UserProfile . EMBEDDED_BOT ,
service_name = ' helloworld ' )
2017-11-01 20:51:12 +01:00
self . bot_handler = EmbeddedBotHandler ( self . bot_profile )
2017-11-05 10:51:25 +01:00
def test_basic_storage_and_retrieval ( self ) - > None :
2018-01-07 19:14:21 +01:00
with self . assertRaises ( ConfigError ) :
self . bot_handler . get_config_info ( ' foo ' )
2018-01-07 19:17:25 +01:00
self . assertEqual ( self . bot_handler . get_config_info ( ' foo ' , optional = True ) , dict ( ) )
2017-11-01 20:51:12 +01:00
config_dict = { " entry 1 " : " value 1 " , " entry 2 " : " value 2 " }
for key , value in config_dict . items ( ) :
set_bot_config ( self . bot_profile , key , value )
2018-01-07 19:17:25 +01:00
self . assertEqual ( self . bot_handler . get_config_info ( ' foo ' ) , config_dict )
2017-11-01 20:51:12 +01:00
config_update = { " entry 2 " : " new value " , " entry 3 " : " value 3 " }
for key , value in config_update . items ( ) :
set_bot_config ( self . bot_profile , key , value )
config_dict . update ( config_update )
2018-01-07 19:17:25 +01:00
self . assertEqual ( self . bot_handler . get_config_info ( ' foo ' ) , config_dict )
2017-11-01 20:51:12 +01:00
@override_settings ( BOT_CONFIG_SIZE_LIMIT = 100 )
2017-11-05 10:51:25 +01:00
def test_config_entry_limit ( self ) - > None :
2017-11-01 20:51:12 +01:00
set_bot_config ( self . bot_profile , " some key " , ' x ' * ( settings . BOT_CONFIG_SIZE_LIMIT - 8 ) )
self . assertRaisesMessage ( ConfigError ,
" Cannot store configuration. Request would require 101 characters. "
" The current configuration size limit is 100 characters. " ,
lambda : set_bot_config ( self . bot_profile , " some key " , ' x ' * ( settings . BOT_CONFIG_SIZE_LIMIT - 8 + 1 ) ) )
set_bot_config ( self . bot_profile , " some key " , ' x ' * ( settings . BOT_CONFIG_SIZE_LIMIT - 20 ) )
set_bot_config ( self . bot_profile , " another key " , ' x ' )
self . assertRaisesMessage ( ConfigError ,
" Cannot store configuration. Request would require 116 characters. "
" The current configuration size limit is 100 characters. " ,
lambda : set_bot_config ( self . bot_profile , " yet another key " , ' x ' ) )
2018-01-07 19:21:04 +01:00
def test_load_bot_config_template ( self ) - > None :
bot_config = load_bot_config_template ( ' giphy ' )
self . assertTrue ( isinstance ( bot_config , dict ) )
self . assertEqual ( len ( bot_config ) , 1 )
def test_load_bot_config_template_for_bot_without_config_data ( self ) - > None :
bot_config = load_bot_config_template ( ' converter ' )
self . assertTrue ( isinstance ( bot_config , dict ) )
self . assertEqual ( len ( bot_config ) , 0 )
2019-01-07 16:20:21 +01:00
def test_bot_send_pm_with_empty_recipients_list ( self ) - > None :
with self . assertRaisesRegex ( EmbeddedBotEmptyRecipientsList , ' Message must have recipients! ' ) :
self . bot_handler . send_message ( message = { ' type ' : ' private ' , ' to ' : [ ] } )
2018-01-07 19:21:04 +01:00
2017-05-25 19:54:06 +02:00
class TestServiceBotEventTriggers ( ZulipTestCase ) :
2017-05-25 19:48:36 +02:00
2017-11-05 10:51:25 +01:00
def setUp ( self ) - > None :
2019-10-19 20:47:00 +02:00
super ( ) . setUp ( )
2017-05-25 19:54:06 +02:00
self . user_profile = self . example_user ( " othello " )
self . bot_profile = do_create_user ( email = " foo-bot@zulip.com " ,
password = " test " ,
2017-08-25 05:20:26 +02:00
realm = get_realm ( " zulip " ) ,
2017-05-25 19:54:06 +02:00
full_name = " FooBot " ,
short_name = " foo-bot " ,
bot_type = UserProfile . OUTGOING_WEBHOOK_BOT ,
bot_owner = self . user_profile )
2017-05-25 20:01:31 +02:00
self . second_bot_profile = do_create_user ( email = " bar-bot@zulip.com " ,
password = " test " ,
2017-08-25 05:20:26 +02:00
realm = get_realm ( " zulip " ) ,
2017-05-25 20:01:31 +02:00
full_name = " BarBot " ,
short_name = " bar-bot " ,
bot_type = UserProfile . OUTGOING_WEBHOOK_BOT ,
bot_owner = self . user_profile )
2017-05-25 19:54:06 +02:00
2017-05-25 20:01:31 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_trigger_on_stream_mention_from_user ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:01:31 +02:00
for bot_type , expected_queue_name in BOT_TYPE_TO_QUEUE_NAME . items ( ) :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
2020-04-09 21:51:58 +02:00
content = ' @**FooBot** foo bar!!! '
2017-05-25 20:01:31 +02:00
recipient = ' Denmark '
trigger = ' mention '
message_type = Recipient . _type_names [ Recipient . STREAM ]
2017-11-05 10:51:25 +01:00
def check_values_passed ( queue_name : Any ,
trigger_event : Union [ Mapping [ Any , Any ] , Any ] ,
2017-11-24 13:18:46 +01:00
x : Callable [ [ Any ] , None ] = None ) - > None :
2017-05-25 20:01:31 +02:00
self . assertEqual ( queue_name , expected_queue_name )
self . assertEqual ( trigger_event [ " message " ] [ " content " ] , content )
self . assertEqual ( trigger_event [ " message " ] [ " display_recipient " ] , recipient )
self . assertEqual ( trigger_event [ " message " ] [ " sender_email " ] , self . user_profile . email )
self . assertEqual ( trigger_event [ " message " ] [ " type " ] , message_type )
self . assertEqual ( trigger_event [ ' trigger ' ] , trigger )
self . assertEqual ( trigger_event [ ' user_profile_id ' ] , self . bot_profile . id )
mock_queue_json_publish . side_effect = check_values_passed
2017-10-28 18:06:36 +02:00
self . send_stream_message (
2020-03-07 11:43:05 +01:00
self . user_profile ,
2017-05-25 20:01:31 +02:00
' Denmark ' ,
content )
self . assertTrue ( mock_queue_json_publish . called )
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_no_trigger_on_stream_message_without_mention ( self , mock_queue_json_publish : mock . Mock ) - > None :
2020-03-07 11:43:05 +01:00
sender = self . user_profile
self . send_stream_message ( sender , " Denmark " )
2017-05-25 20:01:31 +02:00
self . assertFalse ( mock_queue_json_publish . called )
2017-05-25 19:48:36 +02:00
2017-05-25 20:01:31 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_no_trigger_on_stream_mention_from_bot ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:01:31 +02:00
for bot_type in BOT_TYPE_TO_QUEUE_NAME :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
2017-05-25 19:48:36 +02:00
2017-10-28 18:06:36 +02:00
self . send_stream_message (
2020-03-07 11:43:05 +01:00
self . second_bot_profile ,
2017-05-25 20:01:31 +02:00
' Denmark ' ,
2020-04-09 21:51:58 +02:00
' @**FooBot** foo bar!!! ' )
2017-05-25 20:01:31 +02:00
self . assertFalse ( mock_queue_json_publish . called )
2017-05-25 19:48:36 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_trigger_on_personal_message_from_user ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:01:31 +02:00
for bot_type , expected_queue_name in BOT_TYPE_TO_QUEUE_NAME . items ( ) :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
2020-03-07 11:43:05 +01:00
sender = self . user_profile
recipient = self . bot_profile
2017-05-25 20:01:31 +02:00
2017-11-05 10:51:25 +01:00
def check_values_passed ( queue_name : Any ,
trigger_event : Union [ Mapping [ Any , Any ] , Any ] ,
2017-11-24 13:18:46 +01:00
x : Callable [ [ Any ] , None ] = None ) - > None :
2017-05-25 20:01:31 +02:00
self . assertEqual ( queue_name , expected_queue_name )
self . assertEqual ( trigger_event [ " user_profile_id " ] , self . bot_profile . id )
self . assertEqual ( trigger_event [ " trigger " ] , " private_message " )
2020-03-07 11:43:05 +01:00
self . assertEqual ( trigger_event [ " message " ] [ " sender_email " ] , sender . email )
2017-05-25 20:01:31 +02:00
display_recipients = [
trigger_event [ " message " ] [ " display_recipient " ] [ 0 ] [ " email " ] ,
trigger_event [ " message " ] [ " display_recipient " ] [ 1 ] [ " email " ] ,
]
2020-03-07 11:43:05 +01:00
self . assertTrue ( sender . email in display_recipients )
self . assertTrue ( recipient . email in display_recipients )
2017-05-25 20:01:31 +02:00
mock_queue_json_publish . side_effect = check_values_passed
2020-03-07 11:43:05 +01:00
self . send_personal_message ( sender , recipient , ' test ' )
2017-05-25 20:01:31 +02:00
self . assertTrue ( mock_queue_json_publish . called )
2017-05-25 19:48:36 +02:00
2017-05-25 20:02:13 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_no_trigger_on_personal_message_from_bot ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:02:13 +02:00
for bot_type in BOT_TYPE_TO_QUEUE_NAME :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
2020-03-07 11:43:05 +01:00
sender = self . second_bot_profile
recipient = self . bot_profile
self . send_personal_message ( sender , recipient )
2017-05-25 20:02:13 +02:00
self . assertFalse ( mock_queue_json_publish . called )
2017-05-25 19:48:36 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_trigger_on_huddle_message_from_user ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:01:31 +02:00
for bot_type , expected_queue_name in BOT_TYPE_TO_QUEUE_NAME . items ( ) :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
self . second_bot_profile . bot_type = bot_type
self . second_bot_profile . save ( )
2020-03-07 11:43:05 +01:00
sender = self . user_profile
recipients = [ self . bot_profile , self . second_bot_profile ]
2017-05-25 20:01:31 +02:00
profile_ids = [ self . bot_profile . id , self . second_bot_profile . id ]
2017-11-05 10:51:25 +01:00
def check_values_passed ( queue_name : Any ,
trigger_event : Union [ Mapping [ Any , Any ] , Any ] ,
2017-11-24 13:18:46 +01:00
x : Callable [ [ Any ] , None ] = None ) - > None :
2017-05-25 20:01:31 +02:00
self . assertEqual ( queue_name , expected_queue_name )
self . assertIn ( trigger_event [ " user_profile_id " ] , profile_ids )
profile_ids . remove ( trigger_event [ " user_profile_id " ] )
self . assertEqual ( trigger_event [ " trigger " ] , " private_message " )
2020-03-07 11:43:05 +01:00
self . assertEqual ( trigger_event [ " message " ] [ " sender_email " ] , sender . email )
2020-04-09 21:51:58 +02:00
self . assertEqual ( trigger_event [ " message " ] [ " type " ] , ' private ' )
2017-05-25 20:01:31 +02:00
mock_queue_json_publish . side_effect = check_values_passed
2020-03-07 11:43:05 +01:00
self . send_huddle_message ( sender , recipients , ' test ' )
2017-05-25 20:01:31 +02:00
self . assertEqual ( mock_queue_json_publish . call_count , 2 )
mock_queue_json_publish . reset_mock ( )
2017-05-25 20:02:13 +02:00
@mock.patch ( ' zerver.lib.actions.queue_json_publish ' )
2017-11-05 10:51:25 +01:00
def test_no_trigger_on_huddle_message_from_bot ( self , mock_queue_json_publish : mock . Mock ) - > None :
2017-05-25 20:02:13 +02:00
for bot_type in BOT_TYPE_TO_QUEUE_NAME :
self . bot_profile . bot_type = bot_type
self . bot_profile . save ( )
2020-03-07 11:43:05 +01:00
sender = self . second_bot_profile
recipients = [ self . user_profile , self . bot_profile ]
self . send_huddle_message ( sender , recipients )
2017-05-25 20:02:13 +02:00
self . assertFalse ( mock_queue_json_publish . called )