2016-04-24 17:08:51 +02:00
# -*- coding: utf-8 -*-
2016-12-08 09:39:48 +01:00
2016-07-17 04:07:07 +02:00
from django . db import connection
2016-04-24 17:08:51 +02:00
from django . test import override_settings
2016-06-21 21:05:44 +02:00
from sqlalchemy . sql import (
2017-02-22 22:13:57 +01:00
and_ , select , column , table ,
2016-06-21 21:05:44 +02:00
)
2017-08-25 20:01:20 +02:00
from sqlalchemy . sql import compiler
2016-06-21 21:05:44 +02:00
from zerver . models import (
2017-10-28 22:08:58 +02:00
Realm , Stream , Subscription , UserProfile , Attachment ,
2017-10-28 21:31:21 +02:00
get_display_recipient , get_personal_recipient , get_realm , get_stream , get_user ,
2017-10-28 20:26:11 +02:00
Reaction , UserMessage , get_stream_recipient ,
2016-06-21 21:05:44 +02:00
)
2016-10-04 15:52:26 +02:00
from zerver . lib . message import (
2017-10-20 21:34:05 +02:00
MessageDict ,
2016-10-04 15:52:26 +02:00
)
2016-07-16 22:56:33 +02:00
from zerver . lib . narrow import (
build_narrow_filter ,
)
2017-03-05 08:56:57 +01:00
from zerver . lib . request import JsonableError
2016-12-08 09:39:48 +01:00
from zerver . lib . str_utils import force_bytes
2016-07-19 08:12:35 +02:00
from zerver . lib . sqlalchemy_utils import get_sqlalchemy_connection
2016-06-21 21:05:44 +02:00
from zerver . lib . test_helpers import (
2016-11-10 19:30:09 +01:00
POSTRequestMock ,
2016-07-16 22:56:33 +02:00
TestCase ,
2017-03-19 04:40:28 +01:00
get_user_messages , queries_captured ,
2016-06-21 21:05:44 +02:00
)
2016-11-10 19:30:09 +01:00
from zerver . lib . test_classes import (
ZulipTestCase ,
)
2017-08-24 17:58:40 +02:00
from zerver . lib . topic_mutes import (
set_topic_mutes ,
)
2016-06-21 21:05:44 +02:00
from zerver . views . messages import (
2016-07-19 08:12:35 +02:00
exclude_muting_conditions ,
2017-03-24 07:51:46 +01:00
get_messages_backend , ok_to_include_history ,
2017-02-23 05:50:15 +01:00
NarrowBuilder , BadNarrowOperator , Query ,
LARGER_THAN_MAX_MESSAGE_ID ,
2016-06-21 21:05:44 +02:00
)
2017-08-16 15:20:11 +02:00
from typing import Dict , List , Mapping , Sequence , Tuple , Generic , Union , Any , Optional , Text
2017-10-28 01:03:52 +02:00
import mock
2016-07-16 22:56:33 +02:00
import os
2016-06-21 21:05:44 +02:00
import re
import ujson
def get_sqlalchemy_query_params ( query ) :
2016-12-08 09:39:48 +01:00
# type: (Text) -> Dict[Text, Text]
2017-08-25 20:01:20 +02:00
dialect = get_sqlalchemy_connection ( ) . dialect
2016-06-21 21:05:44 +02:00
comp = compiler . SQLCompiler ( dialect , query )
return comp . params
def fix_ws ( s ) :
2016-12-08 09:39:48 +01:00
# type: (Text) -> Text
2016-06-21 21:05:44 +02:00
return re . sub ( ' \ s+ ' , ' ' , str ( s ) ) . strip ( )
def get_recipient_id_for_stream_name ( realm , stream_name ) :
2016-12-08 09:39:48 +01:00
# type: (Realm, Text) -> Text
2016-06-21 21:05:44 +02:00
stream = get_stream ( stream_name , realm )
2017-10-28 20:26:11 +02:00
return get_stream_recipient ( stream . id ) . id
2016-06-21 21:05:44 +02:00
def mute_stream ( realm , user_profile , stream_name ) :
2016-12-08 09:39:48 +01:00
# type: (Realm, Text, Text) -> None
2017-01-13 15:50:17 +01:00
stream = get_stream ( stream_name , realm )
2017-10-28 22:08:58 +02:00
recipient = get_stream_recipient ( stream . id )
2016-06-21 21:05:44 +02:00
subscription = Subscription . objects . get ( recipient = recipient , user_profile = user_profile )
subscription . in_home_view = False
subscription . save ( )
2016-08-23 02:08:42 +02:00
class NarrowBuilderTest ( ZulipTestCase ) :
2016-06-21 21:05:44 +02:00
def setUp ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-01-04 05:30:48 +01:00
self . realm = get_realm ( ' zulip ' )
2017-05-07 17:21:26 +02:00
self . user_profile = self . example_user ( ' hamlet ' )
2016-06-21 21:05:44 +02:00
self . builder = NarrowBuilder ( self . user_profile , column ( ' id ' ) )
2017-02-22 22:13:57 +01:00
self . raw_query = select ( [ column ( " id " ) ] , None , table ( " zerver_message " ) )
2016-06-21 21:05:44 +02:00
def test_add_term_using_not_defined_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' not-defined ' , operand = ' any ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_stream_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' stream ' , operand = ' Scotland ' )
self . _do_add_term_test ( term , ' WHERE recipient_id = :recipient_id_1 ' )
def test_add_term_using_stream_operator_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' stream ' , operand = ' Scotland ' , negated = True )
self . _do_add_term_test ( term , ' WHERE recipient_id != :recipient_id_1 ' )
def test_add_term_using_stream_operator_and_non_existing_operand_should_raise_error ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' stream ' , operand = ' NonExistingStream ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_is_operator_and_private_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' is ' , operand = ' private ' )
self . _do_add_term_test ( term , ' WHERE type = :type_1 OR type = :type_2 ' )
def test_add_term_using_is_operator_private_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' is ' , operand = ' private ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT (type = :type_1 OR type = :type_2) ' )
def test_add_term_using_is_operator_and_non_private_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
for operand in [ ' starred ' , ' mentioned ' , ' alerted ' ] :
term = dict ( operator = ' is ' , operand = operand )
self . _do_add_term_test ( term , ' WHERE (flags & :flags_1) != :param_1 ' )
2017-06-19 03:21:48 +02:00
def test_add_term_using_is_operator_and_unread_operand ( self ) :
# type: () -> None
term = dict ( operator = ' is ' , operand = ' unread ' )
self . _do_add_term_test ( term , ' WHERE (flags & :flags_1) = :param_1 ' )
def test_add_term_using_is_operator_and_unread_operand_and_negated ( self ) : # NEGATED
# type: () -> None
term = dict ( operator = ' is ' , operand = ' unread ' , negated = True )
self . _do_add_term_test ( term , ' WHERE (flags & :flags_1) != :param_1 ' )
2016-06-21 21:05:44 +02:00
def test_add_term_using_is_operator_non_private_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-08-16 15:20:11 +02:00
term = dict ( operator = ' is ' , operand = ' starred ' , negated = True )
where_clause = ' WHERE (flags & :flags_1) = :param_1 '
params = dict (
flags_1 = UserMessage . flags . starred . mask ,
param_1 = 0
)
self . _do_add_term_test ( term , where_clause , params )
term = dict ( operator = ' is ' , operand = ' alerted ' , negated = True )
where_clause = ' WHERE (flags & :flags_1) = :param_1 '
params = dict (
flags_1 = UserMessage . flags . has_alert_word . mask ,
param_1 = 0
)
self . _do_add_term_test ( term , where_clause , params )
term = dict ( operator = ' is ' , operand = ' mentioned ' , negated = True )
where_clause = ' WHERE NOT ((flags & :flags_1) != :param_1 OR (flags & :flags_2) != :param_2) '
params = dict (
flags_1 = UserMessage . flags . mentioned . mask ,
param_1 = 0 ,
flags_2 = UserMessage . flags . wildcard_mentioned . mask ,
param_2 = 0
)
self . _do_add_term_test ( term , where_clause , params )
2016-06-21 21:05:44 +02:00
def test_add_term_using_non_supported_operator_should_raise_error ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' is ' , operand = ' non_supported ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_topic_operator_and_lunch_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' topic ' , operand = ' lunch ' )
self . _do_add_term_test ( term , ' WHERE upper(subject) = upper(:param_1) ' )
def test_add_term_using_topic_operator_lunch_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' topic ' , operand = ' lunch ' , negated = True )
self . _do_add_term_test ( term , ' WHERE upper(subject) != upper(:param_1) ' )
def test_add_term_using_topic_operator_and_personal_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' topic ' , operand = ' personal ' )
self . _do_add_term_test ( term , ' WHERE upper(subject) = upper(:param_1) ' )
def test_add_term_using_topic_operator_personal_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' topic ' , operand = ' personal ' , negated = True )
self . _do_add_term_test ( term , ' WHERE upper(subject) != upper(:param_1) ' )
def test_add_term_using_sender_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' sender ' , operand = self . example_email ( " othello " ) )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE sender_id = :param_1 ' )
def test_add_term_using_sender_operator_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' sender ' , operand = self . example_email ( " othello " ) , negated = True )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE sender_id != :param_1 ' )
def test_add_term_using_sender_operator_with_non_existing_user_as_operand ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' sender ' , operand = ' non-existing@zulip.com ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_pm_with_operator_and_not_the_same_user_as_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' pm-with ' , operand = self . example_email ( " othello " ) )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE sender_id = :sender_id_1 AND recipient_id = :recipient_id_1 OR sender_id = :sender_id_2 AND recipient_id = :recipient_id_2 ' )
def test_add_term_using_pm_with_operator_not_the_same_user_as_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' pm-with ' , operand = self . example_email ( " othello " ) , negated = True )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE NOT (sender_id = :sender_id_1 AND recipient_id = :recipient_id_1 OR sender_id = :sender_id_2 AND recipient_id = :recipient_id_2) ' )
def test_add_term_using_pm_with_operator_the_same_user_as_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:40:26 +02:00
term = dict ( operator = ' pm-with ' , operand = self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE sender_id = :sender_id_1 AND recipient_id = :recipient_id_1 ' )
def test_add_term_using_pm_with_operator_the_same_user_as_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:40:26 +02:00
term = dict ( operator = ' pm-with ' , operand = self . example_email ( " hamlet " ) , negated = True )
2016-06-21 21:05:44 +02:00
self . _do_add_term_test ( term , ' WHERE NOT (sender_id = :sender_id_1 AND recipient_id = :recipient_id_1) ' )
def test_add_term_using_pm_with_operator_and_more_than_user_as_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' pm-with ' , operand = ' hamlet@zulip.com, othello@zulip.com ' )
self . _do_add_term_test ( term , ' WHERE recipient_id = :recipient_id_1 ' )
def test_add_term_using_pm_with_operator_more_than_user_as_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' pm-with ' , operand = ' hamlet@zulip.com, othello@zulip.com ' , negated = True )
self . _do_add_term_test ( term , ' WHERE recipient_id != :recipient_id_1 ' )
def test_add_term_using_pm_with_operator_with_non_existing_user_as_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' pm-with ' , operand = ' non-existing@zulip.com ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_pm_with_operator_with_existing_and_non_existing_user_as_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' pm-with ' , operand = ' othello@zulip.com,non-existing@zulip.com ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_id_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' id ' , operand = 555 )
self . _do_add_term_test ( term , ' WHERE id = :param_1 ' )
def test_add_term_using_id_operator_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' id ' , operand = 555 , negated = True )
self . _do_add_term_test ( term , ' WHERE id != :param_1 ' )
2017-03-23 23:35:37 +01:00
def test_add_term_using_group_pm_operator_and_not_the_same_user_as_operand ( self ) :
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' group-pm-with ' , operand = self . example_email ( " othello " ) )
2017-10-28 01:03:52 +02:00
with mock . patch ( " sqlalchemy.util.warn " ) as mock_warn :
self . _do_add_term_test ( term , ' WHERE recipient_id != recipient_id ' )
# SQLalchemy warns because this query is a tautology.
mock_warn . assert_called_once ( )
2017-03-23 23:35:37 +01:00
def test_add_term_using_group_pm_operator_not_the_same_user_as_operand_and_negated ( self ) : # NEGATED
# type: () -> None
2017-05-25 02:08:35 +02:00
term = dict ( operator = ' group-pm-with ' , operand = self . example_email ( " othello " ) , negated = True )
2017-03-23 23:35:37 +01:00
self . _do_add_term_test ( term , ' WHERE recipient_id = recipient_id ' )
def test_add_term_using_group_pm_operator_with_non_existing_user_as_operand ( self ) :
# type: () -> None
term = dict ( operator = ' group-pm-with ' , operand = ' non-existing@zulip.com ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2016-06-21 21:05:44 +02:00
def test_add_term_using_search_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' search ' , operand = ' " french fries " ' )
self . _do_add_term_test ( term , ' WHERE (lower(content) LIKE lower(:content_1) OR lower(subject) LIKE lower(:subject_1)) AND (search_tsvector @@ plainto_tsquery(:param_2, :param_3)) ' )
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2016-06-21 21:05:44 +02:00
def test_add_term_using_search_operator_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' search ' , operand = ' " french fries " ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT (lower(content) LIKE lower(:content_1) OR lower(subject) LIKE lower(:subject_1)) AND NOT (search_tsvector @@ plainto_tsquery(:param_2, :param_3)) ' )
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = True )
def test_add_term_using_search_operator_pgroonga ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-04-24 17:08:51 +02:00
term = dict ( operator = ' search ' , operand = ' " french fries " ' )
self . _do_add_term_test ( term , ' WHERE search_pgroonga @@ :search_pgroonga_1 ' )
@override_settings ( USING_PGROONGA = True )
def test_add_term_using_search_operator_and_negated_pgroonga ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-04-24 17:08:51 +02:00
term = dict ( operator = ' search ' , operand = ' " french fries " ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT (search_pgroonga @@ :search_pgroonga_1) ' )
2016-06-21 21:05:44 +02:00
def test_add_term_using_has_operator_and_attachment_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' attachment ' )
self . _do_add_term_test ( term , ' WHERE has_attachment ' )
def test_add_term_using_has_operator_attachment_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' attachment ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT has_attachment ' )
def test_add_term_using_has_operator_and_image_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' image ' )
self . _do_add_term_test ( term , ' WHERE has_image ' )
def test_add_term_using_has_operator_image_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' image ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT has_image ' )
def test_add_term_using_has_operator_and_link_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' link ' )
self . _do_add_term_test ( term , ' WHERE has_link ' )
def test_add_term_using_has_operator_link_operand_and_negated ( self ) : # NEGATED
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' link ' , negated = True )
self . _do_add_term_test ( term , ' WHERE NOT has_link ' )
def test_add_term_using_has_operator_non_supported_operand_should_raise_error ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' has ' , operand = ' non_supported ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_in_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
mute_stream ( self . realm , self . user_profile , ' Verona ' )
term = dict ( operator = ' in ' , operand = ' home ' )
self . _do_add_term_test ( term , ' WHERE recipient_id NOT IN (:recipient_id_1) ' )
def test_add_term_using_in_operator_and_negated ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
# negated = True should not change anything
mute_stream ( self . realm , self . user_profile , ' Verona ' )
term = dict ( operator = ' in ' , operand = ' home ' , negated = True )
self . _do_add_term_test ( term , ' WHERE recipient_id NOT IN (:recipient_id_1) ' )
def test_add_term_using_in_operator_and_all_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
mute_stream ( self . realm , self . user_profile , ' Verona ' )
term = dict ( operator = ' in ' , operand = ' all ' )
query = self . _build_query ( term )
self . assertEqual ( str ( query ) , ' SELECT id \n FROM zerver_message ' )
def test_add_term_using_in_operator_all_operand_and_negated ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
# negated = True should not change anything
mute_stream ( self . realm , self . user_profile , ' Verona ' )
term = dict ( operator = ' in ' , operand = ' all ' , negated = True )
query = self . _build_query ( term )
self . assertEqual ( str ( query ) , ' SELECT id \n FROM zerver_message ' )
def test_add_term_using_in_operator_and_not_defined_operand ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' in ' , operand = ' not_defined ' )
self . assertRaises ( BadNarrowOperator , self . _build_query , term )
def test_add_term_using_near_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
term = dict ( operator = ' near ' , operand = ' operand ' )
query = self . _build_query ( term )
self . assertEqual ( str ( query ) , ' SELECT id \n FROM zerver_message ' )
2017-08-16 15:20:11 +02:00
def _do_add_term_test ( self , term , where_clause , params = None ) :
# type: (Dict[str, Any], Text, Optional[Dict[str, Any]]) -> None
query = self . _build_query ( term )
if params is not None :
actual_params = query . compile ( ) . params
self . assertEqual ( actual_params , params )
self . assertTrue ( where_clause in str ( query ) )
2016-06-21 21:05:44 +02:00
def _build_query ( self , term ) :
2016-12-08 09:39:48 +01:00
# type: (Dict[str, Any]) -> Query
2016-06-21 21:05:44 +02:00
return self . builder . add_term ( self . raw_query , term )
2016-07-16 22:56:33 +02:00
class BuildNarrowFilterTest ( TestCase ) :
def test_build_narrow_filter ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-07-16 22:56:33 +02:00
fixtures_path = os . path . join ( os . path . dirname ( __file__ ) ,
' ../fixtures/narrow.json ' )
scenarios = ujson . loads ( open ( fixtures_path , ' r ' ) . read ( ) )
2017-06-19 03:21:48 +02:00
self . assertTrue ( len ( scenarios ) == 9 )
2016-07-16 22:56:33 +02:00
for scenario in scenarios :
narrow = scenario [ ' narrow ' ]
accept_events = scenario [ ' accept_events ' ]
reject_events = scenario [ ' reject_events ' ]
narrow_filter = build_narrow_filter ( narrow )
for e in accept_events :
self . assertTrue ( narrow_filter ( e ) )
for e in reject_events :
self . assertFalse ( narrow_filter ( e ) )
2017-03-05 08:56:57 +01:00
def test_build_narrow_filter_invalid ( self ) :
# type: () -> None
with self . assertRaises ( JsonableError ) :
build_narrow_filter ( [ " invalid_operator " , " operand " ] )
2016-08-23 02:08:42 +02:00
class IncludeHistoryTest ( ZulipTestCase ) :
2016-06-21 21:05:44 +02:00
def test_ok_to_include_history ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-01-04 05:30:48 +01:00
realm = get_realm ( ' zulip ' )
2016-10-21 22:59:59 +02:00
self . make_stream ( ' public_stream ' , realm = realm )
2016-06-21 21:05:44 +02:00
# Negated stream searches should not include history.
narrow = [
dict ( operator = ' stream ' , operand = ' public_stream ' , negated = True ) ,
]
self . assertFalse ( ok_to_include_history ( narrow , realm ) )
# Definitely forbid seeing history on private streams.
narrow = [
dict ( operator = ' stream ' , operand = ' private_stream ' ) ,
]
self . assertFalse ( ok_to_include_history ( narrow , realm ) )
# History doesn't apply to PMs.
narrow = [
dict ( operator = ' is ' , operand = ' private ' ) ,
]
self . assertFalse ( ok_to_include_history ( narrow , realm ) )
2017-06-19 03:21:48 +02:00
# History doesn't apply to unread messages.
narrow = [
dict ( operator = ' is ' , operand = ' unread ' ) ,
]
self . assertFalse ( ok_to_include_history ( narrow , realm ) )
2016-06-21 21:05:44 +02:00
# If we are looking for something like starred messages, there is
# no point in searching historical messages.
narrow = [
dict ( operator = ' stream ' , operand = ' public_stream ' ) ,
dict ( operator = ' is ' , operand = ' starred ' ) ,
]
self . assertFalse ( ok_to_include_history ( narrow , realm ) )
# simple True case
narrow = [
dict ( operator = ' stream ' , operand = ' public_stream ' ) ,
]
self . assertTrue ( ok_to_include_history ( narrow , realm ) )
narrow = [
dict ( operator = ' stream ' , operand = ' public_stream ' ) ,
dict ( operator = ' topic ' , operand = ' whatever ' ) ,
dict ( operator = ' search ' , operand = ' needle in haystack ' ) ,
]
self . assertTrue ( ok_to_include_history ( narrow , realm ) )
2016-08-23 02:08:42 +02:00
class GetOldMessagesTest ( ZulipTestCase ) :
2016-06-21 21:05:44 +02:00
2017-08-26 01:01:12 +02:00
def get_and_check_messages ( self , modified_params , * * kwargs ) :
2017-11-02 19:56:14 +01:00
# type: (Dict[str, Union[str, int]], **Any) -> Dict[str, Any]
2017-07-11 21:38:37 +02:00
post_params = { " anchor " : 1 , " num_before " : 1 , " num_after " : 1 } # type: Dict[str, Union[str, int]]
2016-06-21 21:05:44 +02:00
post_params . update ( modified_params )
2017-08-26 01:01:12 +02:00
payload = self . client_get ( " /json/messages " , dict ( post_params ) ,
* * kwargs )
2016-07-24 16:50:30 +02:00
self . assert_json_success ( payload )
result = ujson . loads ( payload . content )
2016-06-21 21:05:44 +02:00
self . assertIn ( " messages " , result )
self . assertIsInstance ( result [ " messages " ] , list )
for message in result [ " messages " ] :
for field in ( " content " , " content_type " , " display_recipient " ,
" avatar_url " , " recipient_id " , " sender_full_name " ,
2016-12-06 07:19:34 +01:00
" sender_short_name " , " timestamp " , " reactions " ) :
2016-06-21 21:05:44 +02:00
self . assertIn ( field , message )
2016-07-24 16:50:30 +02:00
return result
2016-06-21 21:05:44 +02:00
def get_query_ids ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> Dict[Text, int]
2017-05-07 17:21:26 +02:00
hamlet_user = self . example_user ( ' hamlet ' )
othello_user = self . example_user ( ' othello ' )
2016-06-21 21:05:44 +02:00
2017-07-11 21:38:37 +02:00
query_ids = { } # type: Dict[Text, int]
2016-06-21 21:05:44 +02:00
scotland_stream = get_stream ( ' Scotland ' , hamlet_user . realm )
2017-10-28 20:26:11 +02:00
query_ids [ ' scotland_recipient ' ] = get_stream_recipient ( scotland_stream . id ) . id
2016-06-21 21:05:44 +02:00
query_ids [ ' hamlet_id ' ] = hamlet_user . id
query_ids [ ' othello_id ' ] = othello_user . id
2017-10-28 21:31:21 +02:00
query_ids [ ' hamlet_recipient ' ] = get_personal_recipient ( hamlet_user . id ) . id
query_ids [ ' othello_recipient ' ] = get_personal_recipient ( othello_user . id ) . id
2016-06-21 21:05:44 +02:00
return query_ids
2017-10-21 03:25:39 +02:00
def test_content_types ( self ) :
# type: () -> None
"""
Test old ` / json / messages ` returns reactions .
"""
self . login ( self . example_email ( " hamlet " ) )
def get_content_type ( apply_markdown ) :
# type: (bool) -> Text
req = dict (
apply_markdown = ujson . dumps ( apply_markdown ) ,
) # type: Dict[str, Any]
result = self . get_and_check_messages ( req )
message = result [ ' messages ' ] [ 0 ]
return message [ ' content_type ' ]
self . assertEqual (
get_content_type ( apply_markdown = False ) ,
' text/x-markdown ' ,
)
self . assertEqual (
get_content_type ( apply_markdown = True ) ,
' text/html ' ,
)
2017-03-24 07:51:46 +01:00
def test_successful_get_messages_reaction ( self ) :
2016-12-06 07:19:34 +01:00
# type: () -> None
"""
Test old ` / json / messages ` returns reactions .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-12-06 07:19:34 +01:00
messages = self . get_and_check_messages ( dict ( ) )
message_id = messages [ ' messages ' ] [ 0 ] [ ' id ' ]
2017-05-25 02:08:35 +02:00
self . login ( self . example_email ( " othello " ) )
2017-10-02 23:47:45 +02:00
reaction_name = ' thumbs_up '
2016-12-06 07:19:34 +01:00
url = ' /json/messages/ {} /emoji_reactions/ {} ' . format ( message_id , reaction_name )
payload = self . client_put ( url )
self . assert_json_success ( payload )
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-12-06 07:19:34 +01:00
messages = self . get_and_check_messages ( { } )
message_to_assert = None
for message in messages [ ' messages ' ] :
if message [ ' id ' ] == message_id :
message_to_assert = message
break
2017-05-24 04:21:29 +02:00
assert ( message_to_assert is not None )
2016-12-16 02:01:34 +01:00
self . assertEqual ( len ( message_to_assert [ ' reactions ' ] ) , 1 )
self . assertEqual ( message_to_assert [ ' reactions ' ] [ 0 ] [ ' emoji_name ' ] ,
2016-12-16 05:51:27 +01:00
reaction_name )
2016-12-06 07:19:34 +01:00
2017-03-24 07:51:46 +01:00
def test_successful_get_messages ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
A call to GET / json / messages with valid parameters returns a list of
messages .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-07-24 16:50:30 +02:00
self . get_and_check_messages ( dict ( ) )
2016-06-21 21:05:44 +02:00
# We have to support the legacy tuple style while there are old
# clients around, which might include third party home-grown bots.
2017-05-25 02:08:35 +02:00
self . get_and_check_messages ( dict ( narrow = ujson . dumps ( [ [ ' pm-with ' , self . example_email ( " othello " ) ] ] ) ) )
2016-06-21 21:05:44 +02:00
2017-05-25 02:08:35 +02:00
self . get_and_check_messages ( dict ( narrow = ujson . dumps ( [ dict ( operator = ' pm-with ' , operand = self . example_email ( " othello " ) ) ] ) ) )
2016-06-21 21:05:44 +02:00
2017-10-20 16:52:04 +02:00
def test_client_avatar ( self ) :
# type: () -> None
"""
The client_gravatar flag determines whether we send avatar_url .
"""
hamlet = self . example_user ( ' hamlet ' )
self . login ( hamlet . email )
2017-10-28 17:38:19 +02:00
self . send_personal_message ( hamlet . email , self . example_email ( " iago " ) )
2017-10-20 16:52:04 +02:00
result = self . get_and_check_messages ( { } )
message = result [ ' messages ' ] [ 0 ]
self . assertIn ( ' gravatar.com ' , message [ ' avatar_url ' ] )
result = self . get_and_check_messages ( dict ( client_gravatar = ujson . dumps ( True ) ) )
message = result [ ' messages ' ] [ 0 ]
self . assertEqual ( message [ ' avatar_url ' ] , None )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_pm_with ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
A request for old messages with a narrow by pm - with only returns
conversations with that user .
"""
2017-05-24 02:42:31 +02:00
me = self . example_email ( ' hamlet ' )
2016-11-29 07:22:02 +01:00
2016-06-21 21:05:44 +02:00
def dr_emails ( dr ) :
2016-12-08 09:39:48 +01:00
# type: (Union[Text, List[Dict[str, Any]]]) -> Text
assert isinstance ( dr , list )
2016-06-21 21:05:44 +02:00
return ' , ' . join ( sorted ( set ( [ r [ ' email ' ] for r in dr ] + [ me ] ) ) )
2017-10-28 17:38:19 +02:00
self . send_personal_message ( me , self . example_email ( " iago " ) )
self . send_huddle_message (
me ,
[ self . example_email ( " iago " ) , self . example_email ( " cordelia " ) ] ,
)
2017-05-24 02:42:31 +02:00
personals = [ m for m in get_user_messages ( self . example_user ( ' hamlet ' ) )
2017-10-28 21:53:47 +02:00
if not m . is_stream_message ( ) ]
2017-03-14 09:15:37 +01:00
for personal in personals :
emails = dr_emails ( get_display_recipient ( personal . recipient ) )
2016-06-21 21:05:44 +02:00
2017-03-14 09:15:37 +01:00
self . login ( me )
narrow = [ dict ( operator = ' pm-with ' , operand = emails ) ]
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ) )
2016-06-21 21:05:44 +02:00
2017-03-14 09:15:37 +01:00
for message in result [ " messages " ] :
self . assertEqual ( dr_emails ( message [ ' display_recipient ' ] ) , emails )
2016-06-21 21:05:44 +02:00
2017-03-23 23:35:37 +01:00
def test_get_messages_with_narrow_group_pm_with ( self ) :
# type: () -> None
"""
A request for old messages with a narrow by group - pm - with only returns
group - private conversations with that user .
"""
2017-05-25 01:40:26 +02:00
me = self . example_email ( " hamlet " )
2017-03-23 23:35:37 +01:00
matching_message_ids = [ ]
2017-10-28 17:38:19 +02:00
matching_message_ids . append (
self . send_huddle_message (
me ,
[
self . example_email ( " iago " ) ,
self . example_email ( " cordelia " ) ,
self . example_email ( " othello " ) ,
] ,
) ,
)
matching_message_ids . append (
self . send_huddle_message (
me ,
[
self . example_email ( " cordelia " ) ,
self . example_email ( " othello " ) ,
] ,
) ,
)
2017-03-23 23:35:37 +01:00
non_matching_message_ids = [ ]
2017-10-28 17:38:19 +02:00
non_matching_message_ids . append (
self . send_personal_message ( me , self . example_email ( " cordelia " ) ) ,
)
non_matching_message_ids . append (
self . send_huddle_message (
me ,
[
self . example_email ( " iago " ) ,
self . example_email ( " othello " ) ,
] ,
) ,
)
non_matching_message_ids . append (
self . send_huddle_message (
self . example_email ( " cordelia " ) ,
[
self . example_email ( " iago " ) ,
self . example_email ( " othello " ) ,
] ,
) ,
)
2017-03-23 23:35:37 +01:00
self . login ( me )
2017-05-25 01:50:35 +02:00
narrow = [ dict ( operator = ' group-pm-with ' , operand = self . example_email ( " cordelia " ) ) ]
2017-03-23 23:35:37 +01:00
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ) )
for message in result [ " messages " ] :
self . assertIn ( message [ " id " ] , matching_message_ids )
self . assertNotIn ( message [ " id " ] , non_matching_message_ids )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_stream ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
A request for old messages with a narrow by stream only returns
messages for that stream .
"""
2017-05-24 02:42:31 +02:00
self . login ( self . example_email ( ' hamlet ' ) )
2017-02-22 21:23:22 +01:00
# We need to subscribe to a stream and then send a message to
2016-06-21 21:05:44 +02:00
# it to ensure that we actually have a stream message in this
# narrow view.
2017-08-25 06:01:29 +02:00
self . subscribe ( self . example_user ( " hamlet " ) , ' Scotland ' )
2017-10-28 17:38:19 +02:00
self . send_stream_message ( self . example_email ( " hamlet " ) , " Scotland " )
2017-05-07 17:21:26 +02:00
messages = get_user_messages ( self . example_user ( ' hamlet ' ) )
2017-10-28 21:53:47 +02:00
stream_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2016-06-21 21:05:44 +02:00
stream_name = get_display_recipient ( stream_messages [ 0 ] . recipient )
stream_id = stream_messages [ 0 ] . recipient . id
narrow = [ dict ( operator = ' stream ' , operand = stream_name ) ]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ) )
2016-06-21 21:05:44 +02:00
for message in result [ " messages " ] :
self . assertEqual ( message [ " type " ] , " stream " )
self . assertEqual ( message [ " recipient_id " ] , stream_id )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_stream_mit_unicode_regex ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
A request for old messages for a user in the mit . edu relam with unicode
stream name should be correctly escaped in the database query .
"""
2017-05-24 21:21:35 +02:00
self . login ( self . mit_email ( " starnine " ) )
2016-06-21 21:05:44 +02:00
# We need to susbcribe to a stream and then send a message to
# it to ensure that we actually have a stream message in this
# narrow view.
2016-10-21 22:59:59 +02:00
lambda_stream_name = u " \u03bb -stream "
2017-10-08 21:16:51 +02:00
stream = self . subscribe ( self . mit_user ( " starnine " ) , lambda_stream_name )
self . assertTrue ( stream . is_in_zephyr_realm )
2016-06-21 21:05:44 +02:00
2016-10-21 22:59:59 +02:00
lambda_stream_d_name = u " \u03bb -stream.d "
2017-08-25 06:01:29 +02:00
self . subscribe ( self . mit_user ( " starnine " ) , lambda_stream_d_name )
2016-06-21 21:05:44 +02:00
2017-10-28 17:38:19 +02:00
self . send_stream_message ( self . mit_email ( " starnine " ) , u " \u03bb -stream " )
self . send_stream_message ( self . mit_email ( " starnine " ) , u " \u03bb -stream.d " )
2016-06-21 21:05:44 +02:00
narrow = [ dict ( operator = ' stream ' , operand = u ' \u03bb -stream ' ) ]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict ( num_after = 2 ,
2017-08-26 01:01:12 +02:00
narrow = ujson . dumps ( narrow ) ) ,
subdomain = " zephyr " )
2016-06-21 21:05:44 +02:00
2017-05-24 21:21:35 +02:00
messages = get_user_messages ( self . mit_user ( " starnine " ) )
2017-10-28 21:53:47 +02:00
stream_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2016-06-21 21:05:44 +02:00
self . assertEqual ( len ( result [ " messages " ] ) , 2 )
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
stream_id = stream_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , stream_id )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_topic_mit_unicode_regex ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
2017-02-22 21:23:22 +01:00
A request for old messages for a user in the mit . edu realm with unicode
2016-06-21 21:05:44 +02:00
topic name should be correctly escaped in the database query .
"""
2017-05-23 02:33:53 +02:00
mit_user_profile = self . mit_user ( " starnine " )
email = mit_user_profile . email
self . login ( email )
2016-06-21 21:05:44 +02:00
# We need to susbcribe to a stream and then send a message to
# it to ensure that we actually have a stream message in this
# narrow view.
2017-08-25 06:01:29 +02:00
self . subscribe ( mit_user_profile , " Scotland " )
2016-06-21 21:05:44 +02:00
2017-10-28 17:38:19 +02:00
self . send_stream_message ( email , " Scotland " ,
topic_name = u " \u03bb -topic " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " \u03bb -topic.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " \u03bb -topic.d.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " \u03bb -topic.d.d.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " \u03bb -topic.d.d.d.d " )
2016-06-21 21:05:44 +02:00
narrow = [ dict ( operator = ' topic ' , operand = u ' \u03bb -topic ' ) ]
2017-08-26 01:01:12 +02:00
result = self . get_and_check_messages (
dict ( num_after = 100 , narrow = ujson . dumps ( narrow ) ) ,
subdomain = " zephyr " )
2016-06-21 21:05:44 +02:00
2017-05-23 02:33:53 +02:00
messages = get_user_messages ( mit_user_profile )
2017-10-28 21:53:47 +02:00
stream_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2017-02-22 21:23:22 +01:00
self . assertEqual ( len ( result [ " messages " ] ) , 5 )
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
stream_id = stream_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , stream_id )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_topic_mit_personal ( self ) :
2017-02-22 21:23:22 +01:00
# type: () -> None
"""
We handle . d grouping for MIT realm personal messages correctly .
"""
2017-05-23 02:33:53 +02:00
mit_user_profile = self . mit_user ( " starnine " )
email = mit_user_profile . email
2017-07-11 21:38:37 +02:00
self . login ( email ) # We need to susbcribe to a stream and then send a message to
2017-02-22 21:23:22 +01:00
# it to ensure that we actually have a stream message in this
# narrow view.
2017-08-25 06:01:29 +02:00
self . subscribe ( mit_user_profile , " Scotland " )
2017-02-22 21:23:22 +01:00
2017-10-28 17:38:19 +02:00
self . send_stream_message ( email , " Scotland " ,
topic_name = u " .d.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " PERSONAL " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u ' (instance " " ).d ' )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " .d.d.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " personal.d " )
self . send_stream_message ( email , " Scotland " ,
topic_name = u ' (instance " " ) ' )
self . send_stream_message ( email , " Scotland " ,
topic_name = u " .d.d.d.d " )
2017-02-22 21:23:22 +01:00
narrow = [ dict ( operator = ' topic ' , operand = u ' personal.d.d ' ) ]
2017-08-26 01:01:12 +02:00
result = self . get_and_check_messages (
dict ( num_before = 50 ,
num_after = 50 ,
narrow = ujson . dumps ( narrow ) ) ,
subdomain = " zephyr " )
2017-02-22 21:23:22 +01:00
2017-05-23 02:33:53 +02:00
messages = get_user_messages ( mit_user_profile )
2017-10-28 21:53:47 +02:00
stream_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2017-02-22 21:23:22 +01:00
self . assertEqual ( len ( result [ " messages " ] ) , 7 )
2016-06-21 21:05:44 +02:00
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
stream_id = stream_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , stream_id )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_sender ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
A request for old messages with a narrow by sender only returns
messages sent by that person .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
# We need to send a message here to ensure that we actually
# have a stream message in this narrow view.
2017-10-28 17:38:19 +02:00
self . send_stream_message ( self . example_email ( " hamlet " ) , " Scotland " )
self . send_stream_message ( self . example_email ( " othello " ) , " Scotland " )
self . send_personal_message ( self . example_email ( " othello " ) , self . example_email ( " hamlet " ) )
self . send_stream_message ( self . example_email ( " iago " ) , " Scotland " )
2016-06-21 21:05:44 +02:00
2017-05-25 02:08:35 +02:00
narrow = [ dict ( operator = ' sender ' , operand = self . example_email ( " othello " ) ) ]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ) )
2016-06-21 21:05:44 +02:00
for message in result [ " messages " ] :
2017-05-25 02:08:35 +02:00
self . assertEqual ( message [ " sender_email " ] , self . example_email ( " othello " ) )
2016-06-21 21:05:44 +02:00
2016-09-19 16:34:01 +02:00
def _update_tsvector_index ( self ) :
# type: () -> None
# We use brute force here and update our text search index
# for the entire zerver_message table (which is small in test
# mode). In production there is an async process which keeps
# the search index up to date.
with connection . cursor ( ) as cursor :
cursor . execute ( """
UPDATE zerver_message SET
search_tsvector = to_tsvector ( ' zulip.english_us_search ' ,
subject | | rendered_content )
""" )
2016-09-19 20:18:33 +02:00
@override_settings ( USING_PGROONGA = False )
def test_messages_in_narrow ( self ) :
# type: () -> None
2017-05-25 01:50:35 +02:00
email = self . example_email ( " cordelia " )
2016-09-19 20:18:33 +02:00
self . login ( email )
def send ( content ) :
2016-12-08 09:39:48 +01:00
# type: (Text) -> int
2017-10-28 17:38:19 +02:00
msg_id = self . send_stream_message (
sender_email = email ,
stream_name = " Verona " ,
2016-09-19 20:18:33 +02:00
content = content ,
)
return msg_id
good_id = send ( ' KEYWORDMATCH and should work ' )
bad_id = send ( ' no match ' )
msg_ids = [ good_id , bad_id ]
send ( ' KEYWORDMATCH but not in msg_ids ' )
self . _update_tsvector_index ( )
narrow = [
dict ( operator = ' search ' , operand = ' KEYWORDMATCH ' ) ,
]
raw_params = dict ( msg_ids = msg_ids , narrow = narrow )
params = { k : ujson . dumps ( v ) for k , v in raw_params . items ( ) }
2017-07-31 21:09:55 +02:00
result = self . client_get ( ' /json/messages/matches_narrow ' , params )
2016-09-19 20:18:33 +02:00
self . assert_json_success ( result )
2017-08-17 08:41:20 +02:00
messages = result . json ( ) [ ' messages ' ]
2016-09-19 20:18:33 +02:00
self . assertEqual ( len ( list ( messages . keys ( ) ) ) , 1 )
message = messages [ str ( good_id ) ]
self . assertEqual ( message [ ' match_content ' ] ,
2016-12-03 00:04:17 +01:00
u ' <p><span class= " highlight " >KEYWORDMATCH</span> and should work</p> ' )
2016-09-19 20:18:33 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_search ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:50:35 +02:00
self . login ( self . example_email ( " cordelia " ) )
2016-07-17 04:07:07 +02:00
messages_to_search = [
( ' breakfast ' , ' there are muffins in the conference room ' ) ,
( ' lunch plans ' , ' I am hungry! ' ) ,
( ' meetings ' , ' discuss lunch after lunch ' ) ,
( ' meetings ' , ' please bring your laptops to take notes ' ) ,
( ' dinner ' , ' Anybody staying late tonight? ' ) ,
2017-07-12 08:19:13 +02:00
( ' urltest ' , ' https://google.com ' ) ,
2017-10-31 18:24:00 +01:00
( u ' 日本 ' , u ' こんに ちは 。 今日は いい 天気ですね。 ' ) ,
2017-10-31 13:00:37 +01:00
( u ' 日本 ' , u ' 今朝はごはんを食べました。 ' ) ,
( u ' 日本 ' , u ' 昨日、日本 のお菓子を送りました。 ' ) ,
( ' english ' , u ' I want to go to 日本! ' ) ,
2016-07-17 04:07:07 +02:00
]
2017-08-15 18:20:45 +02:00
next_message_id = self . get_last_message ( ) . id + 1
2016-07-17 04:07:07 +02:00
for topic , content in messages_to_search :
2017-10-28 17:38:19 +02:00
self . send_stream_message (
sender_email = self . example_email ( " cordelia " ) ,
stream_name = " Verona " ,
2016-07-17 04:07:07 +02:00
content = content ,
2017-10-28 17:38:19 +02:00
topic_name = topic ,
2016-07-17 04:07:07 +02:00
)
2016-09-19 16:34:01 +02:00
self . _update_tsvector_index ( )
2016-07-17 04:07:07 +02:00
narrow = [
2017-05-25 01:50:35 +02:00
dict ( operator = ' sender ' , operand = self . example_email ( " cordelia " ) ) ,
2016-07-17 04:07:07 +02:00
dict ( operator = ' search ' , operand = ' lunch ' ) ,
]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict (
2016-07-17 04:07:07 +02:00
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2016-07-17 04:07:07 +02:00
num_after = 10 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2016-07-17 04:07:07 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 2 )
messages = result [ ' messages ' ]
2017-07-12 08:19:13 +02:00
narrow = [ dict ( operator = ' search ' , operand = ' https://google.com ' ) ]
link_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2017-07-12 08:19:13 +02:00
num_after = 10 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-07-12 08:19:13 +02:00
self . assertEqual ( len ( link_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( link_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p><a href= " https://google.com " target= " _blank " title= " https://google.com " >https://<span class= " highlight " >google.com</span></a></p> ' )
2016-07-17 04:07:07 +02:00
meeting_message = [ m for m in messages if m [ ' subject ' ] == ' meetings ' ] [ 0 ]
self . assertEqual (
meeting_message [ ' match_subject ' ] ,
' meetings ' )
self . assertEqual (
meeting_message [ ' match_content ' ] ,
' <p>discuss <span class= " highlight " >lunch</span> after ' +
' <span class= " highlight " >lunch</span></p> ' )
meeting_message = [ m for m in messages if m [ ' subject ' ] == ' lunch plans ' ] [ 0 ]
self . assertEqual (
meeting_message [ ' match_subject ' ] ,
' <span class= " highlight " >lunch</span> plans ' )
self . assertEqual (
meeting_message [ ' match_content ' ] ,
' <p>I am hungry!</p> ' )
2017-01-16 16:53:20 +01:00
# Should not crash when multiple search operands are present
multi_search_narrow = [
dict ( operator = ' search ' , operand = ' discuss ' ) ,
dict ( operator = ' search ' , operand = ' after ' ) ,
]
multi_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( multi_search_narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-01-16 16:53:20 +01:00
num_after = 10 ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-01-16 16:53:20 +01:00
self . assertEqual ( len ( multi_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( multi_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] , ' <p><span class= " highlight " >discuss</span> lunch <span class= " highlight " >after</span> lunch</p> ' )
2017-10-31 13:00:37 +01:00
# Test searching in messages with unicode characters
narrow = [
dict ( operator = ' search ' , operand = u ' 日本 ' ) ,
]
result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
2017-10-31 18:45:54 +01:00
) )
2017-10-31 13:00:37 +01:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 4 )
messages = result [ ' messages ' ]
japanese_message = [ m for m in messages if m [ ' subject ' ] == u ' 日本 ' ] [ - 1 ]
self . assertEqual (
japanese_message [ ' match_subject ' ] ,
u ' <span class= " highlight " >日本</span> ' )
self . assertEqual (
japanese_message [ ' match_content ' ] ,
u ' <p>昨日、<span class= " highlight " >日本</span> ' +
u ' のお菓子を送りました。</p> ' )
english_message = [ m for m in messages if m [ ' subject ' ] == ' english ' ] [ 0 ]
self . assertEqual (
english_message [ ' match_subject ' ] ,
' english ' )
self . assertIn (
english_message [ ' match_content ' ] ,
u ' <p>I want to go to <span class= " highlight " >日本</span>!</p> ' )
2017-10-31 18:24:00 +01:00
# Multiple search operands with unicode
multi_search_narrow = [
dict ( operator = ' search ' , operand = ' ちは ' ) ,
dict ( operator = ' search ' , operand = ' 今日は ' ) ,
]
multi_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( multi_search_narrow ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
) )
self . assertEqual ( len ( multi_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( multi_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p>こんに <span class= " highlight " >ちは</span> 。 <span class= " highlight " >今日は</span> いい 天気ですね。</p> ' )
2017-02-23 00:21:26 +01:00
@override_settings ( USING_PGROONGA = False )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_search_not_subscribed ( self ) :
2017-02-23 00:21:26 +01:00
# type: () -> None
""" Verify support for searching a stream you ' re not subscribed to """
2017-08-25 06:01:29 +02:00
self . subscribe ( self . example_user ( " hamlet " ) , " newstream " )
2017-10-28 17:38:19 +02:00
self . send_stream_message (
sender_email = self . example_email ( " hamlet " ) ,
stream_name = " newstream " ,
2017-02-23 00:21:26 +01:00
content = " Public special content! " ,
2017-10-28 17:38:19 +02:00
topic_name = " new " ,
2017-02-23 00:21:26 +01:00
)
self . _update_tsvector_index ( )
2017-05-25 01:50:35 +02:00
self . login ( self . example_email ( " cordelia " ) )
2017-02-23 00:21:26 +01:00
stream_search_narrow = [
dict ( operator = ' search ' , operand = ' special ' ) ,
dict ( operator = ' stream ' , operand = ' newstream ' ) ,
]
stream_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( stream_search_narrow ) ,
anchor = 0 ,
num_after = 10 ,
num_before = 10 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-02-23 00:21:26 +01:00
self . assertEqual ( len ( stream_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( stream_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p>Public <span class= " highlight " >special</span> content!</p> ' )
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = True )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_search_pgroonga ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:50:35 +02:00
self . login ( self . example_email ( " cordelia " ) )
2016-04-24 17:08:51 +02:00
2017-08-15 18:20:45 +02:00
next_message_id = self . get_last_message ( ) . id + 1
2016-04-24 17:08:51 +02:00
messages_to_search = [
( u ' 日本語 ' , u ' こんにちは。今日はいい天気ですね。 ' ) ,
( u ' 日本語 ' , u ' 今朝はごはんを食べました。 ' ) ,
( u ' 日本語 ' , u ' 昨日、日本のお菓子を送りました。 ' ) ,
( ' english ' , u ' I want to go to 日本! ' ) ,
2017-04-06 15:59:56 +02:00
( ' english ' , ' Can you speak https://en.wikipedia.org/wiki/Japanese? ' ) ,
2017-07-12 08:19:13 +02:00
( ' english ' , ' https://google.com ' ) ,
2016-04-24 17:08:51 +02:00
]
for topic , content in messages_to_search :
2017-10-28 17:38:19 +02:00
self . send_stream_message (
sender_email = self . example_email ( " cordelia " ) ,
stream_name = " Verona " ,
2016-04-24 17:08:51 +02:00
content = content ,
2017-10-28 17:38:19 +02:00
topic_name = topic ,
2016-04-24 17:08:51 +02:00
)
# We use brute force here and update our text search index
# for the entire zerver_message table (which is small in test
# mode). In production there is an async process which keeps
# the search index up to date.
with connection . cursor ( ) as cursor :
cursor . execute ( """
UPDATE zerver_message SET
search_pgroonga = subject | | ' ' | | rendered_content
""" )
narrow = [
dict ( operator = ' search ' , operand = u ' 日本 ' ) ,
]
result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2016-04-24 17:08:51 +02:00
num_after = 10 ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2016-04-24 17:08:51 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 4 )
messages = result [ ' messages ' ]
japanese_message = [ m for m in messages if m [ ' subject ' ] == u ' 日本語 ' ] [ - 1 ]
self . assertEqual (
japanese_message [ ' match_subject ' ] ,
u ' <span class= " highlight " >日本</span>語 ' )
self . assertEqual (
japanese_message [ ' match_content ' ] ,
u ' <p>昨日、<span class= " highlight " >日本</span>の ' +
u ' お菓子を送りました。</p> ' )
english_message = [ m for m in messages if m [ ' subject ' ] == ' english ' ] [ 0 ]
self . assertEqual (
english_message [ ' match_subject ' ] ,
' english ' )
2017-03-30 06:59:45 +02:00
self . assertIn (
2016-04-24 17:08:51 +02:00
english_message [ ' match_content ' ] ,
2017-03-29 12:02:16 +02:00
# NOTE: The whitespace here is off due to a pgroonga bug.
2017-03-30 06:59:45 +02:00
# This bug is a pgroonga regression and according to one of
# the author, this should be fixed in its next release.
[ u ' <p>I want to go to <span class= " highlight " >日本</span>!</p> ' , # This is correct.
u ' <p>I want to go to<span class= " highlight " > 日本</span>!</p> ' , ] )
2016-07-17 04:07:07 +02:00
2017-01-16 16:53:20 +01:00
# Should not crash when multiple search operands are present
multi_search_narrow = [
dict ( operator = ' search ' , operand = ' can ' ) ,
dict ( operator = ' search ' , operand = ' speak ' ) ,
2017-04-06 15:59:56 +02:00
dict ( operator = ' search ' , operand = ' wiki ' ) ,
2017-01-16 16:53:20 +01:00
]
multi_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( multi_search_narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-01-16 16:53:20 +01:00
num_after = 10 ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-01-16 16:53:20 +01:00
self . assertEqual ( len ( multi_search_result [ ' messages ' ] ) , 1 )
2017-04-06 15:59:56 +02:00
self . assertEqual ( multi_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p><span class= " highlight " >Can</span> you <span class= " highlight " >speak</span> <a href= " https://en.wikipedia.org/wiki/Japanese " target= " _blank " title= " https://en.wikipedia.org/wiki/Japanese " >https://en.<span class= " highlight " >wiki</span>pedia.org/<span class= " highlight " >wiki</span>/Japanese</a>?</p> ' )
2017-01-16 16:53:20 +01:00
2017-10-31 18:24:00 +01:00
# Multiple search operands with unicode
multi_search_narrow = [
dict ( operator = ' search ' , operand = ' 朝は ' ) ,
dict ( operator = ' search ' , operand = ' べました ' ) ,
]
multi_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( multi_search_narrow ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
) )
self . assertEqual ( len ( multi_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( multi_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p>今<span class= " highlight " >朝は</span>ごはんを食<span class= " highlight " >べました</span>。</p> ' )
2017-07-12 08:19:13 +02:00
narrow = [ dict ( operator = ' search ' , operand = ' https://google.com ' ) ]
link_search_result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-07-12 08:19:13 +02:00
num_after = 10 ,
2017-08-16 16:37:06 +02:00
num_before = 0 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-07-12 08:19:13 +02:00
self . assertEqual ( len ( link_search_result [ ' messages ' ] ) , 1 )
self . assertEqual ( link_search_result [ ' messages ' ] [ 0 ] [ ' match_content ' ] ,
' <p><a href= " https://google.com " target= " _blank " title= " https://google.com " ><span class= " highlight " >https://google.com</span></a></p> ' )
2017-08-18 15:50:54 +02:00
def test_messages_in_narrow_for_non_search ( self ) :
# type: () -> None
email = self . example_email ( " cordelia " )
self . login ( email )
def send ( content ) :
# type: (Text) -> int
2017-10-28 17:38:19 +02:00
msg_id = self . send_stream_message (
sender_email = email ,
stream_name = " Verona " ,
topic_name = ' test_topic ' ,
2017-08-18 15:50:54 +02:00
content = content ,
)
return msg_id
good_id = send ( ' http://foo.com ' )
bad_id = send ( ' no link here ' )
msg_ids = [ good_id , bad_id ]
send ( ' http://bar.com but not in msg_ids ' )
narrow = [
dict ( operator = ' has ' , operand = ' link ' ) ,
]
raw_params = dict ( msg_ids = msg_ids , narrow = narrow )
params = { k : ujson . dumps ( v ) for k , v in raw_params . items ( ) }
result = self . client_get ( ' /json/messages/matches_narrow ' , params )
self . assert_json_success ( result )
messages = result . json ( ) [ ' messages ' ]
self . assertEqual ( len ( list ( messages . keys ( ) ) ) , 1 )
message = messages [ str ( good_id ) ]
self . assertIn ( ' a href= ' , message [ ' match_content ' ] )
self . assertIn ( ' http://foo.com ' , message [ ' match_content ' ] )
self . assertEqual ( message [ ' match_subject ' ] , ' test_topic ' )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_only_searching_anchor ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
Test that specifying an anchor but 0 for num_before and num_after
returns at most 1 message .
"""
2017-05-25 01:50:35 +02:00
self . login ( self . example_email ( " cordelia " ) )
2017-10-28 17:38:19 +02:00
anchor = self . send_stream_message ( self . example_email ( " cordelia " ) , " Verona " )
2016-06-21 21:05:44 +02:00
2017-05-25 01:50:35 +02:00
narrow = [ dict ( operator = ' sender ' , operand = self . example_email ( " cordelia " ) ) ]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ,
anchor = anchor , num_before = 0 ,
2017-11-02 19:56:14 +01:00
num_after = 0 ) ) # type: Dict[str, Any]
2016-06-21 21:05:44 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 1 )
narrow = [ dict ( operator = ' is ' , operand = ' mentioned ' ) ]
2016-07-24 16:50:30 +02:00
result = self . get_and_check_messages ( dict ( narrow = ujson . dumps ( narrow ) ,
anchor = anchor , num_before = 0 ,
num_after = 0 ) )
2016-06-21 21:05:44 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 0 )
def test_missing_params ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
anchor , num_before , and num_after are all required
2017-03-24 07:51:46 +01:00
POST parameters for get_messages .
2016-06-21 21:05:44 +02:00
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
2017-07-11 21:38:37 +02:00
required_args = ( ( " anchor " , 1 ) , ( " num_before " , 1 ) , ( " num_after " , 1 ) ) # type: Tuple[Tuple[Text, int], ...]
2016-06-21 21:05:44 +02:00
for i in range ( len ( required_args ) ) :
post_params = dict ( required_args [ : i ] + required_args [ i + 1 : ] )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2016-06-21 21:05:44 +02:00
self . assert_json_error ( result ,
" Missing ' %s ' argument " % ( required_args [ i ] [ 0 ] , ) )
def test_bad_int_params ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
num_before , num_after , and narrow must all be non - negative
integers or strings that can be converted to non - negative integers .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
other_params = [ ( " narrow " , { } ) , ( " anchor " , 0 ) ]
int_params = [ " num_before " , " num_after " ]
bad_types = ( False , " " , " -1 " , - 1 )
for idx , param in enumerate ( int_params ) :
for type in bad_types :
# Rotate through every bad type for every integer
# parameter, one at a time.
2016-12-03 18:07:49 +01:00
post_params = dict ( other_params + [ ( param , type ) ] +
[ ( other_param , 0 ) for other_param in
2016-11-30 14:17:35 +01:00
int_params [ : idx ] + int_params [ idx + 1 : ] ]
2016-06-21 21:05:44 +02:00
)
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2016-06-21 21:05:44 +02:00
self . assert_json_error ( result ,
" Bad value for ' %s ' : %s " % ( param , type ) )
def test_bad_narrow_type ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
narrow must be a list of string pairs .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
2017-07-11 21:38:37 +02:00
other_params = [ ( " anchor " , 0 ) , ( " num_before " , 0 ) , ( " num_after " , 0 ) ] # type: List[Tuple[Text, Union[int, str, bool]]]
2016-06-21 21:05:44 +02:00
bad_types = ( False , 0 , ' ' , ' { malformed json, ' ,
2017-07-11 21:38:37 +02:00
' {foo: 3} ' , ' [1,2] ' , ' [[ " x " , " y " , " z " ]] ' ) # type: Tuple[Union[int, str, bool], ...]
2016-06-21 21:05:44 +02:00
for type in bad_types :
post_params = dict ( other_params + [ ( " narrow " , type ) ] )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2016-06-21 21:05:44 +02:00
self . assert_json_error ( result ,
" Bad value for ' narrow ' : %s " % ( type , ) )
def test_bad_narrow_operator ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
Unrecognized narrow operators are rejected .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
for operator in [ ' ' , ' foo ' , ' stream:verona ' , ' __init__ ' ] :
narrow = [ dict ( operator = operator , operand = ' ' ) ]
params = dict ( anchor = 0 , num_before = 0 , num_after = 0 , narrow = ujson . dumps ( narrow ) )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , params )
2016-06-21 21:05:44 +02:00
self . assert_json_error_contains ( result ,
2016-12-03 00:04:17 +01:00
" Invalid narrow operator: unknown operator " )
2016-06-21 21:05:44 +02:00
2016-07-26 23:53:14 +02:00
def test_non_string_narrow_operand_in_dict ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-07-26 23:53:14 +02:00
"""
We expect search operands to be strings , not integers .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-07-26 23:53:14 +02:00
not_a_string = 42
narrow = [ dict ( operator = ' stream ' , operand = not_a_string ) ]
params = dict ( anchor = 0 , num_before = 0 , num_after = 0 , narrow = ujson . dumps ( narrow ) )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , params )
2016-07-26 23:53:14 +02:00
self . assert_json_error_contains ( result , ' elem[ " operand " ] is not a string ' )
2016-06-21 21:05:44 +02:00
def exercise_bad_narrow_operand ( self , operator , operands , error_msg ) :
2016-12-08 09:39:48 +01:00
# type: (Text, Sequence, Text) -> None
2017-07-11 21:38:37 +02:00
other_params = [ ( " anchor " , 0 ) , ( " num_before " , 0 ) , ( " num_after " , 0 ) ] # type: List
2016-06-21 21:05:44 +02:00
for operand in operands :
post_params = dict ( other_params + [
( " narrow " , ujson . dumps ( [ [ operator , operand ] ] ) ) ] )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2016-06-21 21:05:44 +02:00
self . assert_json_error_contains ( result , error_msg )
def test_bad_narrow_stream_content ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
2017-03-24 07:51:46 +01:00
If an invalid stream name is requested in get_messages , an error is
2016-06-21 21:05:44 +02:00
returned .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2017-07-11 21:38:37 +02:00
bad_stream_content = ( 0 , [ ] , [ " x " , " y " ] ) # type: Sequence
2016-06-21 21:05:44 +02:00
self . exercise_bad_narrow_operand ( " stream " , bad_stream_content ,
2016-12-03 00:04:17 +01:00
" Bad value for ' narrow ' " )
2016-06-21 21:05:44 +02:00
def test_bad_narrow_one_on_one_email_content ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
"""
2017-03-24 07:51:46 +01:00
If an invalid ' pm-with ' is requested in get_messages , an
2016-06-21 21:05:44 +02:00
error is returned .
"""
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2017-07-11 21:38:37 +02:00
bad_stream_content = ( 0 , [ ] , [ " x " , " y " ] ) # type: Tuple[int, List[None], List[Text]]
2016-06-21 21:05:44 +02:00
self . exercise_bad_narrow_operand ( " pm-with " , bad_stream_content ,
2016-12-03 00:04:17 +01:00
" Bad value for ' narrow ' " )
2016-06-21 21:05:44 +02:00
def test_bad_narrow_nonexistent_stream ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
self . exercise_bad_narrow_operand ( " stream " , [ ' non-existent stream ' ] ,
2016-12-03 00:04:17 +01:00
" Invalid narrow operator: unknown stream " )
2016-06-21 21:05:44 +02:00
def test_bad_narrow_nonexistent_email ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-25 01:40:26 +02:00
self . login ( self . example_email ( " hamlet " ) )
2016-06-21 21:05:44 +02:00
self . exercise_bad_narrow_operand ( " pm-with " , [ ' non-existent-user@zulip.com ' ] ,
2016-12-03 00:04:17 +01:00
" Invalid narrow operator: unknown user " )
2016-06-21 21:05:44 +02:00
def test_message_without_rendered_content ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
""" Older messages may not have rendered_content in the database """
m = self . get_last_message ( )
m . rendered_content = m . rendered_content_version = None
m . content = ' test content '
2017-10-20 21:34:05 +02:00
d = MessageDict . wide_dict ( m )
MessageDict . finalize_payload ( d , apply_markdown = True )
2016-06-21 21:05:44 +02:00
self . assertEqual ( d [ ' content ' ] , ' <p>test content</p> ' )
2017-03-24 07:51:46 +01:00
def common_check_get_messages_query ( self , query_params , expected ) :
2016-12-08 09:39:48 +01:00
# type: (Dict[str, object], Text) -> None
2017-05-07 17:21:26 +02:00
user_profile = self . example_user ( ' hamlet ' )
2016-06-21 21:05:44 +02:00
request = POSTRequestMock ( query_params , user_profile )
with queries_captured ( ) as queries :
2017-03-24 07:51:46 +01:00
get_messages_backend ( request , user_profile )
2016-06-21 21:05:44 +02:00
for query in queries :
2017-03-24 07:51:46 +01:00
if " /* get_messages */ " in query [ ' sql ' ] :
sql = str ( query [ ' sql ' ] ) . replace ( " /* get_messages */ " , ' ' )
2016-06-21 21:05:44 +02:00
self . assertEqual ( sql , expected )
return
2017-03-24 07:51:46 +01:00
raise AssertionError ( " get_messages query not found " )
2016-06-21 21:05:44 +02:00
2016-07-23 18:41:39 +02:00
def test_use_first_unread_anchor_with_some_unread_messages ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-07 17:21:26 +02:00
user_profile = self . example_user ( ' hamlet ' )
2016-07-23 18:41:39 +02:00
# Have Othello send messages to Hamlet that he hasn't read.
2017-10-28 17:38:19 +02:00
self . send_stream_message ( self . example_email ( " othello " ) , " Scotland " )
last_message_id_to_hamlet = self . send_personal_message (
self . example_email ( " othello " ) ,
self . example_email ( " hamlet " ) ,
)
2016-07-23 18:41:39 +02:00
# Add a few messages that help us test that our query doesn't
# look at messages that are irrelevant to Hamlet.
2017-10-28 17:38:19 +02:00
self . send_personal_message ( self . example_email ( " othello " ) , self . example_email ( " cordelia " ) )
self . send_personal_message ( self . example_email ( " othello " ) , self . example_email ( " iago " ) )
2016-07-23 18:41:39 +02:00
query_params = dict (
use_first_unread_anchor = ' true ' ,
anchor = 0 ,
2017-02-22 23:32:43 +01:00
num_before = 10 ,
num_after = 10 ,
2016-07-23 18:41:39 +02:00
narrow = ' [] '
)
request = POSTRequestMock ( query_params , user_profile )
with queries_captured ( ) as all_queries :
2017-03-24 07:51:46 +01:00
get_messages_backend ( request , user_profile )
2016-07-23 18:41:39 +02:00
# Verify the query for old messages looks correct.
2017-03-24 07:51:46 +01:00
queries = [ q for q in all_queries if ' /* get_messages */ ' in q [ ' sql ' ] ]
2016-07-23 18:41:39 +02:00
self . assertEqual ( len ( queries ) , 1 )
sql = queries [ 0 ] [ ' sql ' ]
2017-02-23 05:50:15 +01:00
self . assertNotIn ( ' AND message_id = %s ' % ( LARGER_THAN_MAX_MESSAGE_ID , ) , sql )
2016-07-23 18:41:39 +02:00
self . assertIn ( ' ORDER BY message_id ASC ' , sql )
2017-02-22 23:32:43 +01:00
cond = ' WHERE user_profile_id = %d AND message_id >= %d ' % ( user_profile . id , last_message_id_to_hamlet )
self . assertIn ( cond , sql )
cond = ' WHERE user_profile_id = %d AND message_id <= %d ' % ( user_profile . id , last_message_id_to_hamlet - 1 )
2016-07-23 18:41:39 +02:00
self . assertIn ( cond , sql )
def test_use_first_unread_anchor_with_no_unread_messages ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-05-07 17:21:26 +02:00
user_profile = self . example_user ( ' hamlet ' )
2016-07-23 18:41:39 +02:00
query_params = dict (
use_first_unread_anchor = ' true ' ,
anchor = 0 ,
2017-02-22 23:32:43 +01:00
num_before = 10 ,
num_after = 10 ,
2016-07-23 18:41:39 +02:00
narrow = ' [] '
)
request = POSTRequestMock ( query_params , user_profile )
with queries_captured ( ) as all_queries :
2017-03-24 07:51:46 +01:00
get_messages_backend ( request , user_profile )
2016-07-23 18:41:39 +02:00
# Next, verify the use_first_unread_anchor setting invokes
2017-02-23 05:50:15 +01:00
# the `message_id = LARGER_THAN_MAX_MESSAGE_ID` hack.
2017-03-24 07:51:46 +01:00
queries = [ q for q in all_queries if ' /* get_messages */ ' in q [ ' sql ' ] ]
2016-07-23 18:41:39 +02:00
self . assertEqual ( len ( queries ) , 1 )
2017-02-23 05:50:15 +01:00
self . assertIn ( ' AND message_id <= %d ' % ( LARGER_THAN_MAX_MESSAGE_ID - 1 , ) , queries [ 0 ] [ ' sql ' ] )
2017-02-22 23:39:40 +01:00
# There should not be an after_query in this case, since it'd be useless
2017-02-23 05:50:15 +01:00
self . assertNotIn ( ' AND message_id >= %d ' % ( LARGER_THAN_MAX_MESSAGE_ID , ) , queries [ 0 ] [ ' sql ' ] )
2016-07-23 18:41:39 +02:00
2016-07-23 18:07:08 +02:00
def test_use_first_unread_anchor_with_muted_topics ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-07-23 18:07:08 +02:00
"""
Test that our logic related to ` use_first_unread_anchor `
2017-02-23 05:50:15 +01:00
invokes the ` message_id = LARGER_THAN_MAX_MESSAGE_ID ` hack for
2017-03-24 07:51:46 +01:00
the ` / * get_messages * / ` query when relevant muting
2016-07-23 18:07:08 +02:00
is in effect .
This is a very arcane test on arcane , but very heavily
2017-03-24 07:51:46 +01:00
field - tested , logic in get_messages_backend ( ) . If
2016-07-23 18:07:08 +02:00
this test breaks , be absolutely sure you know what you ' re
doing .
"""
2017-01-04 05:30:48 +01:00
realm = get_realm ( ' zulip ' )
2016-10-21 22:59:59 +02:00
self . make_stream ( ' web stuff ' )
2017-08-30 02:19:34 +02:00
self . make_stream ( ' bogus ' )
2017-05-07 17:21:26 +02:00
user_profile = self . example_user ( ' hamlet ' )
2017-08-24 17:58:40 +02:00
muted_topics = [
[ ' Scotland ' , ' golf ' ] ,
[ ' web stuff ' , ' css ' ] ,
[ ' bogus ' , ' bogus ' ]
]
set_topic_mutes ( user_profile , muted_topics )
2016-06-21 21:05:44 +02:00
query_params = dict (
use_first_unread_anchor = ' true ' ,
anchor = 0 ,
num_before = 0 ,
num_after = 0 ,
narrow = ' [[ " stream " , " Scotland " ]] '
)
request = POSTRequestMock ( query_params , user_profile )
2016-07-23 18:07:08 +02:00
with queries_captured ( ) as all_queries :
2017-03-24 07:51:46 +01:00
get_messages_backend ( request , user_profile )
2016-06-21 21:05:44 +02:00
2016-07-23 18:07:08 +02:00
# Do some tests on the main query, to verify the muting logic
# runs on this code path.
2016-12-08 09:39:48 +01:00
queries = [ q for q in all_queries if str ( q [ ' sql ' ] ) . startswith ( " SELECT message_id, flags " ) ]
2016-07-23 18:07:08 +02:00
self . assertEqual ( len ( queries ) , 1 )
stream = get_stream ( ' Scotland ' , realm )
2017-10-28 20:26:11 +02:00
recipient_id = get_stream_recipient ( stream . id ) . id
2016-07-23 18:07:08 +02:00
cond = ''' AND NOT (recipient_id = {scotland} AND upper(subject) = upper( ' golf ' )) ''' . format ( scotland = recipient_id )
self . assertIn ( cond , queries [ 0 ] [ ' sql ' ] )
# Next, verify the use_first_unread_anchor setting invokes
2017-02-23 05:50:15 +01:00
# the `message_id = LARGER_THAN_MAX_MESSAGE_ID` hack.
2017-03-24 07:51:46 +01:00
queries = [ q for q in all_queries if ' /* get_messages */ ' in q [ ' sql ' ] ]
2016-07-23 18:07:08 +02:00
self . assertEqual ( len ( queries ) , 1 )
2017-02-23 05:50:15 +01:00
self . assertIn ( ' AND message_id = %d ' % ( LARGER_THAN_MAX_MESSAGE_ID , ) ,
queries [ 0 ] [ ' sql ' ] )
2016-06-21 21:05:44 +02:00
def test_exclude_muting_conditions ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2017-01-04 05:30:48 +01:00
realm = get_realm ( ' zulip ' )
2016-10-21 22:59:59 +02:00
self . make_stream ( ' web stuff ' )
2017-05-07 17:21:26 +02:00
user_profile = self . example_user ( ' hamlet ' )
2016-07-23 17:07:38 +02:00
2017-08-30 02:19:34 +02:00
self . make_stream ( ' irrelevant_stream ' )
2016-07-23 17:07:38 +02:00
# Test the do-nothing case first.
2017-08-24 17:58:40 +02:00
muted_topics = [
[ ' irrelevant_stream ' , ' irrelevant_topic ' ]
]
set_topic_mutes ( user_profile , muted_topics )
2016-07-23 17:07:38 +02:00
# If nothing relevant is muted, then exclude_muting_conditions()
# should return an empty list.
narrow = [
dict ( operator = ' stream ' , operand = ' Scotland ' ) ,
]
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
self . assertEqual ( muting_conditions , [ ] )
# Ok, now set up our muted topics to include a topic relevant to our narrow.
2017-08-24 17:58:40 +02:00
muted_topics = [
[ ' Scotland ' , ' golf ' ] ,
[ ' web stuff ' , ' css ' ] ,
]
set_topic_mutes ( user_profile , muted_topics )
2016-06-21 21:05:44 +02:00
2016-07-23 17:07:38 +02:00
# And verify that our query will exclude them.
2016-06-21 21:05:44 +02:00
narrow = [
dict ( operator = ' stream ' , operand = ' Scotland ' ) ,
]
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
2017-02-22 22:13:57 +01:00
query = select ( [ column ( " id " ) . label ( " message_id " ) ] , None , table ( " zerver_message " ) )
2016-06-21 21:05:44 +02:00
query = query . where ( * muting_conditions )
expected_query = '''
SELECT id AS message_id
FROM zerver_message
WHERE NOT ( recipient_id = : recipient_id_1 AND upper ( subject ) = upper ( : upper_1 ) )
'''
self . assertEqual ( fix_ws ( query ) , fix_ws ( expected_query ) )
params = get_sqlalchemy_query_params ( query )
self . assertEqual ( params [ ' recipient_id_1 ' ] , get_recipient_id_for_stream_name ( realm , ' Scotland ' ) )
self . assertEqual ( params [ ' upper_1 ' ] , ' golf ' )
mute_stream ( realm , user_profile , ' Verona ' )
2017-09-20 22:02:22 +02:00
# Using a bogus stream name should be similar to using no narrow at
# all, and we'll exclude all mutes.
narrow = [
dict ( operator = ' stream ' , operand = ' bogus-stream-name ' ) ,
]
2016-06-21 21:05:44 +02:00
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
2017-02-22 22:13:57 +01:00
query = select ( [ column ( " id " ) ] , None , table ( " zerver_message " ) )
2016-06-21 21:05:44 +02:00
query = query . where ( and_ ( * muting_conditions ) )
expected_query = '''
SELECT id
FROM zerver_message
WHERE recipient_id NOT IN ( : recipient_id_1 )
AND NOT
( recipient_id = : recipient_id_2 AND upper ( subject ) = upper ( : upper_1 ) OR
recipient_id = : recipient_id_3 AND upper ( subject ) = upper ( : upper_2 ) ) '''
self . assertEqual ( fix_ws ( query ) , fix_ws ( expected_query ) )
params = get_sqlalchemy_query_params ( query )
self . assertEqual ( params [ ' recipient_id_1 ' ] , get_recipient_id_for_stream_name ( realm , ' Verona ' ) )
self . assertEqual ( params [ ' recipient_id_2 ' ] , get_recipient_id_for_stream_name ( realm , ' Scotland ' ) )
self . assertEqual ( params [ ' upper_1 ' ] , ' golf ' )
2016-10-21 22:59:59 +02:00
self . assertEqual ( params [ ' recipient_id_3 ' ] , get_recipient_id_for_stream_name ( realm , ' web stuff ' ) )
2016-06-21 21:05:44 +02:00
self . assertEqual ( params [ ' upper_2 ' ] , ' css ' )
2017-03-24 07:51:46 +01:00
def test_get_messages_queries ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage \n WHERE user_profile_id = {hamlet_id} AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 11) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 } , sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage \n WHERE user_profile_id = {hamlet_id} AND message_id <= 100 ORDER BY message_id DESC \n LIMIT 11) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 100 , ' num_before ' : 10 , ' num_after ' : 0 } , sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM ((SELECT message_id, flags \n FROM zerver_usermessage \n WHERE user_profile_id = {hamlet_id} AND message_id <= 99 ORDER BY message_id DESC \n LIMIT 10) UNION ALL (SELECT message_id, flags \n FROM zerver_usermessage \n WHERE user_profile_id = {hamlet_id} AND message_id >= 100 ORDER BY message_id ASC \n LIMIT 11)) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 100 , ' num_before ' : 10 , ' num_after ' : 10 } , sql )
2016-06-21 21:05:44 +02:00
2017-03-24 07:51:46 +01:00
def test_get_messages_with_narrow_queries ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND (sender_id = {othello_id} AND recipient_id = {hamlet_recipient} OR sender_id = {hamlet_id} AND recipient_id = {othello_recipient} ) AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
2017-05-25 02:08:35 +02:00
' narrow ' : ' [[ " pm-with " , " %s " ]] ' % ( self . example_email ( " othello " ) , ) } ,
2017-03-24 07:51:46 +01:00
sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND (flags & 2) != 0 AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " is " , " starred " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND sender_id = {othello_id} AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
2017-05-25 02:08:35 +02:00
' narrow ' : ' [[ " sender " , " %s " ]] ' % ( self . example_email ( " othello " ) , ) } ,
2017-03-24 07:51:46 +01:00
sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id \n FROM (SELECT id AS message_id \n FROM zerver_message \n WHERE recipient_id = {scotland_recipient} AND zerver_message.id >= 0 ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " stream " , " Scotland " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
sql_template = " SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND upper(subject) = upper( ' blah ' ) AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " topic " , " blah " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
sql_template = " SELECT anon_1.message_id \n FROM (SELECT id AS message_id \n FROM zerver_message \n WHERE recipient_id = {scotland_recipient} AND upper(subject) = upper( ' blah ' ) AND zerver_message.id >= 0 ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " stream " , " Scotland " ], [ " topic " , " blah " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
# Narrow to pms with yourself
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND sender_id = {hamlet_id} AND recipient_id = {hamlet_recipient} AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
2017-05-25 01:40:26 +02:00
' narrow ' : ' [[ " pm-with " , " %s " ]] ' % ( self . example_email ( " hamlet " ) , ) } ,
2017-03-24 07:51:46 +01:00
sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags \n FROM (SELECT message_id, flags \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND recipient_id = {scotland_recipient} AND (flags & 2) != 0 AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " stream " , " Scotland " ], [ " is " , " starred " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2017-03-24 07:51:46 +01:00
def test_get_messages_with_search_queries ( self ) :
2016-12-08 09:39:48 +01:00
# type: () -> None
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
2017-07-11 21:38:37 +02:00
sql_template = " SELECT anon_1.message_id, anon_1.flags, anon_1.subject, anon_1.rendered_content, anon_1.content_matches, anon_1.subject_matches \n FROM (SELECT message_id, flags, subject, rendered_content, ts_match_locs_array( ' zulip.english_us_search ' , rendered_content, plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AS content_matches, ts_match_locs_array( ' zulip.english_us_search ' , escape_html(subject), plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AS subject_matches \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND (search_tsvector @@ plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC " # type: Text
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " search " , " jumping " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
sql_template = " SELECT anon_1.message_id, anon_1.subject, anon_1.rendered_content, anon_1.content_matches, anon_1.subject_matches \n FROM (SELECT id AS message_id, subject, rendered_content, ts_match_locs_array( ' zulip.english_us_search ' , rendered_content, plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AS content_matches, ts_match_locs_array( ' zulip.english_us_search ' , escape_html(subject), plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AS subject_matches \n FROM zerver_message \n WHERE recipient_id = {scotland_recipient} AND (search_tsvector @@ plainto_tsquery( ' zulip.english_us_search ' , ' jumping ' )) AND zerver_message.id >= 0 ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " stream " , " Scotland " ], [ " search " , " jumping " ]] ' } ,
sql )
2016-06-21 21:05:44 +02:00
sql_template = ' SELECT anon_1.message_id, anon_1.flags, anon_1.subject, anon_1.rendered_content, anon_1.content_matches, anon_1.subject_matches \n FROM (SELECT message_id, flags, subject, rendered_content, ts_match_locs_array( \' zulip.english_us_search \' , rendered_content, plainto_tsquery( \' zulip.english_us_search \' , \' " jumping " quickly \' )) AS content_matches, ts_match_locs_array( \' zulip.english_us_search \' , escape_html(subject), plainto_tsquery( \' zulip.english_us_search \' , \' " jumping " quickly \' )) AS subject_matches \n FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage.message_id = zerver_message.id \n WHERE user_profile_id = {hamlet_id} AND (content ILIKE \' % jumping % \' OR subject ILIKE \' % jumping % \' ) AND (search_tsvector @@ plainto_tsquery( \' zulip.english_us_search \' , \' " jumping " quickly \' )) AND message_id >= 0 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC '
sql = sql_template . format ( * * query_ids )
2017-03-24 07:51:46 +01:00
self . common_check_get_messages_query ( { ' anchor ' : 0 , ' num_before ' : 0 , ' num_after ' : 10 ,
' narrow ' : ' [[ " search " , " \\ " jumping \\ " quickly " ]] ' } ,
sql )
2017-04-29 00:03:43 +02:00
@override_settings ( USING_PGROONGA = False )
def test_get_messages_with_search_using_email ( self ) :
# type: () -> None
2017-05-25 01:50:35 +02:00
self . login ( self . example_email ( " cordelia " ) )
2017-04-29 00:03:43 +02:00
messages_to_search = [
( ' say hello ' , ' How are you doing, @**Othello, the Moor of Venice**? ' ) ,
( ' lunch plans ' , ' I am hungry! ' ) ,
]
2017-08-15 18:20:45 +02:00
next_message_id = self . get_last_message ( ) . id + 1
2017-04-29 00:03:43 +02:00
for topic , content in messages_to_search :
2017-10-28 17:38:19 +02:00
self . send_stream_message (
sender_email = self . example_email ( " cordelia " ) ,
stream_name = " Verona " ,
2017-04-29 00:03:43 +02:00
content = content ,
2017-10-28 17:38:19 +02:00
topic_name = topic ,
2017-04-29 00:03:43 +02:00
)
self . _update_tsvector_index ( )
narrow = [
2017-05-25 01:50:35 +02:00
dict ( operator = ' sender ' , operand = self . example_email ( " cordelia " ) ) ,
2017-05-25 02:08:35 +02:00
dict ( operator = ' search ' , operand = self . example_email ( " othello " ) ) ,
2017-04-29 00:03:43 +02:00
]
result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-04-29 00:03:43 +02:00
num_after = 10 ,
2017-11-02 19:56:14 +01:00
) ) # type: Dict[str, Any]
2017-04-29 00:03:43 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 0 )
narrow = [
2017-05-25 01:50:35 +02:00
dict ( operator = ' sender ' , operand = self . example_email ( " cordelia " ) ) ,
2017-04-29 00:03:43 +02:00
dict ( operator = ' search ' , operand = ' othello ' ) ,
]
result = self . get_and_check_messages ( dict (
narrow = ujson . dumps ( narrow ) ,
2017-08-15 18:20:45 +02:00
anchor = next_message_id ,
2017-04-29 00:03:43 +02:00
num_after = 10 ,
2017-04-29 00:47:26 +02:00
) )
2017-04-29 00:03:43 +02:00
self . assertEqual ( len ( result [ ' messages ' ] ) , 1 )
messages = result [ ' messages ' ]
meeting_message = [ m for m in messages if m [ ' subject ' ] == ' say hello ' ] [ 0 ]
self . assertEqual (
meeting_message [ ' match_subject ' ] ,
' say hello ' )
2017-09-28 16:50:12 +02:00
othello = self . example_user ( ' othello ' )
2017-04-29 00:03:43 +02:00
self . assertEqual (
meeting_message [ ' match_content ' ] ,
2017-09-28 16:50:12 +02:00
( ' <p>How are you doing, <span class= " user-mention " data-user-email= " %s " data-user-id= " %s " > ' +
2017-05-25 02:08:35 +02:00
' @<span class= " highlight " >Othello</span>, the Moor of Venice</span>?</p> ' ) % (
2017-09-28 16:50:12 +02:00
othello . email , othello . id ) )