2024-07-12 02:30:25 +02:00
from collections . abc import Sequence
2024-06-09 14:07:56 +02:00
from dataclasses import dataclass
2023-11-19 19:45:19 +01:00
from datetime import timedelta
2024-07-12 02:30:25 +02:00
from typing import TYPE_CHECKING , Any
2022-02-12 03:39:06 +01:00
from unittest import mock
2020-06-11 00:54:34 +02:00
2020-08-07 01:09:47 +02:00
import orjson
2016-07-17 04:07:07 +02:00
from django . db import connection
2020-07-01 04:19:54 +02:00
from django . test import override_settings
2020-07-08 01:29:42 +02:00
from django . utils . timezone import now as timezone_now
2021-08-21 01:07:28 +02:00
from sqlalchemy . sql import ClauseElement , Select , and_ , column , select , table
from sqlalchemy . types import Integer
2023-10-12 19:43:45 +02:00
from typing_extensions import override
2016-06-21 21:05:44 +02:00
2020-07-08 01:29:42 +02:00
from analytics . lib . counts import COUNT_STATS
from analytics . models import RealmCount
2022-04-14 23:55:52 +02:00
from zerver . actions . message_edit import do_update_message
2024-04-02 06:24:20 +02:00
from zerver . actions . reactions import check_add_reaction
2022-04-14 23:57:15 +02:00
from zerver . actions . realm_settings import do_set_realm_property
2022-04-14 23:43:26 +02:00
from zerver . actions . uploads import do_claim_attachments
2021-10-26 09:15:16 +02:00
from zerver . actions . user_settings import do_change_user_setting
2022-04-14 23:48:28 +02:00
from zerver . actions . users import do_deactivate_user
2020-08-04 19:33:43 +02:00
from zerver . lib . avatar import avatar_url
2023-12-15 03:04:08 +01:00
from zerver . lib . display_recipient import get_display_recipient
2021-07-16 22:11:10 +02:00
from zerver . lib . exceptions import JsonableError
2023-10-03 03:22:59 +02:00
from zerver . lib . markdown import render_message_markdown
2021-12-29 13:52:27 +01:00
from zerver . lib . mention import MentionBackend , MentionData
2020-07-08 01:29:42 +02:00
from zerver . lib . message import (
get_first_visible_message_id ,
maybe_update_first_visible_message_id ,
update_first_visible_message_id ,
)
2023-10-03 03:25:57 +02:00
from zerver . lib . message_cache import MessageDict
2022-09-09 02:20:21 +02:00
from zerver . lib . narrow import (
2022-10-19 04:19:19 +02:00
LARGER_THAN_MAX_MESSAGE_ID ,
2022-11-17 09:30:48 +01:00
BadNarrowOperatorError ,
2022-09-09 02:20:21 +02:00
NarrowBuilder ,
2024-06-19 15:47:34 +02:00
NarrowParameter ,
2022-09-09 02:20:21 +02:00
exclude_muting_conditions ,
2022-10-19 04:19:19 +02:00
find_first_unread_anchor ,
2022-09-09 02:20:21 +02:00
is_spectator_compatible ,
2022-10-19 04:14:36 +02:00
ok_to_include_history ,
2022-10-21 19:29:15 +02:00
post_process_limited_query ,
2022-09-09 02:20:21 +02:00
)
2023-06-30 02:48:22 +02:00
from zerver . lib . narrow_helpers import NarrowTerm
2024-04-15 23:10:59 +02:00
from zerver . lib . narrow_predicate import build_narrow_predicate
2016-07-19 08:12:35 +02:00
from zerver . lib . sqlalchemy_utils import get_sqlalchemy_connection
2020-09-29 18:06:50 +02:00
from zerver . lib . streams import StreamDict , create_streams_if_needed , get_public_streams_queryset
2020-06-11 00:54:34 +02:00
from zerver . lib . test_classes import ZulipTestCase
2021-02-07 21:34:01 +01:00
from zerver . lib . test_helpers import HostRequestMock , get_user_messages , queries_captured
2024-06-28 08:57:02 +02:00
from zerver . lib . topic import MATCH_TOPIC , RESOLVED_TOPIC_PREFIX , TOPIC_NAME , messages_for_topic
2023-08-10 16:12:37 +02:00
from zerver . lib . types import UserDisplayRecipient
2024-06-20 18:12:58 +02:00
from zerver . lib . upload import create_attachment
2020-07-08 02:38:54 +02:00
from zerver . lib . url_encoding import near_message_url
2023-03-26 15:36:01 +02:00
from zerver . lib . user_topics import set_topic_visibility_policy
2020-06-11 00:54:34 +02:00
from zerver . models import (
2020-07-08 00:44:42 +02:00
Attachment ,
2020-06-11 00:54:34 +02:00
Message ,
Realm ,
2023-07-16 12:41:09 +02:00
Recipient ,
2020-06-11 00:54:34 +02:00
Subscription ,
UserMessage ,
2020-07-08 00:44:42 +02:00
UserProfile ,
2023-03-26 15:36:01 +02:00
UserTopic ,
2020-06-11 00:54:34 +02:00
)
2023-12-15 02:14:24 +01:00
from zerver . models . realms import get_realm
2024-07-04 14:05:48 +02:00
from zerver . models . recipients import get_or_create_direct_message_group
2023-12-15 03:57:04 +01:00
from zerver . models . streams import get_stream
2022-10-21 19:29:15 +02:00
from zerver . views . message_fetch import get_messages_backend
2016-06-21 21:05:44 +02:00
2022-06-08 04:52:09 +02:00
if TYPE_CHECKING :
from django . test . client import _MonkeyPatchedWSGIResponse as TestHttpResponse
2016-06-21 21:05:44 +02:00
2024-06-09 14:07:56 +02:00
@dataclass
class InvalidParam :
value : object
expected_error : str
2019-08-28 22:35:22 +02:00
def get_sqlalchemy_sql ( query : ClauseElement ) - > str :
2022-02-10 04:59:48 +01:00
with get_sqlalchemy_connection ( ) as conn :
dialect = conn . dialect
2019-08-28 22:35:22 +02:00
comp = query . compile ( dialect = dialect )
return str ( comp )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
2024-07-12 02:30:17 +02:00
def get_sqlalchemy_query_params ( query : ClauseElement ) - > dict [ str , object ] :
2022-02-10 04:59:48 +01:00
with get_sqlalchemy_connection ( ) as conn :
dialect = conn . dialect
2019-08-28 22:35:22 +02:00
comp = query . compile ( dialect = dialect )
return comp . params
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
2024-07-12 02:30:23 +02:00
def get_recipient_id_for_channel_name ( realm : Realm , channel_name : str ) - > int | None :
2024-03-14 13:48:06 +01:00
channel = get_stream ( channel_name , realm )
return channel . recipient . id if channel . recipient is not None else None
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
2024-03-14 13:48:06 +01:00
def mute_channel ( realm : Realm , user_profile : UserProfile , channel_name : str ) - > None :
channel = get_stream ( channel_name , realm )
recipient = channel . recipient
2016-06-21 21:05:44 +02:00
subscription = Subscription . objects . get ( recipient = recipient , user_profile = user_profile )
2018-08-02 23:46:05 +02:00
subscription . is_muted = True
2016-06-21 21:05:44 +02:00
subscription . save ( )
2021-02-12 08:19:30 +01:00
2018-03-15 11:58:25 +01:00
def first_visible_id_as ( message_id : int ) - > Any :
return mock . patch (
2022-11-10 00:35:52 +01:00
" zerver.lib.narrow.get_first_visible_message_id " ,
2018-03-15 11:58:25 +01:00
return_value = message_id ,
)
2021-02-12 08:19:30 +01:00
2016-08-23 02:08:42 +02:00
class NarrowBuilderTest ( ZulipTestCase ) :
2023-10-12 19:43:45 +02:00
@override
2017-11-05 10:51:25 +01:00
def setUp ( self ) - > None :
2019-10-19 20:47:00 +02:00
super ( ) . setUp ( )
2021-02-12 08:20:45 +01:00
self . realm = get_realm ( " zulip " )
self . user_profile = self . example_user ( " hamlet " )
2021-08-21 01:07:28 +02:00
self . builder = NarrowBuilder ( self . user_profile , column ( " id " , Integer ) , self . realm )
2022-02-10 04:13:15 +01:00
self . raw_query = select ( column ( " id " , Integer ) ) . select_from ( table ( " zerver_message " ) )
2021-02-12 08:20:45 +01:00
self . hamlet_email = self . example_user ( " hamlet " ) . email
self . othello_email = self . example_user ( " othello " ) . email
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_not_defined_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " not-defined " , operand = " any " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2024-03-14 16:29:27 +01:00
def test_add_term_using_channel_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channel " , operand = " Scotland " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id = %(recipient_id_1)s " )
2016-06-21 21:05:44 +02:00
2024-03-14 16:29:27 +01:00
def test_add_term_using_channel_operator_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channel " , operand = " Scotland " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id != %(recipient_id_1)s " )
2016-06-21 21:05:44 +02:00
2024-03-14 16:29:27 +01:00
def test_add_term_using_channel_operator_and_non_existing_operand_should_raise_error (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channel " , operand = " non-existing-channel " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2024-03-14 19:53:40 +01:00
def test_add_term_using_channels_operator_and_invalid_operand_should_raise_error (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channels " , operand = " invalid_operands " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2019-08-13 20:20:36 +02:00
2024-03-14 19:53:40 +01:00
def test_add_term_using_channels_operator_and_public_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channels " , operand = " public " )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-12-02 00:02:25 +01:00
" WHERE recipient_id IN (__[POSTCOMPILE_recipient_id_1]) " ,
2021-02-12 08:19:30 +01:00
)
2019-08-13 20:20:36 +02:00
2024-03-14 13:48:06 +01:00
# Add new channels
2024-07-12 02:30:17 +02:00
channel_dicts : list [ StreamDict ] = [
2019-08-13 20:20:36 +02:00
{
2024-03-14 13:48:06 +01:00
" name " : " public-channel " ,
" description " : " Public channel with public history " ,
2019-08-13 20:20:36 +02:00
} ,
{
2024-03-14 13:48:06 +01:00
" name " : " private-channel " ,
" description " : " Private channel with non-public history " ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
" invite_only " : True ,
2019-08-13 20:20:36 +02:00
} ,
{
2024-03-14 13:48:06 +01:00
" name " : " private-channel-with-history " ,
" description " : " Private channel with public history " ,
2019-08-13 20:20:36 +02:00
" invite_only " : True ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
" history_public_to_subscribers " : True ,
} ,
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
]
2021-02-12 08:20:45 +01:00
realm = get_realm ( " zulip " )
2024-03-14 13:48:06 +01:00
created , existing = create_streams_if_needed ( realm , channel_dicts )
2021-05-17 05:41:32 +02:00
self . assert_length ( created , 3 )
self . assert_length ( existing , 0 )
2019-08-13 20:20:36 +02:00
# Number of recipient ids will increase by 1 and not 3
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-12-02 00:02:25 +01:00
" WHERE recipient_id IN (__[POSTCOMPILE_recipient_id_1]) " ,
2021-02-12 08:19:30 +01:00
)
2019-08-13 20:20:36 +02:00
2024-03-14 19:53:40 +01:00
def test_add_term_using_channels_operator_and_public_operand_negated ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channels " , operand = " public " , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-12-02 00:02:25 +01:00
" WHERE (recipient_id NOT IN (__[POSTCOMPILE_recipient_id_1])) " ,
2021-02-12 08:19:30 +01:00
)
2019-08-13 20:20:36 +02:00
2024-03-14 13:48:06 +01:00
# Add new channels
2024-07-12 02:30:17 +02:00
channel_dicts : list [ StreamDict ] = [
2019-08-13 20:20:36 +02:00
{
2024-03-14 13:48:06 +01:00
" name " : " public-channel " ,
" description " : " Public channel with public history " ,
2019-08-13 20:20:36 +02:00
} ,
{
2024-03-14 13:48:06 +01:00
" name " : " private-channel " ,
" description " : " Private channel with non-public history " ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
" invite_only " : True ,
2019-08-13 20:20:36 +02:00
} ,
{
2024-03-14 13:48:06 +01:00
" name " : " private-channel-with-history " ,
" description " : " Private channel with public history " ,
2019-08-13 20:20:36 +02:00
" invite_only " : True ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
" history_public_to_subscribers " : True ,
} ,
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
]
2021-02-12 08:20:45 +01:00
realm = get_realm ( " zulip " )
2024-03-14 13:48:06 +01:00
created , existing = create_streams_if_needed ( realm , channel_dicts )
2021-05-17 05:41:32 +02:00
self . assert_length ( created , 3 )
self . assert_length ( existing , 0 )
2019-08-13 20:20:36 +02:00
# Number of recipient ids will increase by 1 and not 3
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-12-02 00:02:25 +01:00
" WHERE (recipient_id NOT IN (__[POSTCOMPILE_recipient_id_1])) " ,
2021-02-12 08:19:30 +01:00
)
2019-08-13 20:20:36 +02:00
2023-04-03 13:58:08 +02:00
def test_add_term_using_is_operator_and_dm_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " dm " )
2023-04-03 13:58:08 +02:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) != %(param_1)s " )
def test_add_term_using_is_operator_dm_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " dm " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) = %(param_1)s " )
2016-06-21 21:05:44 +02:00
2023-04-03 13:58:08 +02:00
def test_add_term_using_is_operator_and_non_dm_operand ( self ) - > None :
2021-02-12 08:20:45 +01:00
for operand in [ " starred " , " mentioned " , " alerted " ] :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = operand )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) != %(param_1)s " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_is_operator_and_unread_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " unread " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) = %(param_1)s " )
2017-06-19 03:21:48 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_is_operator_and_unread_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " unread " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) != %(param_1)s " )
2017-06-19 03:21:48 +02:00
2023-04-03 13:58:08 +02:00
def test_add_term_using_is_operator_non_dm_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " starred " , negated = True )
2021-02-12 08:20:45 +01:00
where_clause = " WHERE (flags & %(flags_1)s ) = %(param_1)s "
2017-08-16 15:20:11 +02:00
params = dict (
flags_1 = UserMessage . flags . starred . mask ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
param_1 = 0 ,
2017-08-16 15:20:11 +02:00
)
self . _do_add_term_test ( term , where_clause , params )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " alerted " , negated = True )
2021-02-12 08:20:45 +01:00
where_clause = " WHERE (flags & %(flags_1)s ) = %(param_1)s "
2017-08-16 15:20:11 +02:00
params = dict (
flags_1 = UserMessage . flags . has_alert_word . mask ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
param_1 = 0 ,
2017-08-16 15:20:11 +02:00
)
self . _do_add_term_test ( term , where_clause , params )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " mentioned " , negated = True )
2023-10-19 18:11:53 +02:00
where_clause = " WHERE (flags & %(flags_1)s ) = %(param_1)s "
mention_flags_mask = (
UserMessage . flags . mentioned . mask
2023-11-03 15:20:44 +01:00
| UserMessage . flags . stream_wildcard_mentioned . mask
2023-10-19 18:11:53 +02:00
| UserMessage . flags . topic_wildcard_mentioned . mask
| UserMessage . flags . group_mentioned . mask
)
2017-08-16 15:20:11 +02:00
params = dict (
2023-10-19 18:11:53 +02:00
flags_1 = mention_flags_mask ,
2017-08-16 15:20:11 +02:00
param_1 = 0 ,
)
self . _do_add_term_test ( term , where_clause , params )
2016-06-21 21:05:44 +02:00
2021-07-13 20:23:36 +02:00
def test_add_term_using_is_operator_for_resolved_topics ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " resolved " )
2021-07-13 20:23:36 +02:00
self . _do_add_term_test ( term , " WHERE (subject LIKE %(subject_1)s || ' %% ' " )
def test_add_term_using_is_operator_for_negated_resolved_topics ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " resolved " , negated = True )
2021-07-13 20:23:36 +02:00
self . _do_add_term_test ( term , " WHERE (subject NOT LIKE %(subject_1)s || ' %% ' " )
2023-10-25 19:44:52 +02:00
def test_add_term_using_is_operator_for_followed_topics ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " followed " , negated = False )
2023-10-25 19:44:52 +02:00
self . _do_add_term_test (
term ,
" EXISTS (SELECT 1 \n FROM zerver_usertopic \n WHERE zerver_usertopic.user_profile_id = %(param_1)s AND zerver_usertopic.visibility_policy = %(param_2)s AND upper(zerver_usertopic.topic_name) = upper(zerver_message.subject) AND zerver_usertopic.recipient_id = zerver_message.recipient_id) " ,
)
def test_add_term_using_is_operator_for_negated_followed_topics ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " followed " , negated = True )
2023-10-25 19:44:52 +02:00
self . _do_add_term_test (
term ,
" NOT (EXISTS (SELECT 1 \n FROM zerver_usertopic \n WHERE zerver_usertopic.user_profile_id = %(param_1)s AND zerver_usertopic.visibility_policy = %(param_2)s AND upper(zerver_usertopic.topic_name) = upper(zerver_message.subject) AND zerver_usertopic.recipient_id = zerver_message.recipient_id)) " ,
)
2017-11-05 10:51:25 +01:00
def test_add_term_using_non_supported_operator_should_raise_error ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " non_supported " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_topic_operator_and_lunch_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " topic " , operand = " lunch " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE upper(subject) = upper( %(param_1)s ) " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_topic_operator_lunch_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " topic " , operand = " lunch " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE upper(subject) != upper( %(param_1)s ) " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_topic_operator_and_personal_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " topic " , operand = " personal " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE upper(subject) = upper( %(param_1)s ) " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_topic_operator_personal_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " topic " , operand = " personal " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE upper(subject) != upper( %(param_1)s ) " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_sender_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " sender " , operand = self . othello_email )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE sender_id = %(param_1)s " )
2016-06-21 21:05:44 +02:00
2017-11-19 04:02:03 +01:00
def test_add_term_using_sender_operator_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " sender " , operand = self . othello_email , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE sender_id != %(param_1)s " )
2016-06-21 21:05:44 +02:00
2017-11-19 04:02:03 +01:00
def test_add_term_using_sender_operator_with_non_existing_user_as_operand (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " sender " , operand = " non-existing@zulip.com " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_and_not_the_same_user_as_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = self . othello_email )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s ) " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def test_combined_channel_dm ( self ) - > None :
2024-03-14 15:32:06 +01:00
expected_error_message = (
" Invalid narrow operator: No message can be both a channel message and direct message "
)
2024-06-19 15:47:34 +02:00
term1 = NarrowParameter ( operator = " dm " , operand = self . othello_email )
2023-12-06 20:41:08 +01:00
self . _build_query ( term1 )
2024-06-19 15:47:34 +02:00
topic_term = NarrowParameter ( operator = " topic " , operand = " bogus " )
2024-03-14 15:32:06 +01:00
with self . assertRaises ( BadNarrowOperatorError ) as error :
self . _build_query ( topic_term )
self . assertEqual ( expected_error_message , str ( error . exception ) )
2024-06-19 15:47:34 +02:00
channels_term = NarrowParameter ( operator = " channels " , operand = " public " )
2024-03-14 15:32:06 +01:00
with self . assertRaises ( BadNarrowOperatorError ) as error :
self . _build_query ( channels_term )
self . assertEqual ( expected_error_message , str ( error . exception ) )
2023-12-06 20:41:08 +01:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_not_the_same_user_as_operand_and_negated (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = self . othello_email , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE NOT ((flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s )) " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_the_same_user_as_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = self . hamlet_email )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
2023-09-27 05:17:20 +02:00
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_the_same_user_as_operand_and_negated (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = self . hamlet_email , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
2023-09-27 05:17:20 +02:00
term ,
2023-09-27 06:02:24 +02:00
" WHERE NOT ((flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s ) " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_and_self_and_user_as_operand ( self ) - > None :
2023-07-31 22:04:41 +02:00
myself_and_other = (
f " { self . example_user ( ' hamlet ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2021-02-12 08:19:30 +01:00
)
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = myself_and_other )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s ) " ,
2021-02-12 08:19:30 +01:00
)
narrow: Handle spurious emails in pm-with searches.
If cordelia searches on pm-with:iago@zulip.com,cordelia@zulip.com,
we now properly treat that the same way as pm-with:iago@zulip.com.
Before this fix, the query would initially go through the
huddle code path. The symptom wasn't completely obvious, as
eventually a deeper function would return a recipient id
corresponding to a single PM with @iago@zulip.com, but we would
only get messages where iago was the recipient, and not any
messages where he was the sender to cordelia.
I put the helper function for this in zerver/lib/addressee, which
is somewhat speculative. Eventually, we'll want pm-with queries
to allow for user ids, and I imagine there will be some shared
logic with other Addressee code in terms of how we handle these
strings. The way we deal with lists of emails/users for various
endpoints is kind of haphazard in the current code, although
granted it's mostly just repeating the same simple patterns. It
would be nice for some of this code to converge a bit. This
affects new messages, typing indicators, search filters, etc.,
and some endpoints have strange legacy stuff like supporting
JSON-encoded lists, so it's not trivial to clean this up.
Tweaked by tabbott to add some additional tests.
2018-10-12 17:56:46 +02:00
2024-07-04 14:05:48 +02:00
def test_add_term_using_dm_operator_more_than_one_user_as_operand_no_direct_message_group (
self ,
) - > None :
2024-04-16 00:54:00 +02:00
# If the group doesn't exist, it's a flat false
two_others = f " { self . example_user ( ' cordelia ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = two_others )
2024-04-16 00:54:00 +02:00
self . _do_add_term_test ( term , " WHERE false " )
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_more_than_one_user_as_operand ( self ) - > None :
2024-07-04 14:05:48 +02:00
# Make the direct message group first
get_or_create_direct_message_group (
2024-04-16 00:54:00 +02:00
[
self . example_user ( " hamlet " ) . id ,
self . example_user ( " cordelia " ) . id ,
self . example_user ( " othello " ) . id ,
]
)
2023-07-31 22:04:41 +02:00
two_others = f " { self . example_user ( ' cordelia ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = two_others )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id = %(recipient_id_1)s " )
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_self_and_user_as_operand_and_negated (
2021-02-12 08:19:30 +01:00
self ,
) - > None : # NEGATED
2023-07-31 22:04:41 +02:00
myself_and_other = (
f " { self . example_user ( ' hamlet ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2021-02-12 08:19:30 +01:00
)
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = myself_and_other , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE NOT ((flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s )) " ,
2021-02-12 08:19:30 +01:00
)
narrow: Handle spurious emails in pm-with searches.
If cordelia searches on pm-with:iago@zulip.com,cordelia@zulip.com,
we now properly treat that the same way as pm-with:iago@zulip.com.
Before this fix, the query would initially go through the
huddle code path. The symptom wasn't completely obvious, as
eventually a deeper function would return a recipient id
corresponding to a single PM with @iago@zulip.com, but we would
only get messages where iago was the recipient, and not any
messages where he was the sender to cordelia.
I put the helper function for this in zerver/lib/addressee, which
is somewhat speculative. Eventually, we'll want pm-with queries
to allow for user ids, and I imagine there will be some shared
logic with other Addressee code in terms of how we handle these
strings. The way we deal with lists of emails/users for various
endpoints is kind of haphazard in the current code, although
granted it's mostly just repeating the same simple patterns. It
would be nice for some of this code to converge a bit. This
affects new messages, typing indicators, search filters, etc.,
and some endpoints have strange legacy stuff like supporting
JSON-encoded lists, so it's not trivial to clean this up.
Tweaked by tabbott to add some additional tests.
2018-10-12 17:56:46 +02:00
2024-07-04 14:05:48 +02:00
def test_add_term_using_dm_operator_more_than_one_user_as_operand_no_direct_message_group_and_negated (
2024-04-16 00:54:00 +02:00
self ,
) - > None : # NEGATED
# If the group doesn't exist, it's a flat true
two_others = f " { self . example_user ( ' cordelia ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = two_others , negated = True )
2024-04-16 00:54:00 +02:00
self . _do_add_term_test ( term , " WHERE true " )
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_more_than_one_user_as_operand_and_negated (
2021-02-12 08:19:30 +01:00
self ,
2023-04-03 17:52:19 +02:00
) - > None : # NEGATED
2024-07-04 14:05:48 +02:00
# Make the direct message group first
get_or_create_direct_message_group (
2024-04-16 00:54:00 +02:00
[
self . example_user ( " hamlet " ) . id ,
self . example_user ( " cordelia " ) . id ,
self . example_user ( " othello " ) . id ,
]
)
2023-07-31 22:04:41 +02:00
two_others = f " { self . example_user ( ' cordelia ' ) . email } , { self . example_user ( ' othello ' ) . email } "
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = two_others , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id != %(recipient_id_1)s " )
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_with_comma_noise ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm " , operand = " ,,, ,,, , " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_add_term_using_dm_operator_with_existing_and_non_existing_user_as_operand (
2021-02-12 08:19:30 +01:00
self ,
) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter (
operator = " dm " , operand = self . othello_email + " ,non-existing@zulip.com "
)
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2023-04-05 17:46:43 +02:00
def test_add_term_using_dm_including_operator_with_logged_in_user_email ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm-including " , operand = self . hamlet_email )
2023-04-05 17:46:43 +02:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) != %(param_1)s " )
def test_add_term_using_dm_including_operator_with_different_user_email ( self ) - > None :
# Test without any such group direct messages existing
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm-including " , operand = self . othello_email )
2023-04-05 17:46:43 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s OR recipient_id IN (__[POSTCOMPILE_recipient_id_3])) " ,
2023-04-05 17:46:43 +02:00
)
# Test with at least one such group direct messages existing
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
self . user_profile , [ self . example_user ( " othello " ) , self . example_user ( " cordelia " ) ]
)
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm-including " , operand = self . othello_email )
2023-04-05 17:46:43 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s OR recipient_id IN (__[POSTCOMPILE_recipient_id_3])) " ,
2023-04-05 17:46:43 +02:00
)
def test_add_term_using_dm_including_operator_with_different_user_email_and_negated (
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm-including " , operand = self . othello_email , negated = True )
2023-04-05 17:46:43 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE NOT ((flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND (sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s OR sender_id = %(sender_id_2)s AND recipient_id = %(recipient_id_2)s OR recipient_id IN (__[POSTCOMPILE_recipient_id_3]))) " ,
2023-04-05 17:46:43 +02:00
)
2023-07-17 15:39:05 +02:00
def test_add_term_using_id_operator_integer ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = 555 )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE id = %(param_1)s " )
2016-06-21 21:05:44 +02:00
2023-07-17 15:39:05 +02:00
def test_add_term_using_id_operator_string ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = " 555 " )
2023-07-17 15:39:05 +02:00
self . _do_add_term_test ( term , " WHERE id = %(param_1)s " )
2018-11-07 00:53:02 +01:00
def test_add_term_using_id_operator_invalid ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = " " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2018-11-07 00:53:02 +01:00
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = " notanint " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2018-11-07 00:53:02 +01:00
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = str ( Message . MAX_POSSIBLE_MESSAGE_ID + 1 ) )
2024-05-31 13:25:43 +02:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2017-11-19 04:02:03 +01:00
def test_add_term_using_id_operator_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " id " , operand = 555 , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE id != %(param_1)s " )
2016-06-21 21:05:44 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_add_term_using_search_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " search " , operand = ' " french fries " ' )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-02-12 08:20:45 +01:00
" WHERE (content ILIKE %(content_1)s OR subject ILIKE %(subject_1)s ) AND (search_tsvector @@ plainto_tsquery( %(param_4)s , %(param_5)s )) " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2021-02-12 08:19:30 +01:00
def test_add_term_using_search_operator_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " search " , operand = ' " french fries " ' , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
term ,
2021-02-12 08:20:45 +01:00
" WHERE NOT (content ILIKE %(content_1)s OR subject ILIKE %(subject_1)s ) AND NOT (search_tsvector @@ plainto_tsquery( %(param_4)s , %(param_5)s )) " ,
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = True )
2017-11-05 10:51:25 +01:00
def test_add_term_using_search_operator_pgroonga ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " search " , operand = ' " french fries " ' )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE search_pgroonga &@~ escape_html( %(escape_html_1)s ) " )
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = True )
2021-02-12 08:19:30 +01:00
def test_add_term_using_search_operator_and_negated_pgroonga ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " search " , operand = ' " french fries " ' , negated = True )
2021-02-12 08:19:30 +01:00
self . _do_add_term_test (
2021-02-12 08:20:45 +01:00
term , " WHERE NOT (search_pgroonga &@~ escape_html( %(escape_html_1)s )) "
2021-02-12 08:19:30 +01:00
)
2016-04-24 17:08:51 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_has_operator_and_attachment_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " attachment " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE has_attachment " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_has_operator_attachment_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " attachment " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE NOT has_attachment " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_has_operator_and_image_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " image " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE has_image " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_has_operator_image_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " image " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE NOT has_image " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_has_operator_and_link_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " link " )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE has_link " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
def test_add_term_using_has_operator_link_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " link " , negated = True )
2021-02-12 08:20:45 +01:00
self . _do_add_term_test ( term , " WHERE NOT has_link " )
2016-06-21 21:05:44 +02:00
2024-04-02 06:24:20 +02:00
def test_add_term_using_has_operator_and_reaction_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " reaction " )
2024-04-02 06:24:20 +02:00
self . _do_add_term_test (
term ,
" EXISTS (SELECT 1 \n FROM zerver_reaction \n WHERE zerver_message.id = zerver_reaction.message_id) " ,
)
def test_add_term_using_has_operator_and_reaction_operand_and_negated ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " reaction " , negated = True )
2024-04-02 06:24:20 +02:00
self . _do_add_term_test (
term ,
" NOT (EXISTS (SELECT 1 \n FROM zerver_reaction \n WHERE zerver_message.id = zerver_reaction.message_id)) " ,
)
2017-11-05 10:51:25 +01:00
def test_add_term_using_has_operator_non_supported_operand_should_raise_error ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " has " , operand = " non_supported " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_in_operator ( self ) - > None :
2024-03-14 13:48:06 +01:00
mute_channel ( self . realm , self . user_profile , " Verona " )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " in " , operand = " home " )
2021-12-02 00:02:25 +01:00
self . _do_add_term_test ( term , " WHERE (recipient_id NOT IN (__[POSTCOMPILE_recipient_id_1])) " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_in_operator_and_negated ( self ) - > None :
2016-06-21 21:05:44 +02:00
# negated = True should not change anything
2024-03-14 13:48:06 +01:00
mute_channel ( self . realm , self . user_profile , " Verona " )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " in " , operand = " home " , negated = True )
2021-12-02 00:02:25 +01:00
self . _do_add_term_test ( term , " WHERE (recipient_id NOT IN (__[POSTCOMPILE_recipient_id_1])) " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_in_operator_and_all_operand ( self ) - > None :
2024-03-14 13:48:06 +01:00
mute_channel ( self . realm , self . user_profile , " Verona " )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " in " , operand = " all " )
2016-06-21 21:05:44 +02:00
query = self . _build_query ( term )
2021-02-12 08:20:45 +01:00
self . assertEqual ( get_sqlalchemy_sql ( query ) , " SELECT id \n FROM zerver_message " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_in_operator_all_operand_and_negated ( self ) - > None :
2016-06-21 21:05:44 +02:00
# negated = True should not change anything
2024-03-14 13:48:06 +01:00
mute_channel ( self . realm , self . user_profile , " Verona " )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " in " , operand = " all " , negated = True )
2016-06-21 21:05:44 +02:00
query = self . _build_query ( term )
2021-02-12 08:20:45 +01:00
self . assertEqual ( get_sqlalchemy_sql ( query ) , " SELECT id \n FROM zerver_message " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_in_operator_and_not_defined_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " in " , operand = " not_defined " )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_add_term_using_near_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " near " , operand = " operand " )
2016-06-21 21:05:44 +02:00
query = self . _build_query ( term )
2021-02-12 08:20:45 +01:00
self . assertEqual ( get_sqlalchemy_sql ( query ) , " SELECT id \n FROM zerver_message " )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def test_add_term_non_web_public_channel_in_web_public_query ( self ) - > None :
self . make_stream ( " non-web-public-channel " , realm = self . realm )
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " channel " , operand = " non-web-public-channel " )
2021-08-21 01:07:28 +02:00
builder = NarrowBuilder ( self . user_profile , column ( " id " , Integer ) , self . realm , True )
2020-08-04 19:33:43 +02:00
2024-06-19 15:47:34 +02:00
def _build_query ( term : NarrowParameter ) - > Select :
2020-08-04 19:33:43 +02:00
return builder . add_term ( self . raw_query , term )
2022-11-17 09:30:48 +01:00
self . assertRaises ( BadNarrowOperatorError , _build_query , term )
2020-08-04 19:33:43 +02:00
2023-04-03 13:58:08 +02:00
# Test "is:private" (legacy alias for "is:dm")
def test_add_term_using_is_operator_and_private_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " private " )
2023-04-03 13:58:08 +02:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) != %(param_1)s " )
def test_add_term_using_is_operator_private_operand_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " is " , operand = " private " , negated = True )
2023-04-03 13:58:08 +02:00
self . _do_add_term_test ( term , " WHERE (flags & %(flags_1)s ) = %(param_1)s " )
2023-04-03 17:52:19 +02:00
# Test that "pm-with" (legacy alias for "dm") works.
def test_add_term_using_pm_with_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " pm-with " , operand = self . hamlet_email )
2023-04-03 17:52:19 +02:00
self . _do_add_term_test (
2023-09-27 05:17:20 +02:00
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s " ,
2023-04-03 17:52:19 +02:00
)
2023-03-24 15:12:17 +01:00
# Test that the underscore version of "pm-with" works.
def test_add_term_using_underscore_version_of_pm_with_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " pm_with " , operand = self . hamlet_email )
2023-03-24 15:12:17 +01:00
self . _do_add_term_test (
2023-09-27 05:17:20 +02:00
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND sender_id = %(sender_id_1)s AND recipient_id = %(recipient_id_1)s " ,
2023-03-24 15:12:17 +01:00
)
2023-04-05 17:46:43 +02:00
# Test that deprecated "group-pm-with" (replaced by "dm-including" ) works.
def test_add_term_using_dm_including_operator_with_non_existing_user ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " dm-including " , operand = " non-existing@zulip.com " )
2023-04-05 17:46:43 +02:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
def test_add_term_using_group_pm_operator_and_not_the_same_user_as_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " group-pm-with " , operand = self . othello_email )
2023-09-27 05:17:20 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND recipient_id IN (__[POSTCOMPILE_recipient_id_1]) " ,
2023-09-27 05:17:20 +02:00
)
2023-04-05 17:46:43 +02:00
def test_add_term_using_group_pm_operator_not_the_same_user_as_operand_and_negated (
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " group-pm-with " , operand = self . othello_email , negated = True )
2023-09-27 05:17:20 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE NOT ((flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND recipient_id IN (__[POSTCOMPILE_recipient_id_1])) " ,
2023-09-27 05:17:20 +02:00
)
2023-04-05 17:46:43 +02:00
def test_add_term_using_group_pm_operator_with_non_existing_user_as_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " group-pm-with " , operand = " non-existing@zulip.com " )
2023-04-05 17:46:43 +02:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2023-03-24 15:12:17 +01:00
# Test that the underscore version of "group-pm-with" works.
def test_add_term_using_underscore_version_of_group_pm_with_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " group_pm_with " , operand = self . othello_email )
2023-09-27 05:17:20 +02:00
self . _do_add_term_test (
term ,
2023-09-27 06:02:24 +02:00
" WHERE (flags & %(flags_1)s ) != %(param_1)s AND realm_id = %(realm_id_1)s AND recipient_id IN (__[POSTCOMPILE_recipient_id_1]) " ,
2023-09-27 05:17:20 +02:00
)
2023-03-24 15:12:17 +01:00
2024-03-14 16:29:27 +01:00
# Test that "stream" (legacy alias for "channel" operator) works.
def test_add_term_using_stream_operator ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " stream " , operand = " Scotland " )
2024-03-14 16:29:27 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id = %(recipient_id_1)s " )
def test_add_term_using_stream_operator_and_negated ( self ) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " stream " , operand = " Scotland " , negated = True )
2024-03-14 16:29:27 +01:00
self . _do_add_term_test ( term , " WHERE recipient_id != %(recipient_id_1)s " )
def test_add_term_using_stream_operator_and_non_existing_operand_should_raise_error (
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " stream " , operand = " non-existing-channel " )
2024-03-14 16:29:27 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
2024-03-14 19:53:40 +01:00
# Test that "streams" (legacy alias for "channels" operator) works.
def test_add_term_using_streams_operator_and_invalid_operand_should_raise_error (
self ,
) - > None : # NEGATED
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " streams " , operand = " invalid_operands " )
2024-03-14 19:53:40 +01:00
self . assertRaises ( BadNarrowOperatorError , self . _build_query , term )
def test_add_term_using_streams_operator_and_public_operand ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " streams " , operand = " public " )
2024-03-14 19:53:40 +01:00
self . _do_add_term_test (
term ,
" WHERE recipient_id IN (__[POSTCOMPILE_recipient_id_1]) " ,
)
def test_add_term_using_streams_operator_and_public_operand_negated ( self ) - > None :
2024-06-19 15:47:34 +02:00
term = NarrowParameter ( operator = " streams " , operand = " public " , negated = True )
2024-03-14 19:53:40 +01:00
self . _do_add_term_test (
term ,
" WHERE (recipient_id NOT IN (__[POSTCOMPILE_recipient_id_1])) " ,
)
2021-02-12 08:19:30 +01:00
def _do_add_term_test (
2024-07-12 02:30:23 +02:00
self , term : NarrowParameter , where_clause : str , params : dict [ str , Any ] | None = None
2021-02-12 08:19:30 +01:00
) - > None :
2017-08-16 15:20:11 +02:00
query = self . _build_query ( term )
if params is not None :
2019-08-28 22:35:22 +02:00
actual_params = get_sqlalchemy_query_params ( query )
2017-08-16 15:20:11 +02:00
self . assertEqual ( actual_params , params )
2019-08-28 22:35:22 +02:00
self . assertIn ( where_clause , get_sqlalchemy_sql ( query ) )
2016-06-21 21:05:44 +02:00
2024-06-19 15:47:34 +02:00
def _build_query ( self , term : NarrowParameter ) - > Select :
2016-06-21 21:05:44 +02:00
return self . builder . add_term ( self . raw_query , term )
2021-02-12 08:19:30 +01:00
2020-07-01 04:19:54 +02:00
class NarrowLibraryTest ( ZulipTestCase ) :
2023-06-29 22:41:44 +02:00
def test_build_narrow_predicate ( self ) - > None :
2024-03-14 16:29:27 +01:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " channel " , operand = " devel " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " display_recipient " : " devel " , " type " : " stream " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " display_recipient " : " social " , " type " : " stream " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " topic " , operand = " bark " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " subject " : " BarK " } ,
flags = [ ] ,
)
)
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " topic " : " bark " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " subject " : " play with tail " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " topic " : " play with tail " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate (
2023-06-28 20:50:39 +02:00
[
2024-03-14 16:29:27 +01:00
NarrowTerm ( operator = " channel " , operand = " devel " ) ,
2023-06-28 20:50:39 +02:00
NarrowTerm ( operator = " topic " , operand = " python " ) ,
]
)
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " display_recipient " : " devel " , " type " : " stream " , " subject " : " python " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " display_recipient " : " devel " , " type " : " stream " , " subject " : " java " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " display_recipient " : " social " , " type " : " stream " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate (
2023-06-28 20:50:39 +02:00
[ NarrowTerm ( operator = " sender " , operand = " hamlet@zulip.com " ) ]
)
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " sender_email " : " hamlet@zulip.com " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " sender_email " : " cordelia@zulip.com " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " dm " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " private " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " } ,
flags = [ ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " starred " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " starred " ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " alerted " ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " alerted " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " mentioned " ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " starred " ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " mentioned " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " mentioned " ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " starred " ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " unread " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { } ,
flags = [ " read " ] ,
)
)
###
2023-06-29 22:41:44 +02:00
narrow_predicate = build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " resolved " ) ] )
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
self . assertTrue (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " subject " : " ✔ python " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " private " } ,
flags = [ ] ,
)
)
self . assertFalse (
2023-06-29 22:41:44 +02:00
narrow_predicate (
tests: Eliminate narrow.json fixture.
In 2484d870b41e2a385919bcd2c359d7a5baf9d7 I created tests
using a fixture called narrow.json. I believe my intention
was to eventually use the fixture for similar tests on the
frontend, but that never happened.
Almost seven years later, I think it's time to just use
straightforward code in Python to test build_narrow_filter.
In particular, we want to move to dataclasses, so that would
create an addition nuisance for fixture-based tests. The
fixture was already annoying in terms of being an extra moving
part, being hard to read, and not being type-safe.
In order to avoid typos, I mostly code-generated the new
Python code by instrumenting the old test:
narrow_filter = build_narrow_filter(narrow)
+ print("###\n")
+ print(f"narrow_filter = build_narrow_filter({narrow})\n")
for e in accept_events:
message = e["message"]
flags = e["flags"]
@@ -610,6 +612,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertTrue(narrow_filter(message=message, flags=flags))
+ print(f"self.assertTrue(narrow_filter(message={message}, flags={flags},))")
+ print()
for e in reject_events:
message = e["message"]
flags = e["flags"]
@@ -618,6 +622,8 @@ class NarrowLibraryTest(ZulipTestCase):
if flags is None:
flags = []
self.assertFalse(narrow_filter(message=message, flags=flags))
+ print(f"self.assertFalse(narrow_filter(message={message}, flags={flags},))")
+ print()
I then basically pasted the output in and ran black to format it.
2023-06-28 19:59:22 +02:00
message = { " type " : " stream " , " subject " : " java " } ,
flags = [ ] ,
)
)
2016-07-16 22:56:33 +02:00
2023-06-29 22:41:44 +02:00
def test_build_narrow_predicate_invalid ( self ) - > None :
2017-03-05 08:56:57 +01:00
with self . assertRaises ( JsonableError ) :
2023-06-29 22:41:44 +02:00
build_narrow_predicate ( [ NarrowTerm ( operator = " invalid_operator " , operand = " operand " ) ] )
2023-10-25 19:44:52 +02:00
with self . assertRaises ( JsonableError ) :
build_narrow_predicate ( [ NarrowTerm ( operator = " is " , operand = " followed " ) ] )
2017-03-05 08:56:57 +01:00
2021-09-04 04:03:07 +02:00
def test_is_spectator_compatible ( self ) - > None :
self . assertTrue ( is_spectator_compatible ( [ ] ) )
2024-06-19 15:47:34 +02:00
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " has " , operand = " attachment " ) ] )
)
self . assertTrue ( is_spectator_compatible ( [ NarrowParameter ( operator = " has " , operand = " image " ) ] ) )
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " search " , operand = " magic " ) ] )
)
self . assertTrue ( is_spectator_compatible ( [ NarrowParameter ( operator = " near " , operand = " 15 " ) ] ) )
2021-02-12 08:19:30 +01:00
self . assertTrue (
2021-09-04 04:03:07 +02:00
is_spectator_compatible (
2024-06-19 15:47:34 +02:00
[
NarrowParameter ( operator = " id " , operand = " 15 " ) ,
NarrowParameter ( operator = " has " , operand = " attachment " ) ,
]
2021-02-12 08:19:30 +01:00
)
)
self . assertTrue (
2024-06-19 15:47:34 +02:00
is_spectator_compatible (
[ NarrowParameter ( operator = " sender " , operand = " hamlet@zulip.com " ) ]
)
2021-02-12 08:19:30 +01:00
)
self . assertFalse (
2024-06-19 15:47:34 +02:00
is_spectator_compatible ( [ NarrowParameter ( operator = " dm " , operand = " hamlet@zulip.com " ) ] )
2021-02-12 08:19:30 +01:00
)
self . assertFalse (
2024-06-19 15:47:34 +02:00
is_spectator_compatible (
[ NarrowParameter ( operator = " dm-including " , operand = " hamlet@zulip.com " ) ]
)
)
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " channel " , operand = " Denmark " ) ] )
2021-02-12 08:19:30 +01:00
)
self . assertTrue (
2021-09-04 04:03:07 +02:00
is_spectator_compatible (
2021-02-12 08:19:30 +01:00
[
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " Denmark " ) ,
NarrowParameter ( operator = " topic " , operand = " logic " ) ,
2021-02-12 08:19:30 +01:00
]
)
)
2024-06-19 15:47:34 +02:00
self . assertFalse (
is_spectator_compatible ( [ NarrowParameter ( operator = " is " , operand = " starred " ) ] )
)
self . assertFalse ( is_spectator_compatible ( [ NarrowParameter ( operator = " is " , operand = " dm " ) ] ) )
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " channels " , operand = " public " ) ] )
)
2018-05-21 17:44:00 +02:00
2023-04-03 13:58:08 +02:00
# "is:private" is a legacy alias for "is:dm".
2024-06-19 15:47:34 +02:00
self . assertFalse (
is_spectator_compatible ( [ NarrowParameter ( operator = " is " , operand = " private " ) ] )
)
2023-04-03 17:52:19 +02:00
# "pm-with:"" is a legacy alias for "dm:"
self . assertFalse (
2024-06-19 15:47:34 +02:00
is_spectator_compatible (
[ NarrowParameter ( operator = " pm-with " , operand = " hamlet@zulip.com " ) ]
)
2023-04-03 17:52:19 +02:00
)
2023-04-05 17:46:43 +02:00
# "group-pm-with:" was deprecated by the addition of "dm-including:"
self . assertFalse (
2024-06-19 15:47:34 +02:00
is_spectator_compatible (
[ NarrowParameter ( operator = " group-pm-with " , operand = " hamlet@zulip.com " ) ]
)
2023-04-05 17:46:43 +02:00
)
2024-03-14 16:29:27 +01:00
# "stream" is a legacy alias for "channel" operator
2024-06-19 15:47:34 +02:00
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " stream " , operand = " Denmark " ) ] )
)
2024-03-14 16:29:27 +01:00
self . assertTrue (
is_spectator_compatible (
[
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " stream " , operand = " Denmark " ) ,
NarrowParameter ( operator = " topic " , operand = " logic " ) ,
2024-03-14 16:29:27 +01:00
]
)
)
2024-03-14 19:53:40 +01:00
# "streams" is a legacy alias for "channels" operator
2024-06-19 15:47:34 +02:00
self . assertTrue (
is_spectator_compatible ( [ NarrowParameter ( operator = " streams " , operand = " public " ) ] )
)
2023-04-03 13:58:08 +02:00
2021-02-12 08:19:30 +01:00
2016-08-23 02:08:42 +02:00
class IncludeHistoryTest ( ZulipTestCase ) :
2017-11-05 10:51:25 +01:00
def test_ok_to_include_history ( self ) - > None :
2018-04-05 00:06:53 +02:00
user_profile = self . example_user ( " hamlet " )
2024-03-14 13:48:06 +01:00
self . make_stream ( " public_channel " , realm = user_profile . realm )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
# Negated channel searches should not include history.
2016-06-21 21:05:44 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " public_channel " , negated = True ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
2024-03-14 19:53:40 +01:00
# channels:public searches should include history for non-guest members.
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertTrue ( ok_to_include_history ( narrow , user_profile , False ) )
2019-08-13 20:20:36 +02:00
2024-03-14 19:53:40 +01:00
# Negated -channels:public searches should not include history.
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " , negated = True ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2019-08-13 20:20:36 +02:00
2024-03-14 13:48:06 +01:00
# Definitely forbid seeing history on private channels.
self . make_stream ( " private_channel " , realm = user_profile . realm , invite_only = True )
2018-04-05 00:28:14 +02:00
subscribed_user_profile = self . example_user ( " cordelia " )
2024-03-14 13:48:06 +01:00
self . subscribe ( subscribed_user_profile , " private_channel " )
2016-06-21 21:05:44 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " private_channel " ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
# Verify that with history_public_to_subscribers, subscribed
2018-04-27 01:00:26 +02:00
# users can access history.
2021-02-12 08:19:30 +01:00
self . make_stream (
2024-03-14 13:48:06 +01:00
" private_channel_2 " ,
2021-02-12 08:19:30 +01:00
realm = user_profile . realm ,
invite_only = True ,
history_public_to_subscribers = True ,
)
2018-04-27 01:00:26 +02:00
subscribed_user_profile = self . example_user ( " cordelia " )
2024-03-14 13:48:06 +01:00
self . subscribe ( subscribed_user_profile , " private_channel_2 " )
2018-04-27 01:00:26 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " private_channel_2 " ) ,
2018-04-27 01:00:26 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
self . assertTrue ( ok_to_include_history ( narrow , subscribed_user_profile , False ) )
2018-04-05 00:28:14 +02:00
2023-04-03 13:58:08 +02:00
# History doesn't apply to direct messages.
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " is " , operand = " dm " ) ,
2023-04-03 13:58:08 +02:00
]
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
# "is:private" is a legacy alias for "is:dm".
2016-06-21 21:05:44 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " is " , operand = " private " ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
2017-06-19 03:21:48 +02:00
# History doesn't apply to unread messages.
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " is " , operand = " unread " ) ,
2017-06-19 03:21:48 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2017-06-19 03:21:48 +02:00
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 = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " public_channel " ) ,
NarrowParameter ( operator = " is " , operand = " starred " ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
2019-08-13 20:20:36 +02:00
# No point in searching history for is operator even if included with
2024-03-14 19:53:40 +01:00
# channels:public
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
NarrowParameter ( operator = " is " , operand = " mentioned " ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
NarrowParameter ( operator = " is " , operand = " unread " ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
NarrowParameter ( operator = " is " , operand = " alerted " ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , user_profile , False ) )
2021-07-13 20:23:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
NarrowParameter ( operator = " is " , operand = " resolved " ) ,
2021-07-13 20:23:36 +02:00
]
2024-05-20 23:37:08 +02:00
self . assertTrue ( ok_to_include_history ( narrow , user_profile , False ) )
2019-08-13 20:20:36 +02:00
2016-06-21 21:05:44 +02:00
# simple True case
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " public_channel " ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertTrue ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " public_channel " ) ,
NarrowParameter ( operator = " topic " , operand = " whatever " ) ,
NarrowParameter ( operator = " search " , operand = " needle in haystack " ) ,
2016-06-21 21:05:44 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertTrue ( ok_to_include_history ( narrow , user_profile , False ) )
2016-06-21 21:05:44 +02:00
2018-05-02 17:00:06 +02:00
# Tests for guest user
guest_user_profile = self . example_user ( " polonius " )
# Using 'Cordelia' to compare between a guest and a normal user
subscribed_user_profile = self . example_user ( " cordelia " )
2024-03-14 19:53:40 +01:00
# channels:public searches should not include history for guest members.
2019-08-13 20:20:36 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channels " , operand = " public " ) ,
2019-08-13 20:20:36 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , guest_user_profile , False ) )
2019-08-13 20:20:36 +02:00
2024-03-14 13:48:06 +01:00
# Guest user can't access public channel
self . subscribe ( subscribed_user_profile , " public_channel_2 " )
2018-05-02 17:00:06 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " public_channel_2 " ) ,
2018-05-02 17:00:06 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , guest_user_profile , False ) )
self . assertTrue ( ok_to_include_history ( narrow , subscribed_user_profile , False ) )
2018-05-02 17:00:06 +02:00
2024-03-14 13:48:06 +01:00
# Definitely, a guest user can't access the unsubscribed private channel
self . subscribe ( subscribed_user_profile , " private_channel_3 " )
2018-05-02 17:00:06 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " private_channel_3 " ) ,
2018-05-02 17:00:06 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertFalse ( ok_to_include_history ( narrow , guest_user_profile , False ) )
self . assertTrue ( ok_to_include_history ( narrow , subscribed_user_profile , False ) )
2018-05-02 17:00:06 +02:00
2024-03-14 13:48:06 +01:00
# Guest user can access (history of) subscribed private channels
self . subscribe ( guest_user_profile , " private_channel_4 " )
self . subscribe ( subscribed_user_profile , " private_channel_4 " )
2018-05-02 17:00:06 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " private_channel_4 " ) ,
2018-05-02 17:00:06 +02:00
]
2020-08-04 19:33:43 +02:00
self . assertTrue ( ok_to_include_history ( narrow , guest_user_profile , False ) )
self . assertTrue ( ok_to_include_history ( narrow , subscribed_user_profile , False ) )
2018-05-02 17:00:06 +02:00
2021-02-12 08:19:30 +01:00
2018-03-15 11:20:55 +01:00
class PostProcessTest ( ZulipTestCase ) :
def test_basics ( self ) - > None :
2021-02-12 08:19:30 +01:00
def verify (
2024-07-12 02:30:17 +02:00
in_ids : list [ int ] ,
2021-02-12 08:19:30 +01:00
num_before : int ,
num_after : int ,
first_visible_message_id : int ,
anchor : int ,
anchored_to_left : bool ,
anchored_to_right : bool ,
2024-07-12 02:30:17 +02:00
out_ids : list [ int ] ,
2021-02-12 08:19:30 +01:00
found_anchor : bool ,
found_oldest : bool ,
found_newest : bool ,
history_limited : bool ,
) - > None :
2018-03-15 11:20:55 +01:00
in_rows = [ [ row_id ] for row_id in in_ids ]
out_rows = [ [ row_id ] for row_id in out_ids ]
info = post_process_limited_query (
rows = in_rows ,
num_before = num_before ,
num_after = num_after ,
anchor = anchor ,
anchored_to_left = anchored_to_left ,
anchored_to_right = anchored_to_right ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = first_visible_message_id ,
2018-03-15 11:20:55 +01:00
)
2022-11-10 00:52:13 +01:00
self . assertEqual ( info . rows , out_rows )
self . assertEqual ( info . found_anchor , found_anchor )
self . assertEqual ( info . found_newest , found_newest )
self . assertEqual ( info . found_oldest , found_oldest )
self . assertEqual ( info . history_limited , history_limited )
2018-03-15 11:20:55 +01:00
2018-09-19 14:23:02 +02:00
# typical 2-sided query, with a bunch of tests for different
# values of first_visible_message_id.
2018-03-15 11:20:55 +01:00
anchor = 10
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 8 , 9 , 10 , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 8 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 8 , 9 , 10 , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 9 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 9 , 10 , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 10 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 10 , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 11 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 12 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 12 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = True ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 8 , 9 , anchor , 11 , 12 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 13 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = True ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# typical 2-sided query missing anchor and grabbing an extra row
anchor = 10
verify (
in_ids = [ 7 , 9 , 11 , 13 , 15 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2018-03-15 11:20:55 +01:00
out_ids = [ 7 , 9 , 11 , 13 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 7 , 9 , 11 , 13 , 15 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 10 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 11 , 13 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 7 , 9 , 11 , 13 , 15 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 9 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 9 , 11 , 13 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# 2-sided query with old anchor
anchor = 100
verify (
in_ids = [ 50 , anchor , 150 , 200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 50 , 100 , 150 , 200 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 50 , anchor , 150 , 200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 100 , 150 , 200 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# 2-sided query with new anchor
anchor = 900
verify (
in_ids = [ 700 , 800 , anchor , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 700 , 800 , 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = True ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 700 , 800 , anchor , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = True ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# left-sided query with old anchor
anchor = 100
verify (
in_ids = [ 50 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 50 , 100 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 50 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 100 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# left-sided query with new anchor
anchor = 900
verify (
in_ids = [ 700 , 800 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 700 , 800 , 900 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 700 , 800 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 900 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# left-sided query with new anchor and extra row
anchor = 900
verify (
in_ids = [ 600 , 700 , 800 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 700 , 800 , 900 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 600 , 700 , 800 , anchor ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 900 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = True ,
found_newest = False ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# left-sided query anchored to the right
2020-07-05 01:03:01 +02:00
anchor = LARGER_THAN_MAX_MESSAGE_ID
2018-03-15 11:20:55 +01:00
verify (
in_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = True ,
2018-03-15 11:20:55 +01:00
out_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = True ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 1000 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = True ,
2018-09-19 14:23:02 +02:00
out_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = True ,
history_limited = True ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 2 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 1100 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = True ,
2018-09-19 14:23:02 +02:00
out_ids = [ ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = True ,
found_newest = True ,
history_limited = True ,
2018-03-15 11:20:55 +01:00
)
# right-sided query with old anchor
anchor = 100
verify (
in_ids = [ anchor , 200 , 300 , 400 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 100 , 200 , 300 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ anchor , 200 , 300 , 400 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 100 , 200 , 300 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ anchor , 200 , 300 , 400 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 300 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 300 , 400 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
2018-09-19 14:23:02 +02:00
# BUG: history_limited should be False here.
2021-02-12 08:19:30 +01:00
found_newest = False ,
history_limited = False ,
2018-03-15 11:20:55 +01:00
)
# right-sided query with new anchor
anchor = 900
verify (
in_ids = [ anchor , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = True ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ anchor , 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 900 , 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = True ,
history_limited = False ,
2018-03-15 11:20:55 +01:00
)
# right-sided query with non-matching anchor
anchor = 903
verify (
in_ids = [ 1000 , 1100 , 1200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 1000 , 1100 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 1000 , 1100 , 1200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 1000 , 1100 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 1000 , 1100 , 1200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 1000 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 1000 , 1100 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 1000 , 1100 , 1200 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 2 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 1100 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 1100 , 1200 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
2018-09-19 14:23:02 +02:00
# BUG: history_limited should be False here.
2021-02-12 08:19:30 +01:00
found_newest = False ,
history_limited = False ,
2018-03-15 11:20:55 +01:00
)
# targeted query that finds row
anchor = 1000
verify (
in_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = anchor ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
found_anchor = True ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-09-19 14:23:02 +02:00
)
verify (
in_ids = [ 1000 ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 1100 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-09-19 14:23:02 +02:00
out_ids = [ ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-03-15 11:20:55 +01:00
)
# targeted query that finds nothing
anchor = 903
verify (
in_ids = [ ] ,
2021-02-12 08:19:30 +01:00
num_before = 0 ,
num_after = 0 ,
2018-09-19 14:23:02 +02:00
first_visible_message_id = 0 ,
2021-02-12 08:19:30 +01:00
anchor = anchor ,
anchored_to_left = False ,
anchored_to_right = False ,
2018-03-15 11:20:55 +01:00
out_ids = [ ] ,
2021-02-12 08:19:30 +01:00
found_anchor = False ,
found_oldest = False ,
found_newest = False ,
history_limited = False ,
2018-03-15 11:20:55 +01:00
)
2016-06-21 21:05:44 +02:00
2021-02-12 08:19:30 +01:00
class GetOldMessagesTest ( ZulipTestCase ) :
def get_and_check_messages (
2024-07-12 02:30:23 +02:00
self , modified_params : dict [ str , str | int ] , * * kwargs : Any
2024-07-12 02:30:17 +02:00
) - > dict [ str , Any ] :
2024-07-12 02:30:23 +02:00
post_params : dict [ str , str | int ] = { " anchor " : 1 , " num_before " : 1 , " num_after " : 1 }
2016-06-21 21:05:44 +02:00
post_params . update ( modified_params )
2021-02-12 08:19:30 +01:00
payload = self . client_get ( " /json/messages " , dict ( post_params ) , * * kwargs )
2016-07-24 16:50:30 +02:00
self . assert_json_success ( payload )
2021-02-12 08:19:30 +01:00
self . assertEqual (
set ( payload [ " Cache-Control " ] . split ( " , " ) ) ,
{ " must-revalidate " , " no-store " , " no-cache " , " max-age=0 " , " private " } ,
)
2019-10-02 00:10:30 +02:00
2020-08-07 01:09:47 +02:00
result = orjson . 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 " ] :
2021-02-12 08:19:30 +01:00
for field in (
" content " ,
" content_type " ,
" display_recipient " ,
" avatar_url " ,
" recipient_id " ,
" sender_full_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
2021-02-12 08:19:30 +01:00
def message_visibility_test (
2024-07-12 02:30:17 +02:00
self , narrow : list [ dict [ str , str ] ] , message_ids : list [ int ] , pivot_index : int
2021-02-12 08:19:30 +01:00
) - > None :
2018-03-15 11:21:36 +01:00
num_before = len ( message_ids )
search: Make `num_after`/`num_after` more consistent.
We now consistently set our query limits so that we get at
least `num_after` rows such that id > anchor. (Obviously, the
caveat is that if there aren't enough rows that fulfill the
query, we'll return the full set of rows, but that may be less
than `num_after`.) Likewise for `num_before`.
Before this change, we would sometimes return one too few rows
for narrow queries.
Now, we're still a bit broken, but in a more consistent way. If
we have a query that does not match the anchor row (which could
be true even for a non-narrow query), but which does match lots
of rows after the anchor, we'll return `num_after + 1` rows
on the right hand side, whether or not the query has narrow
parameters.
The off-by-one semantics here have probably been moot all along,
since our windows are approximate to begin with. If we set
num_after to 100, its just a rough performance optimization to
begin with, so it doesn't matter whether we return 99 or 101 rows,
as long as we set the anchor correctly on the subsequent query.
We will make the results more rigorous in a follow up commit.
2018-03-14 13:22:16 +01:00
2021-02-12 08:19:30 +01:00
post_params = dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
num_before = num_before ,
num_after = 0 ,
anchor = LARGER_THAN_MAX_MESSAGE_ID ,
)
2018-01-02 18:33:28 +01:00
payload = self . client_get ( " /json/messages " , dict ( post_params ) )
self . assert_json_success ( payload )
2020-08-07 01:09:47 +02:00
result = orjson . loads ( payload . content )
2018-01-02 18:33:28 +01:00
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , len ( message_ids ) )
2018-01-02 18:33:28 +01:00
for message in result [ " messages " ] :
2021-02-12 08:19:30 +01:00
assert message [ " id " ] in message_ids
2018-01-02 18:33:28 +01:00
2020-09-03 05:32:15 +02:00
post_params . update ( num_before = len ( message_ids [ pivot_index : ] ) )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ pivot_index ] ) :
2018-01-02 18:33:28 +01:00
payload = self . client_get ( " /json/messages " , dict ( post_params ) )
2018-03-15 11:58:25 +01:00
2018-01-02 18:33:28 +01:00
self . assert_json_success ( payload )
2020-08-07 01:09:47 +02:00
result = orjson . loads ( payload . content )
2018-01-02 18:33:28 +01:00
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , len ( message_ids [ pivot_index : ] ) )
2018-01-02 18:33:28 +01:00
for message in result [ " messages " ] :
2021-02-12 08:19:30 +01:00
assert message [ " id " ] in message_ids
2018-01-02 18:33:28 +01:00
2024-07-12 02:30:23 +02:00
def get_query_ids ( self ) - > dict [ str , int | str ] :
2021-02-12 08:20:45 +01:00
hamlet_user = self . example_user ( " hamlet " )
othello_user = self . example_user ( " othello " )
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:23 +02:00
query_ids : dict [ str , int | str ] = { }
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
scotland_channel = get_stream ( " Scotland " , hamlet_user . realm )
assert scotland_channel . recipient_id is not None
2022-05-31 01:27:38 +02:00
assert hamlet_user . recipient_id is not None
assert othello_user . recipient_id is not None
2023-09-27 05:17:20 +02:00
query_ids [ " realm_id " ] = hamlet_user . realm_id
2024-03-14 13:48:06 +01:00
query_ids [ " scotland_recipient " ] = scotland_channel . recipient_id
2021-02-12 08:20:45 +01:00
query_ids [ " hamlet_id " ] = hamlet_user . id
query_ids [ " othello_id " ] = othello_user . id
query_ids [ " hamlet_recipient " ] = hamlet_user . recipient_id
query_ids [ " othello_recipient " ] = othello_user . recipient_id
2021-02-12 08:19:30 +01:00
recipients = (
get_public_streams_queryset ( hamlet_user . realm )
. values_list ( " recipient_id " , flat = True )
2021-02-12 08:20:45 +01:00
. order_by ( " id " )
2021-02-12 08:19:30 +01:00
)
2024-03-14 13:48:06 +01:00
query_ids [ " public_channels_recipients " ] = " , " . join ( str ( r ) for r in recipients )
2016-06-21 21:05:44 +02:00
return query_ids
2022-07-14 02:58:20 +02:00
def check_unauthenticated_response (
self , result : " TestHttpResponse " , www_authenticate : str = ' Session realm= " zulip " '
) - > None :
"""
In ` JsonErrorHandler ` , we convert ` MissingAuthenticationError ` into responses with ` WWW - Authenticate `
set depending on which endpoint encounters the error .
This verifies the status code as well as the value of the set header .
` www_authenticate ` should be ` Basic realm = " zulip " ` for paths starting with " /api " , and
` Session realm = " zulip " ` otherwise .
"""
self . assert_json_error (
result , " Not logged in: API authentication or user session required " , status_code = 401
)
self . assertEqual ( result [ " WWW-Authenticate " ] , www_authenticate )
2017-11-05 10:51:25 +01:00
def test_content_types ( self ) - > None :
2017-10-21 03:25:39 +02:00
"""
Test old ` / json / messages ` returns reactions .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2017-10-21 03:25:39 +02:00
2018-05-10 19:00:29 +02:00
def get_content_type ( apply_markdown : bool ) - > str :
2024-07-12 02:30:17 +02:00
req : dict [ str , Any ] = dict (
2020-08-07 01:09:47 +02:00
apply_markdown = orjson . dumps ( apply_markdown ) . decode ( ) ,
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
)
2017-10-21 03:25:39 +02:00
result = self . get_and_check_messages ( req )
2021-02-12 08:20:45 +01:00
message = result [ " messages " ] [ 0 ]
return message [ " content_type " ]
2017-10-21 03:25:39 +02:00
self . assertEqual (
get_content_type ( apply_markdown = False ) ,
2021-02-12 08:20:45 +01:00
" text/x-markdown " ,
2017-10-21 03:25:39 +02:00
)
self . assertEqual (
get_content_type ( apply_markdown = True ) ,
2021-02-12 08:20:45 +01:00
" text/html " ,
2017-10-21 03:25:39 +02:00
)
2017-11-05 10:51:25 +01:00
def test_successful_get_messages_reaction ( self ) - > None :
2016-12-06 07:19:34 +01:00
"""
Test old ` / json / messages ` returns reactions .
"""
2021-04-29 17:22:48 +02:00
self . send_stream_message ( self . example_user ( " iago " ) , " Verona " )
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2021-04-29 17:22:48 +02:00
2024-07-12 02:30:23 +02:00
get_messages_params : dict [ str , int | str ] = { " anchor " : " newest " , " num_before " : 1 }
2021-04-29 17:22:48 +02:00
messages = self . get_and_check_messages ( get_messages_params ) [ " messages " ]
self . assert_length ( messages , 1 )
message_id = messages [ 0 ] [ " id " ]
self . assert_length ( messages [ 0 ] [ " reactions " ] , 0 )
2016-12-06 07:19:34 +01:00
2021-02-12 08:20:45 +01:00
self . login ( " othello " )
reaction_name = " thumbs_up "
2019-10-10 19:03:09 +02:00
reaction_info = {
2021-02-12 08:20:45 +01:00
" emoji_name " : reaction_name ,
2019-10-10 19:03:09 +02:00
}
2016-12-06 07:19:34 +01:00
2021-02-12 08:20:45 +01:00
url = f " /json/messages/ { message_id } /reactions "
2019-10-10 19:03:09 +02:00
payload = self . client_post ( url , reaction_info )
2016-12-06 07:19:34 +01:00
self . assert_json_success ( payload )
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2021-04-29 17:22:48 +02:00
messages = self . get_and_check_messages ( get_messages_params ) [ " messages " ]
self . assert_length ( messages , 1 )
self . assertEqual ( messages [ 0 ] [ " id " ] , message_id )
self . assert_length ( messages [ 0 ] [ " reactions " ] , 1 )
self . assertEqual ( messages [ 0 ] [ " reactions " ] [ 0 ] [ " emoji_name " ] , reaction_name )
2016-12-06 07:19:34 +01:00
2017-11-05 10:51:25 +01:00
def test_successful_get_messages ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
A call to GET / json / messages with valid parameters returns a list of
messages .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2020-09-02 08:14:51 +02:00
self . get_and_check_messages ( { } )
2016-06-21 21:05:44 +02:00
2021-02-12 08:20:45 +01:00
othello_email = self . example_user ( " othello " ) . email
2020-03-12 14:17:25 +01:00
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.
2020-03-12 14:17:25 +01:00
self . get_and_check_messages (
dict (
2020-08-07 01:09:47 +02:00
narrow = orjson . dumps (
2023-04-03 17:52:19 +02:00
[ [ " dm " , othello_email ] ] ,
2020-08-07 01:09:47 +02:00
) . decode ( ) ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
) ,
2020-03-12 14:17:25 +01:00
)
2016-06-21 21:05:44 +02:00
2020-03-12 14:17:25 +01:00
self . get_and_check_messages (
dict (
2020-08-07 01:09:47 +02:00
narrow = orjson . dumps (
2023-04-03 17:52:19 +02:00
[ dict ( operator = " dm " , operand = othello_email ) ] ,
2020-08-07 01:09:47 +02:00
) . decode ( ) ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
) ,
2020-03-12 14:17:25 +01:00
)
2016-06-21 21:05:44 +02:00
2022-02-02 07:52:50 +01:00
def test_unauthenticated_get_messages ( self ) - > None :
2024-03-14 19:53:40 +01:00
# Require channels:web-public as narrow to get web-public messages.
2022-02-02 07:52:50 +01:00
get_params = {
2020-08-04 19:33:43 +02:00
" anchor " : 10000000000000000 ,
" num_before " : 5 ,
" num_after " : 1 ,
2022-02-02 07:52:50 +01:00
}
result = self . client_get ( " /json/messages " , dict ( get_params ) )
2022-07-14 02:58:20 +02:00
self . check_unauthenticated_response ( result )
# Paths starting with /api/v1 should receive a response that asks
# for basic auth.
result = self . client_get ( " /api/v1/messages " , dict ( get_params ) )
self . check_unauthenticated_response ( result , www_authenticate = ' Basic realm= " zulip " ' )
2022-02-02 07:52:50 +01:00
2024-03-14 13:48:06 +01:00
# Successful access to web-public channel messages.
2024-07-12 02:30:23 +02:00
web_public_channel_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
2024-03-14 19:53:40 +01:00
" narrow " : orjson . dumps ( [ dict ( operator = " channels " , operand = " web-public " ) ] ) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
2024-03-14 13:48:06 +01:00
result = self . client_get ( " /json/messages " , dict ( web_public_channel_get_params ) )
2022-02-02 07:52:50 +01:00
# More detailed check of message parameters is done in `test_get_messages_with_web_public`.
self . assert_json_success ( result )
2020-08-04 19:33:43 +02:00
2022-02-02 07:52:50 +01:00
# Realm doesn't exist in our database.
2021-02-12 08:20:45 +01:00
with mock . patch ( " zerver.context_processors.get_realm " , side_effect = Realm . DoesNotExist ) :
2024-03-14 13:48:06 +01:00
result = self . client_get ( " /json/messages " , dict ( web_public_channel_get_params ) )
2021-02-12 08:19:30 +01:00
self . assert_json_error ( result , " Invalid subdomain " , status_code = 404 )
2020-08-04 19:33:43 +02:00
2023-04-03 13:58:08 +02:00
# Cannot access direct messages without login.
2024-07-12 02:30:23 +02:00
direct_messages_get_params : dict [ str , int | str | bool ] = {
2023-04-03 13:58:08 +02:00
* * get_params ,
" narrow " : orjson . dumps ( [ dict ( operator = " is " , operand = " dm " ) ] ) . decode ( ) ,
}
result = self . client_get ( " /json/messages " , dict ( direct_messages_get_params ) )
self . check_unauthenticated_response ( result )
# "is:private" is a legacy alias for "is:dm".
2024-07-12 02:30:23 +02:00
private_message_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
2021-02-12 08:20:45 +01:00
" narrow " : orjson . dumps ( [ dict ( operator = " is " , operand = " private " ) ] ) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
2022-02-02 07:52:50 +01:00
result = self . client_get ( " /json/messages " , dict ( private_message_get_params ) )
2022-07-14 02:58:20 +02:00
self . check_unauthenticated_response ( result )
2020-08-04 19:33:43 +02:00
2022-02-02 07:52:50 +01:00
# narrow should pass conditions in `is_spectator_compatible`.
2024-07-12 02:30:23 +02:00
non_spectator_compatible_narrow_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
2023-04-03 13:58:08 +02:00
# "is:dm" is not a is_spectator_compatible narrow.
2021-02-12 08:19:30 +01:00
" narrow " : orjson . dumps (
[
2024-03-14 19:53:40 +01:00
dict ( operator = " channels " , operand = " web-public " ) ,
2023-04-03 13:58:08 +02:00
dict ( operator = " is " , operand = " dm " ) ,
2021-02-12 08:19:30 +01:00
]
) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
2022-02-02 07:52:50 +01:00
result = self . client_get ( " /json/messages " , dict ( non_spectator_compatible_narrow_get_params ) )
2022-07-14 02:58:20 +02:00
self . check_unauthenticated_response ( result )
2020-08-04 19:33:43 +02:00
2022-02-02 07:52:50 +01:00
# Spectator login disabled in Realm.
2021-10-03 14:16:07 +02:00
do_set_realm_property (
get_realm ( " zulip " ) , " enable_spectator_access " , False , acting_user = None
)
2024-03-14 13:48:06 +01:00
result = self . client_get ( " /json/messages " , dict ( web_public_channel_get_params ) )
2022-07-14 02:58:20 +02:00
self . check_unauthenticated_response ( result )
2021-10-03 14:16:07 +02:00
do_set_realm_property ( get_realm ( " zulip " ) , " enable_spectator_access " , True , acting_user = None )
2022-02-02 07:52:50 +01:00
# Verify works after enabling `realm.enable_spectator_access` again.
2024-03-14 13:48:06 +01:00
result = self . client_get ( " /json/messages " , dict ( web_public_channel_get_params ) )
2022-02-02 07:52:50 +01:00
self . assert_json_success ( result )
2024-03-14 19:53:40 +01:00
# Cannot access even web-public channels without channels:web-public narrow.
2024-07-12 02:30:23 +02:00
non_web_public_channel_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
2024-03-14 16:29:27 +01:00
" narrow " : orjson . dumps ( [ dict ( operator = " channel " , operand = " Rome " ) ] ) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
2024-03-14 13:48:06 +01:00
result = self . client_get ( " /json/messages " , dict ( non_web_public_channel_get_params ) )
2022-07-14 02:58:20 +02:00
self . check_unauthenticated_response ( result )
2020-08-04 19:33:43 +02:00
2024-03-14 19:53:40 +01:00
# Verify that same request would work with channels:web-public added.
2024-07-12 02:30:23 +02:00
rome_web_public_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
" narrow " : orjson . dumps (
[
2024-03-14 19:53:40 +01:00
dict ( operator = " channels " , operand = " web-public " ) ,
2024-03-14 16:29:27 +01:00
# Rome is a web-channel channel.
dict ( operator = " channel " , operand = " Rome " ) ,
2022-02-02 07:52:50 +01:00
]
) . decode ( ) ,
}
result = self . client_get ( " /json/messages " , dict ( rome_web_public_get_params ) )
self . assert_json_success ( result )
2024-03-14 19:53:40 +01:00
# Cannot access non-web-public channel even with channels:web-public narrow.
2024-07-12 02:30:23 +02:00
scotland_web_public_get_params : dict [ str , int | str | bool ] = {
2022-02-02 07:52:50 +01:00
* * get_params ,
2021-02-12 08:19:30 +01:00
" narrow " : orjson . dumps (
[
2024-03-14 19:53:40 +01:00
dict ( operator = " channels " , operand = " web-public " ) ,
2024-03-14 13:48:06 +01:00
# Scotland is not a web-public channel.
2024-03-14 16:29:27 +01:00
dict ( operator = " channel " , operand = " Scotland " ) ,
2021-02-12 08:19:30 +01:00
]
) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
2022-02-02 07:52:50 +01:00
result = self . client_get ( " /json/messages " , dict ( scotland_web_public_get_params ) )
2021-02-12 08:19:30 +01:00
self . assert_json_error (
2024-03-14 16:29:27 +01:00
result , " Invalid narrow operator: unknown web-public channel Scotland " , status_code = 400
2021-02-12 08:19:30 +01:00
)
2020-08-04 19:33:43 +02:00
2021-02-12 08:19:30 +01:00
def setup_web_public_test ( self , num_web_public_message : int = 1 ) - > None :
2020-08-04 19:33:43 +02:00
"""
2024-03-14 13:48:06 +01:00
Send N + 2 messages , N in a web - public channel , then one in a non - web - public channel
2023-04-03 13:58:08 +02:00
and then a direct message .
2020-08-04 19:33:43 +02:00
"""
2021-02-12 08:20:45 +01:00
user_profile = self . example_user ( " iago " )
2021-10-03 14:16:07 +02:00
do_set_realm_property (
user_profile . realm , " enable_spectator_access " , True , acting_user = user_profile
)
2021-02-12 08:20:45 +01:00
self . login ( " iago " )
2024-03-14 13:48:06 +01:00
web_public_channel = self . make_stream ( " web-public-channel " , is_web_public = True )
non_web_public_channel = self . make_stream ( " non-web-public-channel " )
self . subscribe ( user_profile , web_public_channel . name )
self . subscribe ( user_profile , non_web_public_channel . name )
2020-08-04 19:33:43 +02:00
for _ in range ( num_web_public_message ) :
2021-02-12 08:19:30 +01:00
self . send_stream_message (
2024-03-14 13:48:06 +01:00
user_profile , web_public_channel . name , content = " web-public message "
2021-02-12 08:19:30 +01:00
)
self . send_stream_message (
2024-03-14 13:48:06 +01:00
user_profile , non_web_public_channel . name , content = " non-web-public message "
2021-02-12 08:19:30 +01:00
)
self . send_personal_message (
2023-04-03 13:58:08 +02:00
user_profile , self . example_user ( " hamlet " ) , content = " direct message "
2021-02-12 08:19:30 +01:00
)
2020-08-04 19:33:43 +02:00
self . logout ( )
2021-02-12 08:19:30 +01:00
def verify_web_public_query_result_success (
2022-06-08 04:52:09 +02:00
self , result : " TestHttpResponse " , expected_num_messages : int
2021-02-12 08:19:30 +01:00
) - > None :
2020-08-04 19:33:43 +02:00
self . assert_json_success ( result )
2021-02-12 08:20:45 +01:00
messages = orjson . loads ( result . content ) [ " messages " ]
2020-08-04 19:33:43 +02:00
self . assert_length ( messages , expected_num_messages )
2021-02-12 08:20:45 +01:00
sender = self . example_user ( " iago " )
2020-08-04 19:33:43 +02:00
for msg in messages :
2021-02-12 08:20:45 +01:00
self . assertEqual ( msg [ " content " ] , " <p>web-public message</p> " )
self . assertEqual ( msg [ " flags " ] , [ " read " ] )
self . assertEqual ( msg [ " sender_email " ] , sender . email )
self . assertEqual ( msg [ " avatar_url " ] , avatar_url ( sender ) )
2020-08-04 19:33:43 +02:00
2024-03-14 13:48:06 +01:00
def test_unauthenticated_narrow_to_web_public_channels ( self ) - > None :
2020-08-04 19:33:43 +02:00
self . setup_web_public_test ( )
2024-07-12 02:30:23 +02:00
post_params : dict [ str , int | str | bool ] = {
2020-08-04 19:33:43 +02:00
" anchor " : 1 ,
" num_before " : 1 ,
" num_after " : 1 ,
2021-02-12 08:19:30 +01:00
" narrow " : orjson . dumps (
[
2024-03-14 19:53:40 +01:00
dict ( operator = " channels " , operand = " web-public " ) ,
2024-03-14 16:29:27 +01:00
dict ( operator = " channel " , operand = " web-public-channel " ) ,
2021-02-12 08:19:30 +01:00
]
) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
result = self . client_get ( " /json/messages " , dict ( post_params ) )
self . verify_web_public_query_result_success ( result , 1 )
def test_get_messages_with_web_public ( self ) - > None :
"""
An unauthenticated call to GET / json / messages with valid parameters
2024-03-14 19:53:40 +01:00
including channels : web - public narrow returns list of messages in the
2024-03-14 13:48:06 +01:00
web - public channels .
2020-08-04 19:33:43 +02:00
"""
self . setup_web_public_test ( num_web_public_message = 8 )
post_params = {
" anchor " : " first_unread " ,
" num_before " : 5 ,
" num_after " : 1 ,
2024-03-14 19:53:40 +01:00
" narrow " : orjson . dumps ( [ dict ( operator = " channels " , operand = " web-public " ) ] ) . decode ( ) ,
2020-08-04 19:33:43 +02:00
}
result = self . client_get ( " /json/messages " , dict ( post_params ) )
# Of the last 7 (num_before + num_after + 1) messages, only 5
# messages are returned, which were all web-public messages.
# The other two messages should not be returned even though
# they are the most recent.
self . verify_web_public_query_result_success ( result , 5 )
2017-11-05 10:51:25 +01:00
def test_client_avatar ( self ) - > None :
2017-10-20 16:52:04 +02:00
"""
The client_gravatar flag determines whether we send avatar_url .
"""
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
2020-03-06 18:40:46 +01:00
self . login_user ( hamlet )
2017-10-20 16:52:04 +02:00
2021-10-26 09:15:16 +02:00
do_change_user_setting (
hamlet ,
2021-03-01 11:33:24 +01:00
" email_address_visibility " ,
2021-10-26 09:15:16 +02:00
UserProfile . EMAIL_ADDRESS_VISIBILITY_EVERYONE ,
2021-03-01 11:33:24 +01:00
acting_user = None ,
2021-02-12 08:19:30 +01:00
)
2020-03-12 14:17:25 +01:00
2020-03-07 11:43:05 +01:00
self . send_personal_message ( hamlet , self . example_user ( " iago " ) )
2017-10-20 16:52:04 +02:00
2021-10-26 09:15:16 +02:00
result = self . get_and_check_messages (
dict ( anchor = " newest " , client_gravatar = orjson . dumps ( False ) . decode ( ) )
)
2021-02-12 08:20:45 +01:00
message = result [ " messages " ] [ 0 ]
self . assertIn ( " gravatar.com " , message [ " avatar_url " ] )
2017-10-20 16:52:04 +02:00
2021-10-26 09:15:16 +02:00
result = self . get_and_check_messages (
dict ( anchor = " newest " , client_gravatar = orjson . dumps ( True ) . decode ( ) )
)
2021-02-12 08:20:45 +01:00
message = result [ " messages " ] [ 0 ]
self . assertEqual ( message [ " avatar_url " ] , None )
2017-10-20 16:52:04 +02:00
2019-02-05 07:12:37 +01:00
# Now verify client_gravatar doesn't run with EMAIL_ADDRESS_VISIBILITY_ADMINS
2021-10-26 09:15:16 +02:00
do_change_user_setting (
hamlet ,
2021-03-01 11:33:24 +01:00
" email_address_visibility " ,
2021-10-26 09:15:16 +02:00
UserProfile . EMAIL_ADDRESS_VISIBILITY_ADMINS ,
2021-03-01 11:33:24 +01:00
acting_user = None ,
2021-02-12 08:19:30 +01:00
)
2021-10-26 09:15:16 +02:00
result = self . get_and_check_messages (
dict ( anchor = " newest " , client_gravatar = orjson . dumps ( True ) . decode ( ) )
)
2021-02-12 08:20:45 +01:00
message = result [ " messages " ] [ 0 ]
self . assertIn ( " gravatar.com " , message [ " avatar_url " ] )
2019-02-05 07:12:37 +01:00
2023-04-03 17:52:19 +02:00
def test_get_messages_with_narrow_dm ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
2023-04-03 17:52:19 +02:00
A request for old messages with a narrow by direct message only returns
2016-06-21 21:05:44 +02:00
conversations with that user .
"""
2021-02-12 08:20:45 +01:00
me = self . example_user ( " hamlet " )
2016-11-29 07:22:02 +01:00
2024-07-12 02:30:17 +02:00
def dr_emails ( dr : list [ UserDisplayRecipient ] ) - > str :
2016-12-08 09:39:48 +01:00
assert isinstance ( dr , list )
2021-02-12 08:20:45 +01:00
return " , " . join ( sorted ( { * ( r [ " email " ] for r in dr ) , me . email } ) )
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:17 +02:00
def dr_ids ( dr : list [ UserDisplayRecipient ] ) - > list [ int ] :
2019-06-08 23:21:01 +02:00
assert isinstance ( dr , list )
2021-02-12 08:20:45 +01:00
return sorted ( { * ( r [ " id " ] for r in dr ) , self . example_user ( " hamlet " ) . id } )
2019-06-08 23:21:01 +02:00
2020-03-07 11:43:05 +01:00
self . send_personal_message ( me , self . example_user ( " iago " ) )
2019-06-29 15:09:35 +02:00
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2017-10-28 17:38:19 +02:00
me ,
2020-03-07 11:43:05 +01:00
[ self . example_user ( " iago " ) , self . example_user ( " cordelia " ) ] ,
2017-10-28 17:38:19 +02:00
)
2019-06-29 15:09:35 +02:00
2023-04-03 17:52:19 +02:00
# Send a 1:1 and group direct message containing Aaron.
# Then deactivate Aaron to test "dm" narrow includes messages
2019-06-29 15:09:35 +02:00
# from deactivated users also.
2020-03-07 11:43:05 +01:00
self . send_personal_message ( me , self . example_user ( " aaron " ) )
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2019-06-29 15:09:35 +02:00
me ,
2020-03-07 11:43:05 +01:00
[ self . example_user ( " iago " ) , self . example_user ( " aaron " ) ] ,
2019-06-29 15:09:35 +02:00
)
aaron = self . example_user ( " aaron " )
2021-03-27 06:02:12 +01:00
do_deactivate_user ( aaron , acting_user = None )
2019-06-29 15:09:35 +02:00
self . assertFalse ( aaron . is_active )
2021-02-12 08:19:30 +01:00
personals = [
2021-02-12 08:20:45 +01:00
m for m in get_user_messages ( self . example_user ( " hamlet " ) ) if not m . is_stream_message ( )
2021-02-12 08:19:30 +01:00
]
2017-03-14 09:15:37 +01:00
for personal in personals :
emails = dr_emails ( get_display_recipient ( personal . recipient ) )
2020-03-06 18:40:46 +01:00
self . login_user ( me )
2024-07-12 02:30:17 +02:00
narrow : list [ dict [ str , Any ] ] = [ dict ( operator = " dm " , operand = emails ) ]
2020-08-07 01:09:47 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
2019-06-08 23:21:01 +02:00
for message in result [ " messages " ] :
2021-02-12 08:20:45 +01:00
self . assertEqual ( dr_emails ( message [ " display_recipient " ] ) , emails )
2019-06-08 23:21:01 +02:00
2023-04-03 17:52:19 +02:00
# check passing user IDs is consistent with passing user emails as operand
2019-06-08 23:21:01 +02:00
ids = dr_ids ( get_display_recipient ( personal . recipient ) )
2023-04-03 17:52:19 +02:00
narrow = [ dict ( operator = " dm " , operand = ids ) ]
2020-08-07 01:09:47 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
2016-06-21 21:05:44 +02:00
2017-03-14 09:15:37 +01:00
for message in result [ " messages " ] :
2021-02-12 08:20:45 +01:00
self . assertEqual ( dr_emails ( message [ " display_recipient " ] ) , emails )
2016-06-21 21:05:44 +02:00
2024-05-20 22:09:35 +02:00
def test_get_messages_with_nonexistent_group_dm ( self ) - > None :
2024-04-16 00:54:00 +02:00
me = self . example_user ( " hamlet " )
2024-07-04 14:05:48 +02:00
# Direct message group which doesn't match anything gets no results
non_existent_direct_message_group = [
2024-04-16 00:54:00 +02:00
me . id ,
self . example_user ( " iago " ) . id ,
self . example_user ( " othello " ) . id ,
]
self . login_user ( me )
2024-07-12 02:30:17 +02:00
narrow : list [ dict [ str , Any ] ] = [
2024-07-04 14:05:48 +02:00
dict ( operator = " dm " , operand = non_existent_direct_message_group )
]
2024-04-16 00:54:00 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assertEqual ( result [ " messages " ] , [ ] )
2024-07-04 14:05:48 +02:00
narrow = [ dict ( operator = " dm " , operand = non_existent_direct_message_group , negated = True ) ]
2024-04-16 00:54:00 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assertEqual ( [ m [ " id " ] for m in result [ " messages " ] ] , [ 1 , 3 ] )
2023-04-03 17:52:19 +02:00
def test_get_visible_messages_with_narrow_dm ( self ) - > None :
2021-02-12 08:20:45 +01:00
me = self . example_user ( " hamlet " )
2020-03-06 18:40:46 +01:00
self . login_user ( me )
2021-02-12 08:20:45 +01:00
self . subscribe ( self . example_user ( " hamlet " ) , " Scotland " )
2018-01-02 18:33:28 +01:00
2023-07-31 22:52:35 +02:00
message_ids = [ self . send_personal_message ( me , self . example_user ( " iago " ) ) for i in range ( 5 ) ]
2018-01-02 18:33:28 +01:00
2023-04-03 17:52:19 +02:00
narrow = [ dict ( operator = " dm " , operand = self . example_user ( " iago " ) . email ) ]
2018-01-02 18:33:28 +01:00
self . message_visibility_test ( narrow , message_ids , 2 )
2023-04-05 17:46:43 +02:00
def test_get_messages_with_narrow_dm_including ( self ) - > None :
"""
A request for old messages with a narrow by " dm-including " only
returns direct messages ( both group and 1 : 1 ) with that user .
"""
me = self . example_user ( " hamlet " )
iago = self . example_user ( " iago " )
cordelia = self . example_user ( " cordelia " )
othello = self . example_user ( " othello " )
2023-07-31 22:52:35 +02:00
matching_message_ids = [
# group direct message, sent by current user
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
me ,
[ iago , cordelia , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
# group direct message, sent by searched user
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
cordelia ,
[ me , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
# group direct message, sent by another user
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
othello ,
[ me , cordelia ] ,
) ,
2023-07-31 22:52:35 +02:00
# direct 1:1 message, sent by current user to searched user
2023-04-05 17:46:43 +02:00
self . send_personal_message ( me , cordelia ) ,
2023-07-31 22:52:35 +02:00
# direct 1:1 message, sent by searched user to current user
2023-04-05 17:46:43 +02:00
self . send_personal_message ( cordelia , me ) ,
2023-07-31 22:52:35 +02:00
]
2023-04-05 17:46:43 +02:00
2023-07-31 22:52:35 +02:00
non_matching_message_ids = [
# direct 1:1 message, does not include current user
2023-04-05 17:46:43 +02:00
self . send_personal_message ( iago , cordelia ) ,
2023-07-31 22:52:35 +02:00
# direct 1:1 message, does not include searched user
2023-04-05 17:46:43 +02:00
self . send_personal_message ( iago , me ) ,
2023-07-31 22:52:35 +02:00
# direct 1:1 message, current user to self
2023-04-05 17:46:43 +02:00
self . send_personal_message ( me , me ) ,
2023-07-31 22:52:35 +02:00
# group direct message, sent by current user
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
me ,
[ iago , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
# group direct message, sent by searched user
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
cordelia ,
[ iago , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
]
2023-04-05 17:46:43 +02:00
self . login_user ( me )
test_operands = [ cordelia . email , cordelia . id ]
for operand in test_operands :
narrow = [ dict ( operator = " dm-including " , operand = operand ) ]
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
for message in result [ " messages " ] :
self . assertIn ( message [ " id " ] , matching_message_ids )
self . assertNotIn ( message [ " id " ] , non_matching_message_ids )
def test_get_visible_messages_with_narrow_dm_including ( self ) - > None :
me = self . example_user ( " hamlet " )
self . login_user ( me )
iago = self . example_user ( " iago " )
cordelia = self . example_user ( " cordelia " )
othello = self . example_user ( " othello " )
2023-07-31 22:52:35 +02:00
message_ids = [
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
me ,
[ iago , cordelia , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
self . send_personal_message ( me , cordelia ) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
cordelia ,
[ me , othello ] ,
) ,
2023-07-31 22:52:35 +02:00
self . send_personal_message ( cordelia , me ) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2023-04-05 17:46:43 +02:00
iago ,
[ cordelia , me ] ,
) ,
2023-07-31 22:52:35 +02:00
]
2023-04-05 17:46:43 +02:00
narrow = [ dict ( operator = " dm-including " , operand = cordelia . email ) ]
self . message_visibility_test ( narrow , message_ids , 2 )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_narrow_group_pm_with ( self ) - > None :
2017-03-23 23:35:37 +01:00
"""
2023-04-05 17:46:43 +02:00
A request for old messages with a narrow by deprecated " group-pm-with "
only returns direct message group conversations with that user .
2017-03-23 23:35:37 +01:00
"""
2020-03-07 11:43:05 +01:00
me = self . example_user ( " hamlet " )
2017-03-23 23:35:37 +01:00
2020-03-12 14:17:25 +01:00
iago = self . example_user ( " iago " )
cordelia = self . example_user ( " cordelia " )
othello = self . example_user ( " othello " )
2023-07-31 22:52:35 +02:00
matching_message_ids = [
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2017-10-28 17:38:19 +02:00
me ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
[ iago , cordelia , othello ] ,
2017-10-28 17:38:19 +02:00
) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2017-10-28 17:38:19 +02:00
me ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
[ cordelia , othello ] ,
2017-10-28 17:38:19 +02:00
) ,
2023-07-31 22:52:35 +02:00
]
2017-10-28 17:38:19 +02:00
2023-07-31 22:52:35 +02:00
non_matching_message_ids = [
2020-03-12 14:17:25 +01:00
self . send_personal_message ( me , cordelia ) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2017-10-28 17:38:19 +02:00
me ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
[ iago , othello ] ,
2017-10-28 17:38:19 +02:00
) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2020-03-07 11:43:05 +01:00
self . example_user ( " cordelia " ) ,
2020-03-12 14:17:25 +01:00
[ iago , othello ] ,
2017-10-28 17:38:19 +02:00
) ,
2023-07-31 22:52:35 +02:00
]
2017-03-23 23:35:37 +01:00
2020-03-06 18:40:46 +01:00
self . login_user ( me )
2020-03-12 14:17:25 +01:00
test_operands = [ cordelia . email , cordelia . id ]
2019-07-14 19:31:28 +02:00
for operand in test_operands :
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " group-pm-with " , operand = operand ) ]
2020-08-07 01:09:47 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
2019-07-14 19:31:28 +02:00
for message in result [ " messages " ] :
self . assertIn ( message [ " id " ] , matching_message_ids )
self . assertNotIn ( message [ " id " ] , non_matching_message_ids )
2017-03-23 23:35:37 +01:00
2018-01-02 18:33:28 +01:00
def test_get_visible_messages_with_narrow_group_pm_with ( self ) - > None :
2021-02-12 08:20:45 +01:00
me = self . example_user ( " hamlet " )
2020-03-06 18:40:46 +01:00
self . login_user ( me )
2018-01-02 18:33:28 +01:00
2020-03-12 14:17:25 +01:00
iago = self . example_user ( " iago " )
cordelia = self . example_user ( " cordelia " )
othello = self . example_user ( " othello " )
2023-07-31 22:52:35 +02:00
message_ids = [
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2018-01-02 18:33:28 +01:00
me ,
2020-03-12 14:17:25 +01:00
[ iago , cordelia , othello ] ,
2018-01-02 18:33:28 +01:00
) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2018-01-02 18:33:28 +01:00
me ,
2020-03-12 14:17:25 +01:00
[ cordelia , othello ] ,
2018-01-02 18:33:28 +01:00
) ,
2024-07-04 14:05:48 +02:00
self . send_group_direct_message (
2018-01-02 18:33:28 +01:00
me ,
2020-03-12 14:17:25 +01:00
[ cordelia , iago ] ,
2018-01-02 18:33:28 +01:00
) ,
2023-07-31 22:52:35 +02:00
]
2018-01-02 18:33:28 +01:00
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " group-pm-with " , operand = cordelia . email ) ]
2018-01-02 18:33:28 +01:00
self . message_visibility_test ( narrow , message_ids , 1 )
2017-11-05 10:51:25 +01:00
def test_include_history ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
cordelia = self . example_user ( " cordelia " )
2017-11-07 16:48:06 +01:00
2024-03-14 13:48:06 +01:00
channel_name = " test channel "
self . subscribe ( cordelia , channel_name )
2017-11-07 16:48:06 +01:00
2024-03-14 13:48:06 +01:00
old_message_id = self . send_stream_message ( cordelia , channel_name , content = " foo " )
2017-11-07 16:48:06 +01:00
2024-03-14 13:48:06 +01:00
self . subscribe ( hamlet , channel_name )
2017-11-07 16:48:06 +01:00
2021-02-12 08:20:45 +01:00
content = " hello @**King Hamlet** "
2024-03-14 13:48:06 +01:00
new_message_id = self . send_stream_message ( cordelia , channel_name , content = content )
2017-11-07 16:48:06 +01:00
2020-03-06 18:40:46 +01:00
self . login_user ( hamlet )
2017-11-07 16:48:06 +01:00
narrow = [
2024-03-14 16:29:27 +01:00
dict ( operator = " channel " , operand = channel_name ) ,
2017-11-07 16:48:06 +01:00
]
req = dict (
2020-08-07 01:09:47 +02:00
narrow = orjson . dumps ( narrow ) . decode ( ) ,
2017-11-07 16:48:06 +01:00
anchor = LARGER_THAN_MAX_MESSAGE_ID ,
num_before = 100 ,
num_after = 100 ,
)
2021-02-12 08:20:45 +01:00
payload = self . client_get ( " /json/messages " , req )
2017-11-07 16:48:06 +01:00
self . assert_json_success ( payload )
2020-08-07 01:09:47 +02:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2021-05-17 05:41:32 +02:00
self . assert_length ( messages , 2 )
2017-11-07 16:48:06 +01:00
for message in messages :
2021-02-12 08:20:45 +01:00
if message [ " id " ] == old_message_id :
2017-11-07 16:48:06 +01:00
old_message = message
2021-02-12 08:20:45 +01:00
elif message [ " id " ] == new_message_id :
2017-11-07 16:48:06 +01:00
new_message = message
2021-02-12 08:20:45 +01:00
self . assertEqual ( old_message [ " flags " ] , [ " read " , " historical " ] )
self . assertEqual ( new_message [ " flags " ] , [ " mentioned " ] )
2017-11-07 16:48:06 +01:00
2024-03-14 13:48:06 +01:00
def test_get_messages_with_narrow_channel ( self ) - > None :
2023-07-16 12:41:09 +02:00
hamlet = self . example_user ( " hamlet " )
self . login_user ( hamlet )
realm = hamlet . realm
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
num_messages_per_channel = 5
channel_names = [ " Scotland " , " Verona " , " Venice " ]
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def send_messages_to_all_channels ( ) - > None :
2023-08-30 21:19:37 +02:00
Message . objects . filter ( realm_id = realm . id , recipient__type = Recipient . STREAM ) . delete ( )
2024-03-14 13:48:06 +01:00
for channel_name in channel_names :
self . subscribe ( hamlet , channel_name )
for i in range ( num_messages_per_channel ) :
message_id = self . send_stream_message ( hamlet , channel_name , content = f " test { i } " )
2023-07-16 12:41:09 +02:00
message = Message . objects . get ( id = message_id )
2024-03-14 13:48:06 +01:00
self . assert_message_stream_name ( message , channel_name )
2023-07-16 12:41:09 +02:00
2024-03-14 13:48:06 +01:00
send_messages_to_all_channels ( )
2023-07-16 12:41:09 +02:00
self . send_personal_message ( hamlet , hamlet )
messages = get_user_messages ( hamlet )
2024-03-14 13:48:06 +01:00
channel_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
self . assertGreater ( len ( messages ) , len ( channel_messages ) )
self . assert_length ( channel_messages , num_messages_per_channel * len ( channel_names ) )
2023-07-16 12:41:09 +02:00
2024-03-14 13:48:06 +01:00
for channel_name in channel_names :
channel = get_stream ( channel_name , realm )
for operand in [ channel . name , channel . id ] :
2024-03-14 16:29:27 +01:00
narrow = [ dict ( operator = " channel " , operand = operand ) ]
2023-07-16 12:41:09 +02:00
result = self . get_and_check_messages (
dict ( narrow = orjson . dumps ( narrow ) . decode ( ) , num_after = 100 )
)
2024-07-12 02:30:17 +02:00
fetched_messages : list [ dict [ str , object ] ] = result [ " messages " ]
2024-03-14 13:48:06 +01:00
self . assert_length ( fetched_messages , num_messages_per_channel )
2023-07-16 12:41:09 +02:00
for message_dict in fetched_messages :
self . assertEqual ( message_dict [ " type " ] , " stream " )
2024-03-14 13:48:06 +01:00
self . assertEqual ( message_dict [ " display_recipient " ] , channel_name )
self . assertEqual ( message_dict [ " recipient_id " ] , channel . recipient_id )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def test_get_visible_messages_with_narrow_channel ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
self . subscribe ( self . example_user ( " hamlet " ) , " Scotland " )
2018-01-02 18:33:28 +01:00
2023-07-31 22:52:35 +02:00
message_ids = [
self . send_stream_message ( self . example_user ( " iago " ) , " Scotland " ) for i in range ( 5 )
]
2018-01-02 18:33:28 +01:00
2024-03-14 16:29:27 +01:00
narrow = [ dict ( operator = " channel " , operand = " Scotland " ) ]
2018-01-02 18:33:28 +01:00
self . message_visibility_test ( narrow , message_ids , 2 )
2024-03-14 13:48:06 +01:00
def test_get_messages_with_narrow_channel_mit_unicode_regex ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
2020-10-23 02:43:28 +02:00
A request for old messages for a user in the mit . edu relam with Unicode
2024-03-14 13:48:06 +01:00
channel name should be correctly escaped in the database query .
2016-06-21 21:05:44 +02:00
"""
2021-02-12 08:20:45 +01:00
user = self . mit_user ( " starnine " )
2020-03-06 18:40:46 +01:00
self . login_user ( user )
2024-03-14 13:48:06 +01:00
# We need to subscribe to a channel and then send a message to
# it to ensure that we actually have a channel message in this
2016-06-21 21:05:44 +02:00
# narrow view.
2024-03-14 13:48:06 +01:00
lambda_channel_name = " \u03bb -channel "
channel = self . subscribe ( user , lambda_channel_name )
self . assertTrue ( channel . is_in_zephyr_realm )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
lambda_channel_d_name = " \u03bb -channel.d "
self . subscribe ( user , lambda_channel_d_name )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
self . send_stream_message ( user , " \u03bb -channel " )
self . send_stream_message ( user , " \u03bb -channel.d " )
2016-06-21 21:05:44 +02:00
2024-03-14 16:29:27 +01:00
narrow = [ dict ( operator = " channel " , operand = " \u03bb -channel " ) ]
2021-02-12 08:19:30 +01:00
result = self . get_and_check_messages (
dict ( num_after = 2 , narrow = orjson . dumps ( narrow ) . decode ( ) ) , 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 " ) )
2024-03-14 13:48:06 +01:00
channel_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2016-06-21 21:05:44 +02:00
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 2 )
2016-06-21 21:05:44 +02:00
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
2024-03-14 13:48:06 +01:00
channel_id = channel_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , channel_id )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_get_messages_with_narrow_topic_mit_unicode_regex ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
2020-10-23 02:43:28 +02: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 " )
2020-03-06 18:40:46 +01:00
self . login_user ( mit_user_profile )
2024-03-14 13:48:06 +01:00
# We need to subscribe to a channel and then send a message to
# it to ensure that we actually have a channel message in this
2016-06-21 21:05:44 +02:00
# narrow view.
2017-08-25 06:01:29 +02:00
self . subscribe ( mit_user_profile , " Scotland " )
2020-04-09 21:51:58 +02:00
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " \u03bb -topic " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " \u03bb -topic.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " \u03bb -topic.d.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " \u03bb -topic.d.d.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " \u03bb -topic.d.d.d.d " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " topic " , operand = " \u03bb -topic " ) ]
2017-08-26 01:01:12 +02:00
result = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict ( num_after = 100 , narrow = orjson . dumps ( narrow ) . decode ( ) ) , 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 )
2024-03-14 13:48:06 +01:00
channel_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 5 )
2017-02-22 21:23:22 +01:00
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
2024-03-14 13:48:06 +01:00
channel_id = channel_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , channel_id )
2017-02-22 21:23:22 +01:00
2017-11-05 10:51:25 +01:00
def test_get_messages_with_narrow_topic_mit_personal ( self ) - > None :
2017-02-22 21:23:22 +01:00
"""
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 " )
2017-11-18 00:11:24 +01:00
2024-03-14 13:48:06 +01:00
# We need to subscribe to a channel and then send a message to
# it to ensure that we actually have a channel message in this
2017-02-22 21:23:22 +01:00
# narrow view.
2020-03-06 18:40:46 +01:00
self . login_user ( mit_user_profile )
2017-08-25 06:01:29 +02:00
self . subscribe ( mit_user_profile , " Scotland " )
2017-02-22 21:23:22 +01:00
2020-04-09 21:51:58 +02:00
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " .d.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " PERSONAL " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = ' (instance " " ).d ' )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " .d.d.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " personal.d " )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = ' (instance " " ) ' )
self . send_stream_message ( mit_user_profile , " Scotland " , topic_name = " .d.d.d.d " )
2017-02-22 21:23:22 +01:00
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " topic " , operand = " personal.d.d " ) ]
2017-08-26 01:01:12 +02:00
result = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict ( num_before = 50 , num_after = 50 , narrow = orjson . dumps ( narrow ) . decode ( ) ) ,
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 )
2024-03-14 13:48:06 +01:00
channel_messages = [ msg for msg in messages if msg . is_stream_message ( ) ]
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 7 )
2016-06-21 21:05:44 +02:00
for i , message in enumerate ( result [ " messages " ] ) :
self . assertEqual ( message [ " type " ] , " stream " )
2024-03-14 13:48:06 +01:00
channel_id = channel_messages [ i ] . recipient . id
self . assertEqual ( message [ " recipient_id " ] , channel_id )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_get_messages_with_narrow_sender ( self ) - > 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 .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2020-03-12 14:17:25 +01:00
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
othello = self . example_user ( " othello " )
iago = self . example_user ( " iago " )
2020-03-12 14:17:25 +01:00
2016-06-21 21:05:44 +02:00
# We need to send a message here to ensure that we actually
2024-03-14 13:48:06 +01:00
# have a channel message in this narrow view.
tests: Ensure stream senders get a UserMessage row.
We now complain if a test author sends a stream message
that does not result in the sender getting a
UserMessage row for the message.
This is basically 100% equivalent to complaining that
the author failed to subscribe the sender to the stream
as part of the test setup, as far as I can tell, so the
AssertionError instructs the author to subscribe the
sender to the stream.
We exempt bots from this check, although it is
plausible we should only exempt the system bots like
the notification bot.
I considered auto-subscribing the sender to the stream,
but that can be a little more expensive than the
current check, and we generally want test setup to be
explicit.
If there is some legitimate way than a subscribed human
sender can't get a UserMessage, then we probably want
an explicit test for that, or we may want to change the
backend to just write a UserMessage row in that
hypothetical situation.
For most tests, including almost all the ones fixed
here, the author just wants their test setup to
realistically reflect normal operation, and often devs
may not realize that Cordelia is not subscribed to
Denmark or not realize that Hamlet is not subscribed to
Scotland.
Some of us don't remember our Shakespeare from high
school, and our stream subscriptions don't even
necessarily reflect which countries the Bard placed his
characters in.
There may also be some legitimate use case where an
author wants to simulate sending a message to an
unsubscribed stream, but for those edge cases, they can
always set allow_unsubscribed_sender to True.
2021-12-10 13:55:48 +01:00
self . send_stream_message ( hamlet , " Denmark " )
self . send_stream_message ( othello , " Denmark " )
2020-03-12 14:17:25 +01:00
self . send_personal_message ( othello , hamlet )
tests: Ensure stream senders get a UserMessage row.
We now complain if a test author sends a stream message
that does not result in the sender getting a
UserMessage row for the message.
This is basically 100% equivalent to complaining that
the author failed to subscribe the sender to the stream
as part of the test setup, as far as I can tell, so the
AssertionError instructs the author to subscribe the
sender to the stream.
We exempt bots from this check, although it is
plausible we should only exempt the system bots like
the notification bot.
I considered auto-subscribing the sender to the stream,
but that can be a little more expensive than the
current check, and we generally want test setup to be
explicit.
If there is some legitimate way than a subscribed human
sender can't get a UserMessage, then we probably want
an explicit test for that, or we may want to change the
backend to just write a UserMessage row in that
hypothetical situation.
For most tests, including almost all the ones fixed
here, the author just wants their test setup to
realistically reflect normal operation, and often devs
may not realize that Cordelia is not subscribed to
Denmark or not realize that Hamlet is not subscribed to
Scotland.
Some of us don't remember our Shakespeare from high
school, and our stream subscriptions don't even
necessarily reflect which countries the Bard placed his
characters in.
There may also be some legitimate use case where an
author wants to simulate sending a message to an
unsubscribed stream, but for those edge cases, they can
always set allow_unsubscribed_sender to True.
2021-12-10 13:55:48 +01:00
self . send_stream_message ( iago , " Denmark " )
2016-06-21 21:05:44 +02:00
2020-03-12 14:17:25 +01:00
test_operands = [ othello . email , othello . id ]
2019-07-13 01:48:04 +02:00
for operand in test_operands :
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " sender " , operand = operand ) ]
2020-08-07 01:09:47 +02:00
result = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
2016-06-21 21:05:44 +02:00
2019-07-13 01:48:04 +02:00
for message in result [ " messages " ] :
2020-03-12 14:17:25 +01:00
self . assertEqual ( message [ " sender_id " ] , othello . id )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def _update_tsvector_index ( self ) - > None :
2016-09-19 16:34:01 +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 :
2021-02-12 08:19:30 +01:00
cursor . execute (
"""
2016-09-19 16:34:01 +02:00
UPDATE zerver_message SET
search_tsvector = to_tsvector ( ' zulip.english_us_search ' ,
subject | | rendered_content )
2021-02-12 08:19:30 +01:00
"""
)
2016-09-19 16:34:01 +02:00
2024-06-28 08:57:02 +02:00
def test_get_visible_messages_using_narrow_with ( self ) - > None :
hamlet = self . example_user ( " hamlet " )
othello = self . example_user ( " othello " )
iago = self . example_user ( " iago " )
realm = hamlet . realm
self . login ( " iago " )
self . make_stream ( " dev team " , invite_only = True , history_public_to_subscribers = False )
self . subscribe ( iago , " dev team " )
# Test `with` operator effective when targeting a topic with
# message which can be accessed by the user.
msg_id = self . send_stream_message ( iago , " dev team " , topic_name = " test " )
narrow = [
dict ( operator = " channel " , operand = " dev team " ) ,
dict ( operator = " topic " , operand = " other_topic " ) ,
dict ( operator = " with " , operand = msg_id ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assertEqual ( results [ " messages " ] [ 0 ] [ " id " ] , msg_id )
# Notably we returned the message with its actual topic.
self . assertEqual ( results [ " messages " ] [ 0 ] [ " subject " ] , " test " )
# Test `with` operator without channel/topic operators.
narrow = [
dict ( operator = " with " , operand = msg_id ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assertEqual ( results [ " messages " ] [ 0 ] [ " id " ] , msg_id )
# Test `with` operator ineffective when targeting a topic with
# message that can not be accessed by the user.
# Since !history_public_to_subscribers, hamlet cannot view.
self . subscribe ( hamlet , " dev team " )
self . login ( " hamlet " )
narrow = [
dict ( operator = " channel " , operand = " dev team " ) ,
dict ( operator = " topic " , operand = " test " ) ,
dict ( operator = " with " , operand = msg_id ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assert_length ( results [ " messages " ] , 0 )
# Same result with topic specified incorrectly
narrow = [
dict ( operator = " channel " , operand = " dev team " ) ,
dict ( operator = " topic " , operand = " wrong_guess " ) ,
dict ( operator = " with " , operand = msg_id ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assert_length ( results [ " messages " ] , 0 )
# If just with is specified, we get messages a la combined feed,
# but not the target message.
narrow = [
dict ( operator = " with " , operand = msg_id ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( narrow ) . decode ( ) ) )
self . assertNotIn ( msg_id , [ message [ " id " ] for message in results [ " messages " ] ] )
# Test `with` operator is effective when targeting personal
# messages with message id, and returns messages of that narrow.
#
# This will be relevant if we allow moving DMs in the future.
#
# First, attempt to view a message ID we can't access.
msg_ids = [ self . send_personal_message ( iago , othello ) for _ in range ( 2 ) ]
with_narrow = [
# Important: We pass the wrong conversation.
dict ( operator = " dm " , operand = [ hamlet . id ] ) ,
dict ( operator = " with " , operand = msg_ids [ 0 ] ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( with_narrow ) . decode ( ) ) )
self . assertNotIn ( msg_id , [ message [ " id " ] for message in results [ " messages " ] ] )
# Now switch to a user how does have access.
self . login ( " iago " )
with_narrow = [
# Important: We pass the wrong conversation.
dict ( operator = " dm " , operand = [ hamlet . id ] ) ,
dict ( operator = " with " , operand = msg_ids [ 0 ] ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( with_narrow ) . decode ( ) ) )
for msg in results [ " messages " ] :
self . assertIn ( msg [ " id " ] , msg_ids )
# Test `with` operator is effective when targeting direct
# messages group with message id.
iago = self . example_user ( " iago " )
cordelia = self . example_user ( " cordelia " )
hamlet = self . example_user ( " othello " )
msg_ids = [ self . send_group_direct_message ( iago , [ cordelia , hamlet ] ) for _ in range ( 2 ) ]
with_narrow = [
# Again, query the wrong conversation.
dict ( operator = " dm " , operand = [ hamlet . id ] ) ,
dict ( operator = " with " , operand = msg_ids [ 0 ] ) ,
]
results = self . get_and_check_messages ( dict ( narrow = orjson . dumps ( with_narrow ) . decode ( ) ) )
for msg in results [ " messages " ] :
self . assertIn ( msg [ " id " ] , msg_ids )
# Test `with` operator effective with spectator access when
# spectator has access to message.
self . logout ( )
self . setup_web_public_test ( 5 )
channel = get_stream ( " web-public-channel " , realm )
assert channel . recipient_id is not None
message_ids = messages_for_topic ( realm . id , channel . recipient_id , " test " ) . values_list (
" id " , flat = True
)
web_public_narrow = [
dict ( operator = " channels " , operand = " web-public " , negated = False ) ,
dict ( operator = " channel " , operand = " web-public-channel " ) ,
# Important: Pass a topic that doesn't contain the target message
dict ( operator = " topic " , operand = " wrong topic " ) ,
dict ( operator = " with " , operand = message_ids [ 0 ] ) ,
]
post_params = {
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 5 ,
" narrow " : orjson . dumps ( web_public_narrow ) . decode ( ) ,
}
result = self . client_get ( " /json/messages " , dict ( post_params ) )
self . verify_web_public_query_result_success ( result , 5 )
# Test `with` operator ineffective when spectator does not have
# access to message, by trying to access the same set of messages
# but when the spectator access is not allowed.
do_set_realm_property ( hamlet . realm , " enable_spectator_access " , False , acting_user = hamlet )
result = self . client_get ( " /json/messages " , dict ( post_params ) )
self . check_unauthenticated_response ( result )
# Test request with multiple `with` operators raises
# InvalidOperatorCombinationError
self . login ( " iago " )
iago = self . example_user ( " iago " )
msg_id_1 = self . send_stream_message ( iago , " Verona " )
msg_id_2 = self . send_stream_message ( iago , " Scotland " )
narrow = [
dict ( operator = " channel " , operand = " Verona " ) ,
dict ( operator = " with " , operand = msg_id_1 ) ,
dict ( operator = " topic " , operand = " test " ) ,
dict ( operator = " with " , operand = msg_id_2 ) ,
]
post_params = {
" anchor " : msg_id_1 ,
" num_before " : 0 ,
" num_after " : 5 ,
" narrow " : orjson . dumps ( narrow ) . decode ( ) ,
}
result = self . client_get ( " /json/messages " , dict ( post_params ) )
self . assert_json_error (
result , " Invalid narrow operator combination: Duplicate ' with ' operators. "
)
# Test request with an invalid message id for `with` operator fails.
msg_id = self . send_stream_message ( iago , " Verona " , topic_name = " Invalid id " )
narrow = [
dict ( operator = " channel " , operand = " Verona " ) ,
dict ( operator = " topic " , operand = " Invalid id " ) ,
dict ( operator = " with " , operand = " 3.2 " ) ,
]
post_params = {
" anchor " : msg_id ,
" num_before " : 0 ,
" num_after " : 5 ,
" narrow " : orjson . dumps ( narrow ) . decode ( ) ,
}
result = self . client_get ( " /json/messages " , dict ( post_params ) )
self . assert_json_error ( result , " Invalid narrow operator: Invalid ' with ' operator " )
2016-09-19 20:18:33 +02:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_messages_in_narrow ( self ) - > None :
2020-03-07 11:43:05 +01:00
user = self . example_user ( " cordelia " )
2020-03-06 18:40:46 +01:00
self . login_user ( user )
2016-09-19 20:18:33 +02:00
2018-05-10 19:00:29 +02:00
def send ( content : str ) - > int :
2017-10-28 17:38:19 +02:00
msg_id = self . send_stream_message (
2024-03-14 13:48:06 +01:00
user ,
" Verona " ,
2016-09-19 20:18:33 +02:00
content = content ,
)
return msg_id
2021-02-12 08:20:45 +01:00
good_id = send ( " KEYWORDMATCH and should work " )
bad_id = send ( " no match " )
2016-09-19 20:18:33 +02:00
msg_ids = [ good_id , bad_id ]
2021-02-12 08:20:45 +01:00
send ( " KEYWORDMATCH but not in msg_ids " )
2016-09-19 20:18:33 +02:00
self . _update_tsvector_index ( )
narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " KEYWORDMATCH " ) ,
2016-09-19 20:18:33 +02:00
]
raw_params = dict ( msg_ids = msg_ids , narrow = narrow )
2020-08-07 01:09:47 +02:00
params = { k : orjson . dumps ( v ) . decode ( ) for k , v in raw_params . items ( ) }
2021-02-12 08:20:45 +01:00
result = self . client_get ( " /json/messages/matches_narrow " , params )
2022-06-07 01:37:01 +02:00
messages = self . assert_json_success ( result ) [ " messages " ]
2023-09-12 23:19:57 +02:00
self . assert_length ( messages , 1 )
2016-09-19 20:18:33 +02:00
message = messages [ str ( good_id ) ]
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
message [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p><span class= " highlight " >KEYWORDMATCH</span> and should work</p> ' ,
2023-08-05 23:13:30 +02:00
)
narrow = [
dict ( operator = " search " , operand = " KEYWORDMATCH " ) ,
dict ( operator = " search " , operand = " work " ) ,
]
raw_params = dict ( msg_ids = msg_ids , narrow = narrow )
params = { k : orjson . dumps ( v ) . decode ( ) for k , v in raw_params . items ( ) }
result = self . client_get ( " /json/messages/matches_narrow " , params )
messages = self . assert_json_success ( result ) [ " messages " ]
2023-09-12 23:19:57 +02:00
self . assert_length ( messages , 1 )
2023-08-05 23:13:30 +02:00
message = messages [ str ( good_id ) ]
self . assertEqual (
message [ " match_content " ] ,
' <p><span class= " highlight " >KEYWORDMATCH</span> and should <span class= " highlight " >work</span></p> ' ,
2021-02-12 08:19:30 +01:00
)
2016-09-19 20:18:33 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_search ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " cordelia " )
2016-07-17 04:07:07 +02:00
messages_to_search = [
2021-02-12 08:20:45 +01:00
( " 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? " ) ,
( " urltest " , " https://google.com " ) ,
( " 日本 " , " こんに ちは 。 今日は いい 天気ですね。 " ) ,
( " 日本 " , " 今朝はごはんを食べました。 " ) ,
( " 日本 " , " 昨日、日本 のお菓子を送りました。 " ) ,
( " english " , " 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
2021-02-12 08:20:45 +01:00
cordelia = self . example_user ( " cordelia " )
2020-03-12 14:17:25 +01:00
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 (
2024-03-14 13:48:06 +01:00
cordelia ,
" 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 = [
2021-02-12 08:20:45 +01:00
dict ( operator = " sender " , operand = cordelia . email ) ,
dict ( operator = " search " , operand = " lunch " ) ,
2016-07-17 04:07:07 +02:00
]
2024-07-12 02:30:17 +02:00
result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_before = 0 ,
num_after = 10 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 2 )
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2016-07-17 04:07:07 +02:00
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " search " , operand = " https://google.com " ) ]
2024-07-12 02:30:17 +02:00
link_search_result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_before = 0 ,
num_after = 10 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( link_search_result [ " messages " ] , 1 )
2016-07-17 04:07:07 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
link_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p><a href= " https://google.com " >https://<span class= " highlight " >google.com</span></a></p> ' ,
)
2021-08-02 23:16:44 +02:00
( meeting_message , ) = ( m for m in messages if m [ TOPIC_NAME ] == " meetings " )
2021-02-12 08:20:45 +01:00
self . assertEqual ( meeting_message [ MATCH_TOPIC ] , " meetings " )
2016-07-17 04:07:07 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
meeting_message [ " match_content " ] ,
2023-01-03 02:16:53 +01:00
(
' <p>discuss <span class= " highlight " >lunch</span> after <span '
' class= " highlight " >lunch</span></p> '
) ,
2021-02-12 08:19:30 +01:00
)
2016-07-17 04:07:07 +02:00
2021-08-02 23:16:44 +02:00
( lunch_message , ) = ( m for m in messages if m [ TOPIC_NAME ] == " lunch plans " )
2021-02-12 08:19:30 +01:00
self . assertEqual ( lunch_message [ MATCH_TOPIC ] , ' <span class= " highlight " >lunch</span> plans ' )
2021-02-12 08:20:45 +01:00
self . assertEqual ( lunch_message [ " match_content " ] , " <p>I am hungry!</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 = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " discuss " ) ,
dict ( operator = " search " , operand = " after " ) ,
2017-01-16 16:53:20 +01:00
]
2024-07-12 02:30:17 +02:00
multi_search_result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( multi_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( multi_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
multi_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p><span class= " highlight " >discuss</span> lunch <span class= " highlight " >after</span> lunch</p> ' ,
)
2017-01-16 16:53:20 +01:00
2020-10-23 02:43:28 +02:00
# Test searching in messages with Unicode characters
2017-10-31 13:00:37 +01:00
narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " 日本 " ) ,
2017-10-31 13:00:37 +01:00
]
2021-02-12 08:19:30 +01:00
result = self . get_and_check_messages (
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 4 )
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2017-10-31 13:00:37 +01:00
2021-02-12 08:20:45 +01:00
japanese_message = [ m for m in messages if m [ TOPIC_NAME ] == " 日本 " ] [ - 1 ]
2021-02-12 08:19:30 +01:00
self . assertEqual ( japanese_message [ MATCH_TOPIC ] , ' <span class= " highlight " >日本</span> ' )
2017-10-31 13:00:37 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
japanese_message [ " match_content " ] ,
2023-01-03 02:16:53 +01:00
' <p>昨日、<span class= " highlight " >日本</span> のお菓子を送りました。</p> ' ,
2021-02-12 08:19:30 +01:00
)
2017-10-31 13:00:37 +01:00
2021-08-02 23:16:44 +02:00
( english_message , ) = ( m for m in messages if m [ TOPIC_NAME ] == " english " )
2021-02-12 08:20:45 +01:00
self . assertEqual ( english_message [ MATCH_TOPIC ] , " english " )
2017-10-31 13:00:37 +01:00
self . assertIn (
2021-02-12 08:20:45 +01:00
english_message [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p>I want to go to <span class= " highlight " >日本</span>!</p> ' ,
)
2017-10-31 13:00:37 +01:00
2020-10-23 02:43:28 +02:00
# Multiple search operands with Unicode
2017-10-31 18:24:00 +01:00
multi_search_narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " ちは " ) ,
dict ( operator = " search " , operand = " 今日は " ) ,
2017-10-31 18:24:00 +01:00
]
2021-02-12 08:19:30 +01:00
multi_search_result = self . get_and_check_messages (
dict (
narrow = orjson . dumps ( multi_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( multi_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
multi_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p>こんに <span class= " highlight " >ちは</span> 。 <span class= " highlight " >今日は</span> いい 天気ですね。</p> ' ,
)
2017-10-31 18:24:00 +01:00
2018-01-02 18:33:28 +01:00
@override_settings ( USING_PGROONGA = False )
def test_get_visible_messages_with_search ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
self . subscribe ( self . example_user ( " hamlet " ) , " Scotland " )
2018-01-02 18:33:28 +01:00
messages_to_search = [
( " Gryffindor " , " Hogwart ' s house which values courage, bravery, nerve, and chivalry " ) ,
2021-02-12 08:19:30 +01:00
(
" Hufflepuff " ,
" Hogwart ' s house which values hard work, patience, justice, and loyalty. " ,
) ,
(
" Ravenclaw " ,
" Hogwart ' s house which values intelligence, creativity, learning, and wit " ,
) ,
(
" Slytherin " ,
" Hogwart ' s house which values ambition, cunning, leadership, and resourcefulness " ,
) ,
2018-01-02 18:33:28 +01:00
]
2023-07-31 22:52:35 +02:00
message_ids = [
self . send_stream_message (
self . example_user ( " iago " ) , " Scotland " , topic_name = topic , content = content
2021-02-12 08:19:30 +01:00
)
2023-07-31 22:52:35 +02:00
for topic , content in messages_to_search
]
2018-01-02 18:33:28 +01:00
self . _update_tsvector_index ( )
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " search " , operand = " Hogwart ' s " ) ]
2018-01-02 18:33:28 +01:00
self . message_visibility_test ( narrow , message_ids , 2 )
2017-02-23 00:21:26 +01:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_search_not_subscribed ( self ) - > None :
2024-03-14 13:48:06 +01:00
""" Verify support for searching a channel you ' re not subscribed to """
self . subscribe ( self . example_user ( " hamlet " ) , " new-channel " )
2017-10-28 17:38:19 +02:00
self . send_stream_message (
2024-03-14 13:48:06 +01:00
self . example_user ( " hamlet " ) ,
" new-channel " ,
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 ( )
2021-02-12 08:20:45 +01:00
self . login ( " cordelia " )
2017-02-23 00:21:26 +01:00
2024-03-14 13:48:06 +01:00
channel_search_narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " special " ) ,
2024-03-14 16:29:27 +01:00
dict ( operator = " channel " , operand = " new-channel " ) ,
2017-02-23 00:21:26 +01:00
]
2024-07-12 02:30:17 +02:00
channel_search_result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
2024-03-14 13:48:06 +01:00
narrow = orjson . dumps ( channel_search_narrow ) . decode ( ) ,
2021-02-12 08:19:30 +01:00
anchor = 0 ,
num_after = 10 ,
num_before = 10 ,
)
)
2024-03-14 13:48:06 +01:00
self . assert_length ( channel_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2024-03-14 13:48:06 +01:00
channel_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p>Public <span class= " highlight " >special</span> content!</p> ' ,
)
2017-02-23 00:21:26 +01:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = True )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_search_pgroonga ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " 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 = [
2021-02-12 08:20:45 +01:00
( " 日本語 " , " こんにちは。今日はいい天気ですね。 " ) ,
( " 日本語 " , " 今朝はごはんを食べました。 " ) ,
( " 日本語 " , " 昨日、日本のお菓子を送りました。 " ) ,
( " english " , " I want to go to 日本! " ) ,
( " english " , " Can you speak https://en.wikipedia.org/wiki/Japanese? " ) ,
2022-08-15 09:26:07 +02:00
( " english " , " https://domain.com/path/to.something-I,want/ " ) ,
( " english " , " foo.cht " ) ,
2021-02-12 08:20:45 +01:00
( " bread & butter " , " chalk & cheese " ) ,
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 (
2024-03-14 13:48:06 +01:00
self . example_user ( " cordelia " ) ,
" 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 :
2021-02-12 08:19:30 +01:00
cursor . execute (
"""
2016-04-24 17:08:51 +02:00
UPDATE zerver_message SET
2018-05-19 05:39:13 +02:00
search_pgroonga = escape_html ( subject ) | | ' ' | | rendered_content
2021-02-12 08:19:30 +01:00
"""
)
2016-04-24 17:08:51 +02:00
narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " 日本 " ) ,
2016-04-24 17:08:51 +02:00
]
2024-07-12 02:30:17 +02:00
result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 4 )
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2016-04-24 17:08:51 +02:00
2021-02-12 08:20:45 +01:00
japanese_message = [ m for m in messages if m [ TOPIC_NAME ] == " 日本語 " ] [ - 1 ]
2021-02-12 08:19:30 +01:00
self . assertEqual ( japanese_message [ MATCH_TOPIC ] , ' <span class= " highlight " >日本</span>語 ' )
2016-04-24 17:08:51 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
japanese_message [ " match_content " ] ,
2023-01-03 02:16:53 +01:00
' <p>昨日、<span class= " highlight " >日本</span>のお菓子を送りました。</p> ' ,
2021-02-12 08:19:30 +01:00
)
2016-04-24 17:08:51 +02:00
2023-07-22 00:34:11 +02:00
[ english_message ] = ( m for m in messages if m [ TOPIC_NAME ] == " english " )
2021-02-12 08:20:45 +01:00
self . assertEqual ( english_message [ MATCH_TOPIC ] , " english " )
2020-10-23 08:27:01 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
english_message [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <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 = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " can " ) ,
dict ( operator = " search " , operand = " speak " ) ,
dict ( operator = " search " , operand = " wiki " ) ,
2017-01-16 16:53:20 +01:00
]
2024-07-12 02:30:17 +02:00
multi_search_result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( multi_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( multi_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
multi_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p><span class= " highlight " >Can</span> you <span class= " highlight " >speak</span> <a href= " 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
2020-10-23 02:43:28 +02:00
# Multiple search operands with Unicode
2017-10-31 18:24:00 +01:00
multi_search_narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " 朝は " ) ,
dict ( operator = " search " , operand = " べました " ) ,
2017-10-31 18:24:00 +01:00
]
2021-02-12 08:19:30 +01:00
multi_search_result = self . get_and_check_messages (
dict (
narrow = orjson . dumps ( multi_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( multi_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
multi_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p>今<span class= " highlight " >朝は</span>ごはんを食<span class= " highlight " >べました</span>。</p> ' ,
)
2017-10-31 18:24:00 +01:00
2024-07-12 02:30:23 +02:00
def search ( operand : str , link : str | None , highlight : str ) - > None :
2022-08-15 09:26:07 +02:00
narrow = [ dict ( operator = " search " , operand = operand ) ]
2024-07-12 02:30:17 +02:00
link_search_result : dict [ str , Any ] = self . get_and_check_messages (
2022-08-15 09:26:07 +02:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
2021-02-12 08:19:30 +01:00
)
2022-08-15 09:26:07 +02:00
self . assert_length ( link_search_result [ " messages " ] , 1 )
self . assertEqual (
link_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
f ' <p><a href= " { link } " > { highlight } </a></p> ' if link else f " <p> { highlight } </p> " ,
)
search ( " foo.cht " , None , ' <span class= " highlight " >foo.cht</span> ' )
search ( " foo " , None , ' <span class= " highlight " >foo</span>.cht ' )
search ( " cht " , None , ' foo.<span class= " highlight " >cht</span> ' )
url = " https://domain.com/path/to.something-I,want/ "
search ( url , url , f ' <span class= " highlight " > { url } </span> ' )
search (
" https://domain " ,
url ,
' <span class= " highlight " >https://domain</span>.com/path/to.something-I,want/ ' ,
)
search (
" domain " ,
url ,
' https://<span class= " highlight " >domain</span>.com/path/to.something-I,want/ ' ,
)
search (
" domain. " ,
url ,
' https://<span class= " highlight " >domain.</span>com/path/to.something-I,want/ ' ,
)
search (
" domain.com " ,
url ,
' https://<span class= " highlight " >domain.com</span>/path/to.something-I,want/ ' ,
)
search (
" domain.com/ " ,
url ,
' https://<span class= " highlight " >domain.com/</span>path/to.something-I,want/ ' ,
)
search (
" domain.com/path " ,
url ,
' https://<span class= " highlight " >domain.com/path</span>/to.something-I,want/ ' ,
)
search (
" .something " ,
url ,
' https://domain.com/path/to<span class= " highlight " >.something</span>-I,want/ ' ,
)
search (
" to.something " ,
url ,
' https://domain.com/path/<span class= " highlight " >to.something</span>-I,want/ ' ,
)
search (
" something-I " ,
url ,
' https://domain.com/path/to.<span class= " highlight " >something-I</span>,want/ ' ,
)
search (
" ,want " ,
url ,
' https://domain.com/path/to.something-I<span class= " highlight " >,want</span>/ ' ,
)
search (
" I,want " ,
url ,
' https://domain.com/path/to.something-<span class= " highlight " >I,want</span>/ ' ,
2021-02-12 08:19:30 +01:00
)
2017-07-12 08:19:13 +02:00
2020-10-23 02:43:28 +02:00
# Search operands with HTML special characters
2018-05-19 05:39:13 +02:00
special_search_narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " butter " ) ,
2018-05-19 05:39:13 +02:00
]
2024-07-12 02:30:17 +02:00
special_search_result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( special_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( special_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
special_search_result [ " messages " ] [ 0 ] [ MATCH_TOPIC ] ,
2021-02-12 08:19:30 +01:00
' bread & <span class= " highlight " >butter</span> ' ,
)
2018-05-19 05:39:13 +02:00
special_search_narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " search " , operand = " & " ) ,
2018-05-19 05:39:13 +02:00
]
2021-02-12 08:19:30 +01:00
special_search_result = self . get_and_check_messages (
dict (
narrow = orjson . dumps ( special_search_narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
num_before = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( special_search_result [ " messages " ] , 1 )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
special_search_result [ " messages " ] [ 0 ] [ MATCH_TOPIC ] ,
2021-02-12 08:19:30 +01:00
' bread <span class= " highlight " >&</span> butter ' ,
)
self . assertEqual (
2021-02-12 08:20:45 +01:00
special_search_result [ " messages " ] [ 0 ] [ " match_content " ] ,
2021-02-12 08:19:30 +01:00
' <p>chalk <span class= " highlight " >&</span> cheese</p> ' ,
)
2018-05-19 05:39:13 +02:00
2017-11-05 10:51:25 +01:00
def test_messages_in_narrow_for_non_search ( self ) - > None :
2020-03-07 11:43:05 +01:00
user = self . example_user ( " cordelia " )
2020-03-06 18:40:46 +01:00
self . login_user ( user )
2017-08-18 15:50:54 +02:00
2018-05-10 19:00:29 +02:00
def send ( content : str ) - > int :
2017-10-28 17:38:19 +02:00
msg_id = self . send_stream_message (
2024-03-14 13:48:06 +01:00
user ,
" Verona " ,
2021-02-12 08:20:45 +01:00
topic_name = " test_topic " ,
2017-08-18 15:50:54 +02:00
content = content ,
)
return msg_id
2021-02-12 08:20:45 +01:00
good_id = send ( " http://foo.com " )
bad_id = send ( " no link here " )
2017-08-18 15:50:54 +02:00
msg_ids = [ good_id , bad_id ]
2021-02-12 08:20:45 +01:00
send ( " http://bar.com but not in msg_ids " )
2017-08-18 15:50:54 +02:00
narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " has " , operand = " link " ) ,
2017-08-18 15:50:54 +02:00
]
raw_params = dict ( msg_ids = msg_ids , narrow = narrow )
2020-08-07 01:09:47 +02:00
params = { k : orjson . dumps ( v ) . decode ( ) for k , v in raw_params . items ( ) }
2021-02-12 08:20:45 +01:00
result = self . client_get ( " /json/messages/matches_narrow " , params )
2022-06-07 01:37:01 +02:00
messages = self . assert_json_success ( result ) [ " messages " ]
2023-09-12 23:19:57 +02:00
self . assert_length ( messages , 1 )
2017-08-18 15:50:54 +02:00
message = messages [ str ( good_id ) ]
2021-02-12 08:20:45 +01:00
self . assertIn ( " a href= " , message [ " match_content " ] )
self . assertIn ( " http://foo.com " , message [ " match_content " ] )
self . assertEqual ( message [ MATCH_TOPIC ] , " test_topic " )
2017-08-18 15:50:54 +02:00
2017-11-05 10:51:25 +01:00
def test_get_messages_with_only_searching_anchor ( self ) - > 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 .
"""
2021-02-12 08:20:45 +01:00
self . login ( " cordelia " )
2016-06-21 21:05:44 +02:00
2021-02-12 08:20:45 +01:00
cordelia = self . example_user ( " cordelia " )
2020-03-12 14:17:25 +01:00
anchor = self . send_stream_message ( cordelia , " Verona " )
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " sender " , operand = cordelia . email ) ]
2024-07-12 02:30:17 +02:00
result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = anchor ,
num_before = 0 ,
num_after = 0 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 1 )
2016-06-21 21:05:44 +02:00
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = " is " , operand = " mentioned " ) ]
2021-02-12 08:19:30 +01:00
result = self . get_and_check_messages (
dict ( narrow = orjson . dumps ( narrow ) . decode ( ) , anchor = anchor , num_before = 0 , num_after = 0 )
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 0 )
2016-06-21 21:05:44 +02:00
2021-07-13 20:23:36 +02:00
def test_get_messages_for_resolved_topics ( self ) - > None :
self . login ( " cordelia " )
cordelia = self . example_user ( " cordelia " )
self . send_stream_message ( cordelia , " Verona " , " whatever1 " )
resolved_topic_name = RESOLVED_TOPIC_PREFIX + " foo "
anchor = self . send_stream_message ( cordelia , " Verona " , " whatever2 " , resolved_topic_name )
self . send_stream_message ( cordelia , " Verona " , " whatever3 " )
narrow = [ dict ( operator = " is " , operand = " resolved " ) ]
result = self . get_and_check_messages (
2024-05-09 18:44:19 +02:00
dict ( narrow = orjson . dumps ( narrow ) . decode ( ) , anchor = anchor , num_before = 0 , num_after = 0 )
2021-07-13 20:23:36 +02:00
)
self . assert_length ( result [ " messages " ] , 1 )
self . assertEqual ( result [ " messages " ] [ 0 ] [ " id " ] , anchor )
2018-01-02 18:33:28 +01:00
def test_get_visible_messages_with_anchor ( self ) - > None :
2024-07-12 02:30:17 +02:00
def messages_matches_ids ( messages : list [ dict [ str , Any ] ] , message_ids : list [ int ] ) - > None :
2021-05-17 05:41:32 +02:00
self . assert_length ( messages , len ( message_ids ) )
2018-01-02 18:33:28 +01:00
for message in messages :
2021-02-12 08:19:30 +01:00
assert message [ " id " ] in message_ids
2018-01-02 18:33:28 +01:00
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2018-01-02 18:33:28 +01:00
2018-09-19 14:23:02 +02:00
Message . objects . all ( ) . delete ( )
2023-07-31 22:52:35 +02:00
message_ids = [
self . send_stream_message ( self . example_user ( " cordelia " ) , " Verona " ) for i in range ( 10 )
]
2018-01-02 18:33:28 +01:00
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 9 ] , num_before = 9 , num_after = 0 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 5 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 9 ] , num_before = 9 , num_after = 0 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , True )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 5 : ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 2 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 6 ] , num_before = 9 , num_after = 0 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , True )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 2 : 7 ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 9 ] + 1 ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 9 ] , num_before = 9 , num_after = 0 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2018-03-15 11:43:51 +01:00
self . assert_length ( messages , 0 )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , True )
2018-01-02 18:33:28 +01:00
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 0 , num_after = 5 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 5 : ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 7 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 0 , num_after = 5 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 7 : ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 2 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 0 ] , num_before = 0 , num_after = 5 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2018-03-15 11:21:36 +01:00
messages_matches_ids ( messages , message_ids [ 2 : 7 ] )
2018-01-02 18:33:28 +01:00
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 9 ] + 1 ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 0 ] , num_before = 0 , num_after = 5 )
2018-01-02 18:33:28 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2018-03-15 11:43:51 +01:00
self . assert_length ( messages , 0 )
2018-01-02 18:33:28 +01:00
2020-01-28 06:30:23 +01:00
# Verify that with anchor=0 we always get found_oldest=True
with first_visible_id_as ( 0 ) :
data = self . get_messages_response ( anchor = 0 , num_before = 0 , num_after = 5 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:30:23 +01:00
messages_matches_ids ( messages , message_ids [ 0 : 5 ] )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:30:23 +01:00
2020-01-28 06:37:25 +01:00
# Verify that with anchor=-1 we always get found_oldest=True
# anchor=-1 is arguably invalid input, but it used to be supported
with first_visible_id_as ( 0 ) :
data = self . get_messages_response ( anchor = - 1 , num_before = 0 , num_after = 5 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:37:25 +01:00
messages_matches_ids ( messages , message_ids [ 0 : 5 ] )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:37:25 +01:00
# And anchor='first' does the same thing.
with first_visible_id_as ( 0 ) :
2021-02-12 08:20:45 +01:00
data = self . get_messages_response ( anchor = " oldest " , num_before = 0 , num_after = 5 )
2020-01-28 06:37:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:37:25 +01:00
messages_matches_ids ( messages , message_ids [ 0 : 5 ] )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:37:25 +01:00
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 5 , num_after = 4 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2018-09-19 14:23:02 +02:00
messages_matches_ids ( messages , message_ids )
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 10 , num_after = 10 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2018-03-15 11:43:51 +01:00
messages_matches_ids ( messages , message_ids )
2018-01-02 18:33:28 +01:00
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 5 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 5 , num_after = 4 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , True )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 5 : ] )
2018-09-19 14:23:02 +02:00
with first_visible_id_as ( message_ids [ 5 ] ) :
data = self . get_messages_response ( anchor = message_ids [ 2 ] , num_before = 5 , num_after = 3 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , True )
2018-09-19 14:23:02 +02:00
messages_matches_ids ( messages , message_ids [ 5 : 8 ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 5 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 2 ] , num_before = 10 , num_after = 10 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , True )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 5 : ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 9 ] + 1 ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 5 , num_after = 4 )
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , True )
2018-03-15 11:43:51 +01:00
self . assert_length ( messages , 0 )
2018-01-02 18:33:28 +01:00
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 5 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 5 ] , num_before = 0 , num_after = 0 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , True )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2018-01-02 18:33:28 +01:00
messages_matches_ids ( messages , message_ids [ 5 : 6 ] )
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( message_ids [ 5 ] ) :
2018-03-15 11:43:51 +01:00
data = self . get_messages_response ( anchor = message_ids [ 2 ] , num_before = 0 , num_after = 0 )
2018-03-15 11:58:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
2018-03-15 11:58:25 +01:00
self . assert_length ( messages , 0 )
2018-01-02 18:33:28 +01:00
2020-01-28 06:30:23 +01:00
# Verify some additional behavior of found_newest.
with first_visible_id_as ( 0 ) :
2021-02-12 08:19:30 +01:00
data = self . get_messages_response (
anchor = LARGER_THAN_MAX_MESSAGE_ID , num_before = 5 , num_after = 0
)
2020-01-28 06:37:25 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:37:25 +01:00
self . assert_length ( messages , 5 )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:37:25 +01:00
# The anchor value of 'last' behaves just like LARGER_THAN_MAX_MESSAGE_ID.
with first_visible_id_as ( 0 ) :
2021-02-12 08:20:45 +01:00
data = self . get_messages_response ( anchor = " newest " , num_before = 5 , num_after = 0 )
2020-01-28 06:30:23 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:30:23 +01:00
self . assert_length ( messages , 5 )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:30:23 +01:00
with first_visible_id_as ( 0 ) :
2021-02-12 08:19:30 +01:00
data = self . get_messages_response (
anchor = LARGER_THAN_MAX_MESSAGE_ID + 1 , num_before = 5 , num_after = 0
)
2020-01-28 06:30:23 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:30:23 +01:00
self . assert_length ( messages , 5 )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:30:23 +01:00
with first_visible_id_as ( 0 ) :
2021-02-12 08:19:30 +01:00
data = self . get_messages_response (
anchor = LARGER_THAN_MAX_MESSAGE_ID , num_before = 20 , num_after = 0
)
2020-01-28 06:30:23 +01:00
2021-02-12 08:20:45 +01:00
messages = data [ " messages " ]
2020-01-28 06:30:23 +01:00
self . assert_length ( messages , 10 )
2021-02-12 08:20:45 +01:00
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , True )
self . assertEqual ( data [ " found_newest " ] , True )
self . assertEqual ( data [ " history_limited " ] , False )
2020-01-28 06:30:23 +01:00
2022-11-11 03:32:09 +01:00
data = self . get_messages_response (
anchor = message_ids [ 5 ] , num_before = 3 , num_after = 0 , include_anchor = False
)
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
messages_matches_ids ( messages , message_ids [ 2 : 5 ] )
data = self . get_messages_response (
anchor = message_ids [ 5 ] , num_before = 0 , num_after = 3 , include_anchor = False
)
messages = data [ " messages " ]
self . assertEqual ( data [ " found_anchor " ] , False )
self . assertEqual ( data [ " found_oldest " ] , False )
self . assertEqual ( data [ " found_newest " ] , False )
self . assertEqual ( data [ " history_limited " ] , False )
messages_matches_ids ( messages , message_ids [ 6 : 9 ] )
2017-11-05 10:51:25 +01:00
def test_missing_params ( self ) - > 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
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:17 +02:00
required_args : tuple [ tuple [ str , int ] , . . . ] = ( ( " num_before " , 1 ) , ( " num_after " , 1 ) )
2016-06-21 21:05:44 +02:00
for i in range ( len ( required_args ) ) :
2021-02-12 08:19:30 +01:00
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 )
2021-02-12 08:19:30 +01:00
self . assert_json_error ( result , f " Missing ' { required_args [ i ] [ 0 ] } ' argument " )
2016-06-21 21:05:44 +02:00
2018-09-09 14:54:52 +02:00
def test_get_messages_limits ( self ) - > None :
"""
A call to GET / json / messages requesting more than
MAX_MESSAGES_PER_FETCH messages returns an error message .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2018-09-09 14:54:52 +02:00
result = self . client_get ( " /json/messages " , dict ( anchor = 1 , num_before = 3000 , num_after = 3000 ) )
self . assert_json_error ( result , " Too many messages requested (maximum 5000). " )
result = self . client_get ( " /json/messages " , dict ( anchor = 1 , num_before = 6000 , num_after = 0 ) )
self . assert_json_error ( result , " Too many messages requested (maximum 5000). " )
result = self . client_get ( " /json/messages " , dict ( anchor = 1 , num_before = 0 , num_after = 6000 ) )
self . assert_json_error ( result , " Too many messages requested (maximum 5000). " )
2017-11-05 10:51:25 +01:00
def test_bad_int_params ( self ) - > 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 .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2016-06-21 21:05:44 +02:00
2020-09-02 06:59:07 +02:00
other_params = { " narrow " : { } , " anchor " : 0 }
2016-06-21 21:05:44 +02:00
int_params = [ " num_before " , " num_after " ]
2024-07-12 02:30:17 +02:00
invalid_parameters : list [ InvalidParam ] = [
2024-06-09 14:52:19 +02:00
InvalidParam ( value = False , expected_error = " is not valid JSON " ) ,
InvalidParam ( value = " " , expected_error = " is not valid JSON " ) ,
InvalidParam ( value = " -1 " , expected_error = " is too small " ) ,
InvalidParam ( value = - 1 , expected_error = " is too small " ) ,
2024-06-09 14:07:56 +02:00
]
2016-06-21 21:05:44 +02:00
for idx , param in enumerate ( int_params ) :
2024-06-09 14:07:56 +02:00
for invalid_parameter in invalid_parameters :
2016-06-21 21:05:44 +02:00
# Rotate through every bad type for every integer
# parameter, one at a time.
2020-09-02 06:59:07 +02:00
post_params = {
* * other_params ,
2024-06-09 14:07:56 +02:00
param : invalid_parameter . value ,
2024-03-01 03:05:22 +01:00
* * dict . fromkeys ( int_params [ : idx ] + int_params [ idx + 1 : ] , 0 ) ,
2020-09-02 06:59:07 +02:00
}
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2024-06-09 14:52:19 +02:00
self . assert_json_error ( result , f " { param } { invalid_parameter . expected_error } " )
2016-06-21 21:05:44 +02:00
2022-11-11 03:32:09 +01:00
def test_bad_include_anchor ( self ) - > None :
self . login ( " hamlet " )
result = self . client_get (
" /json/messages " , dict ( anchor = 1 , num_before = 1 , num_after = 1 , include_anchor = " false " )
)
self . assert_json_error ( result , " The anchor can only be excluded at an end of the range " )
2017-11-05 10:51:25 +01:00
def test_bad_narrow_type ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
narrow must be a list of string pairs .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2016-06-21 21:05:44 +02:00
2020-09-02 06:59:07 +02:00
other_params = { " anchor " : 0 , " num_before " : 0 , " num_after " : 0 }
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:17 +02:00
invalid_parameters : list [ InvalidParam ] = [
2024-06-09 14:52:19 +02:00
InvalidParam ( value = False , expected_error = " narrow is not valid JSON " ) ,
InvalidParam ( value = 0 , expected_error = " narrow is not a list " ) ,
InvalidParam ( value = " " , expected_error = " narrow is not valid JSON " ) ,
InvalidParam ( value = " { malformed json, " , expected_error = " narrow is not valid JSON " ) ,
InvalidParam ( value = " {foo: 3} " , expected_error = " narrow is not valid JSON " ) ,
InvalidParam (
value = " [1,2] " ,
expected_error = " Invalid narrow[0]: Value error, dict or list required " ,
) ,
InvalidParam (
value = ' [[ " x " , " y " , " z " ]] ' ,
expected_error = " Invalid narrow[0]: Value error, element is not a string pair " ,
) ,
2024-06-09 14:07:56 +02:00
]
for invalid_parameter in invalid_parameters :
post_params = { * * other_params , " narrow " : invalid_parameter . value }
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2024-06-09 14:52:19 +02:00
self . assert_json_error ( result , invalid_parameter . expected_error )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_bad_narrow_operator ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
Unrecognized narrow operators are rejected .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2024-03-14 16:29:27 +01:00
for operator in [ " " , " foo " , " channel:verona " , " __init__ " ] :
2021-02-12 08:20:45 +01:00
narrow = [ dict ( operator = operator , operand = " " ) ]
2020-08-07 01:09:47 +02:00
params = dict ( anchor = 0 , num_before = 0 , num_after = 0 , narrow = orjson . dumps ( narrow ) . decode ( ) )
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , params )
2021-02-12 08:19:30 +01:00
self . assert_json_error_contains ( result , " Invalid narrow operator: unknown operator " )
2016-06-21 21:05:44 +02:00
2019-08-07 17:32:19 +02:00
def test_invalid_narrow_operand_in_dict ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2019-08-07 17:32:19 +02:00
2024-03-14 16:29:27 +01:00
# str or int is required for "id", "sender", "channel", "dm-including" and "group-pm-with"
2023-07-17 15:39:05 +02:00
# operators
2024-07-12 02:30:17 +02:00
invalid_operands : list [ InvalidParam ] = [
2024-06-09 14:52:19 +02:00
InvalidParam ( value = [ " 1 " ] , expected_error = " operand is not a string or integer " ) ,
InvalidParam ( value = [ " 2 " ] , expected_error = " operand is not a string or integer " ) ,
InvalidParam (
value = None , expected_error = " Invalid narrow[0]: Value error, operand is missing "
) ,
2024-06-09 14:07:56 +02:00
]
2024-06-28 08:57:02 +02:00
for operand in [ " id " , " sender " , " channel " , " dm-including " , " group-pm-with " , " with " ] :
2024-06-09 14:07:56 +02:00
self . exercise_bad_narrow_operand_using_dict_api ( operand , invalid_operands )
2019-08-07 17:32:19 +02:00
2023-04-03 17:52:19 +02:00
# str or int list is required for "dm" and "pm-with" operator
2024-06-09 14:07:56 +02:00
# First set of invalid operands
2024-06-09 14:52:19 +02:00
invalid_operands = [
InvalidParam (
value = None , expected_error = " Invalid narrow[0]: Value error, operand is missing "
)
]
2023-04-03 17:52:19 +02:00
for operand in [ " dm " , " pm-with " ] :
2024-06-09 14:07:56 +02:00
self . exercise_bad_narrow_operand_using_dict_api ( operand , invalid_operands )
2019-08-07 17:32:19 +02:00
2024-06-09 14:07:56 +02:00
# Second set of invalid operands
invalid_operands = [
2024-06-09 14:52:19 +02:00
InvalidParam ( value = [ " 2 " ] , expected_error = " operand[0] is not an integer " ) ,
2024-06-09 14:07:56 +02:00
]
2023-04-03 17:52:19 +02:00
for operand in [ " dm " , " pm-with " ] :
2024-06-09 14:07:56 +02:00
self . exercise_bad_narrow_operand_using_dict_api ( operand , invalid_operands )
2019-08-07 17:32:19 +02:00
2024-06-09 14:07:56 +02:00
# Third set of invalid operands
invalid_operands = [
2024-06-09 14:52:19 +02:00
InvalidParam ( value = 2 , expected_error = " operand is not a string " ) ,
InvalidParam (
value = None , expected_error = " Invalid narrow[0]: Value error, operand is missing "
) ,
InvalidParam ( value = [ 1 ] , expected_error = " operand is not a string " ) ,
2024-06-09 14:07:56 +02:00
]
2023-07-17 15:39:05 +02:00
for operand in [ " is " , " near " , " has " ] :
2024-06-09 14:07:56 +02:00
self . exercise_bad_narrow_operand_using_dict_api ( operand , invalid_operands )
2019-08-07 17:32:19 +02:00
2020-04-11 17:32:32 +02:00
# Disallow empty search terms
2024-06-09 14:52:19 +02:00
invalid_operands = [ InvalidParam ( value = " " , expected_error = " operand cannot be blank. " ) ]
2024-06-09 14:07:56 +02:00
self . exercise_bad_narrow_operand_using_dict_api ( " search " , invalid_operands )
2020-04-11 17:32:32 +02:00
2019-08-07 17:32:19 +02:00
# The exercise_bad_narrow_operand helper method uses legacy tuple format to
2020-10-23 02:43:28 +02:00
# test bad narrow, this method uses the current dict API format
2021-02-12 08:19:30 +01:00
def exercise_bad_narrow_operand_using_dict_api (
2024-06-09 14:07:56 +02:00
self , operator : str , operands : Sequence [ InvalidParam ]
2021-02-12 08:19:30 +01:00
) - > None :
2019-08-07 17:32:19 +02:00
for operand in operands :
2024-06-09 14:07:56 +02:00
narrow = [ dict ( operator = operator , operand = operand . value ) ]
2020-08-07 01:09:47 +02:00
params = dict ( anchor = 0 , num_before = 0 , num_after = 0 , narrow = orjson . dumps ( narrow ) . decode ( ) )
2021-02-12 08:20:45 +01:00
result = self . client_get ( " /json/messages " , params )
2024-06-09 14:07:56 +02:00
self . assert_json_error_contains ( result , operand . expected_error )
2016-07-26 23:53:14 +02:00
2024-06-09 14:07:56 +02:00
def exercise_bad_narrow_operand ( self , operator : str , operands : Sequence [ InvalidParam ] ) - > None :
2020-09-02 06:59:07 +02:00
other_params = { " anchor " : " 0 " , " num_before " : " 0 " , " num_after " : " 0 " }
2016-06-21 21:05:44 +02:00
for operand in operands :
2024-06-09 14:07:56 +02:00
post_params = {
* * other_params ,
" narrow " : orjson . dumps ( [ [ operator , operand . value ] ] ) . decode ( ) ,
}
2016-07-28 00:38:45 +02:00
result = self . client_get ( " /json/messages " , post_params )
2024-06-09 14:07:56 +02:00
self . assert_json_error_contains ( result , operand . expected_error )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def test_bad_narrow_channel_content ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
2024-03-14 13:48:06 +01:00
If an invalid channel name is requested in get_messages , an error is
2016-06-21 21:05:44 +02:00
returned .
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2024-06-09 14:52:19 +02:00
error_msg = " Invalid narrow[0]: Value error, element is not a string pair "
2024-07-12 02:30:17 +02:00
bad_channel_content : list [ InvalidParam ] = [
2024-06-09 14:07:56 +02:00
InvalidParam ( value = 0 , expected_error = error_msg ) ,
InvalidParam ( value = [ ] , expected_error = error_msg ) ,
InvalidParam ( value = [ " x " , " y " ] , expected_error = error_msg ) ,
]
self . exercise_bad_narrow_operand ( " channel " , bad_channel_content )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_bad_narrow_one_on_one_email_content ( self ) - > None :
2016-06-21 21:05:44 +02:00
"""
2023-04-03 17:52:19 +02:00
If an invalid " dm " narrow is requested in get_messages ,
an error is returned .
2016-06-21 21:05:44 +02:00
"""
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2024-06-09 14:52:19 +02:00
error_msg = " Invalid narrow[0]: Value error, element is not a string pair "
2024-07-12 02:30:17 +02:00
bad_channel_content : list [ InvalidParam ] = [
2024-06-09 14:07:56 +02:00
InvalidParam ( value = 0 , expected_error = error_msg ) ,
InvalidParam ( value = [ ] , expected_error = error_msg ) ,
InvalidParam ( value = [ " x " , " y " ] , expected_error = error_msg ) ,
]
self . exercise_bad_narrow_operand ( " dm " , bad_channel_content )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
def test_bad_narrow_nonexistent_channel ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:17 +02:00
non_existing_channel_id_operand : list [ InvalidParam ] = [
2024-06-09 14:07:56 +02:00
InvalidParam (
value = " non-existent channel " ,
expected_error = " Invalid narrow operator: unknown channel " ,
) ,
]
self . exercise_bad_narrow_operand ( " channel " , non_existing_channel_id_operand )
non_existing_channel_id_operand = [
InvalidParam (
value = 1232891381239 , expected_error = " Invalid narrow operator: unknown channel "
) ,
]
self . exercise_bad_narrow_operand_using_dict_api ( " channel " , non_existing_channel_id_operand )
2019-08-07 17:32:19 +02:00
2017-11-05 10:51:25 +01:00
def test_bad_narrow_nonexistent_email ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2024-06-09 14:07:56 +02:00
error_msg = " Invalid narrow operator: unknown user "
2024-07-12 02:30:17 +02:00
invalid_operands : list [ InvalidParam ] = [
2024-06-09 14:07:56 +02:00
InvalidParam ( value = " non-existent-user@zulip.com " , expected_error = error_msg ) ,
]
self . exercise_bad_narrow_operand ( " dm " , invalid_operands )
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
def test_bad_narrow_dm_id_list ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " hamlet " )
2024-07-12 02:30:17 +02:00
invalid_operands : list [ InvalidParam ] = [
2024-06-09 14:52:19 +02:00
InvalidParam (
value = - 24 ,
expected_error = " Invalid narrow[0]: Value error, element is not a string pair " ,
)
2024-06-09 14:07:56 +02:00
]
self . exercise_bad_narrow_operand ( " dm " , invalid_operands )
2019-06-08 23:21:01 +02:00
2017-11-05 10:51:25 +01:00
def test_message_without_rendered_content ( self ) - > 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
2021-02-12 08:20:45 +01:00
m . content = " test content "
2020-03-26 23:16:23 +01:00
wide_dict = MessageDict . wide_dict ( m )
final_dict = MessageDict . finalize_payload (
wide_dict ,
apply_markdown = True ,
client_gravatar = False ,
)
2021-02-12 08:20:45 +01:00
self . assertEqual ( final_dict [ " content " ] , " <p>test content</p> " )
2016-06-21 21:05:44 +02:00
2024-07-12 02:30:17 +02:00
def common_check_get_messages_query ( self , query_params : dict [ str , Any ] , expected : str ) - > None :
2021-02-12 08:20:45 +01:00
user_profile = self . example_user ( " hamlet " )
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2016-06-21 21:05:44 +02:00
with queries_captured ( ) as queries :
2024-06-09 13:10:17 +02:00
get_messages_backend (
request ,
user_profile ,
num_before = query_params [ " num_before " ] ,
num_after = query_params [ " num_after " ] ,
)
2016-06-21 21:05:44 +02:00
for query in queries :
2023-06-06 23:54:19 +02:00
sql = str ( query . sql )
2022-10-15 22:59:17 +02:00
if " /* get_messages */ " in sql :
sql = 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
2018-04-05 14:54:30 +02:00
def test_find_first_unread_anchor ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
cordelia = self . example_user ( " cordelia " )
othello = self . example_user ( " othello " )
2018-04-05 14:54:30 +02:00
2021-02-12 08:20:45 +01:00
self . make_stream ( " England " )
tests: Ensure stream senders get a UserMessage row.
We now complain if a test author sends a stream message
that does not result in the sender getting a
UserMessage row for the message.
This is basically 100% equivalent to complaining that
the author failed to subscribe the sender to the stream
as part of the test setup, as far as I can tell, so the
AssertionError instructs the author to subscribe the
sender to the stream.
We exempt bots from this check, although it is
plausible we should only exempt the system bots like
the notification bot.
I considered auto-subscribing the sender to the stream,
but that can be a little more expensive than the
current check, and we generally want test setup to be
explicit.
If there is some legitimate way than a subscribed human
sender can't get a UserMessage, then we probably want
an explicit test for that, or we may want to change the
backend to just write a UserMessage row in that
hypothetical situation.
For most tests, including almost all the ones fixed
here, the author just wants their test setup to
realistically reflect normal operation, and often devs
may not realize that Cordelia is not subscribed to
Denmark or not realize that Hamlet is not subscribed to
Scotland.
Some of us don't remember our Shakespeare from high
school, and our stream subscriptions don't even
necessarily reflect which countries the Bard placed his
characters in.
There may also be some legitimate use case where an
author wants to simulate sending a message to an
unsubscribed stream, but for those edge cases, they can
always set allow_unsubscribed_sender to True.
2021-12-10 13:55:48 +01:00
self . subscribe ( cordelia , " England " )
2018-04-05 14:54:30 +02:00
# Send a few messages that Hamlet won't have UserMessage rows for.
2021-02-12 08:20:45 +01:00
unsub_message_id = self . send_stream_message ( cordelia , " England " )
2020-03-07 11:43:05 +01:00
self . send_personal_message ( cordelia , othello )
2018-04-05 14:54:30 +02:00
2021-02-12 08:20:45 +01:00
self . subscribe ( hamlet , " England " )
2018-04-05 14:54:30 +02:00
muted_topics = [
2021-02-12 08:20:45 +01:00
[ " England " , " muted " ] ,
2018-04-05 14:54:30 +02:00
]
2023-03-26 15:36:01 +02:00
set_topic_visibility_policy ( hamlet , muted_topics , UserTopic . VisibilityPolicy . MUTED )
2018-04-05 14:54:30 +02:00
# send a muted message
2021-02-12 08:20:45 +01:00
muted_message_id = self . send_stream_message ( cordelia , " England " , topic_name = " muted " )
2018-04-05 14:54:30 +02:00
# finally send Hamlet a "normal" message
2021-02-12 08:20:45 +01:00
first_message_id = self . send_stream_message ( cordelia , " England " )
2018-04-05 14:54:30 +02:00
# send a few more messages
2021-02-12 08:20:45 +01:00
extra_message_id = self . send_stream_message ( cordelia , " England " )
2020-03-07 11:43:05 +01:00
self . send_personal_message ( cordelia , hamlet )
2018-04-05 14:54:30 +02:00
user_profile = hamlet
2022-02-10 04:59:48 +01:00
with get_sqlalchemy_connection ( ) as sa_conn :
anchor = find_first_unread_anchor (
sa_conn = sa_conn ,
user_profile = user_profile ,
narrow = [ ] ,
)
2018-04-05 14:54:30 +02:00
self . assertEqual ( anchor , first_message_id )
2018-04-05 22:32:30 +02:00
# With the same data setup, we now want to test that a reasonable
# search still gets the first message sent to Hamlet (before he
2024-03-14 13:48:06 +01:00
# subscribed) and other recent messages to the channel.
2018-04-05 22:32:30 +02:00
query_params = dict (
2020-01-29 03:29:15 +01:00
anchor = " first_unread " ,
2018-04-05 22:32:30 +02:00
num_before = 10 ,
num_after = 10 ,
2024-03-14 16:29:27 +01:00
narrow = ' [[ " channel " , " England " ]] ' ,
2018-04-05 22:32:30 +02:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2018-04-05 22:32:30 +02:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-08-07 01:09:47 +02:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , first_message_id )
self . assertEqual ( result [ " found_newest " ] , True )
self . assertEqual ( result [ " found_oldest " ] , True )
2018-04-05 22:32:30 +02:00
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2018-04-05 22:32:30 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
{ msg [ " id " ] for msg in messages } ,
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
{ unsub_message_id , muted_message_id , first_message_id , extra_message_id } ,
2018-04-05 22:32:30 +02:00
)
2020-11-28 20:30:02 +01:00
def test_parse_anchor_value ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
cordelia = self . example_user ( " cordelia " )
2020-11-28 20:30:02 +01:00
# Send the first message to Hamlet
first_message_id = self . send_personal_message ( cordelia , hamlet )
# Send another message
self . send_personal_message ( cordelia , hamlet )
user_profile = hamlet
# Check if the anchor value in response is correct for different
# values of anchor parameter in request
# With anchor input as first_unread, see if response anchor
# value is same as the id of first unread message of Hamlet
query_params = dict (
anchor = " first_unread " ,
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2020-11-28 20:30:02 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2020-11-28 20:30:02 +01:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-11-28 20:30:02 +01:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , first_message_id )
2020-11-28 20:30:02 +01:00
# With anchor input as oldest, see if response anchor value is 0
query_params = dict (
anchor = " oldest " ,
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2020-11-28 20:30:02 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2020-11-28 20:30:02 +01:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-11-28 20:30:02 +01:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , 0 )
2020-11-28 20:30:02 +01:00
# With anchor input as newest, see if response
# anchor value is LARGER_THAN_MAX_MESSAGE_ID
query_params = dict (
anchor = " newest " ,
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2020-11-28 20:30:02 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2020-11-28 20:30:02 +01:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-11-28 20:30:02 +01:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , LARGER_THAN_MAX_MESSAGE_ID )
2020-11-28 20:30:02 +01:00
# With anchor input negative, see if
# response anchor value is clamped to 0
query_params = dict (
anchor = " -1 " ,
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2020-11-28 20:30:02 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2020-11-28 20:30:02 +01:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-11-28 20:30:02 +01:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , 0 )
2020-11-28 20:30:02 +01:00
# With anchor input more than LARGER_THAN_MAX_MESSAGE_ID,
# see if response anchor value is clamped down to LARGER_THAN_MAX_MESSAGE_ID
query_params = dict (
anchor = " 10000000000000001 " ,
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2020-11-28 20:30:02 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2020-11-28 20:30:02 +01:00
2024-06-09 13:10:17 +02:00
payload = get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2020-11-28 20:30:02 +01:00
result = orjson . loads ( payload . content )
2021-02-12 08:20:45 +01:00
self . assertEqual ( result [ " anchor " ] , LARGER_THAN_MAX_MESSAGE_ID )
2020-11-28 20:30:02 +01:00
2017-11-05 10:51:25 +01:00
def test_use_first_unread_anchor_with_some_unread_messages ( self ) - > None :
2021-02-12 08:20:45 +01: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.
2024-03-14 13:48:06 +01:00
# Here, Hamlet isn't subscribed to the channel Scotland
2020-03-07 11:43:05 +01:00
self . send_stream_message ( self . example_user ( " othello " ) , " Scotland " )
2018-05-19 03:34:51 +02:00
first_unread_message_id = self . send_personal_message (
2020-03-07 11:43:05 +01:00
self . example_user ( " othello " ) ,
self . example_user ( " hamlet " ) ,
2017-10-28 17:38:19 +02:00
)
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.
2020-03-07 11:43:05 +01:00
self . send_personal_message ( self . example_user ( " othello " ) , self . example_user ( " cordelia " ) )
self . send_personal_message ( self . example_user ( " othello " ) , self . example_user ( " iago " ) )
2016-07-23 18:41:39 +02:00
query_params = dict (
2020-01-29 03:29:15 +01:00
anchor = " first_unread " ,
2017-02-22 23:32:43 +01:00
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2016-07-23 18:41:39 +02:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2016-07-23 18:41:39 +02:00
with queries_captured ( ) as all_queries :
2024-06-09 13:10:17 +02:00
get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2016-07-23 18:41:39 +02:00
# Verify the query for old messages looks correct.
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if " /* get_messages */ " in q . sql ]
2021-05-17 05:41:32 +02:00
self . assert_length ( queries , 1 )
2023-06-06 23:54:19 +02:00
sql = queries [ 0 ] . sql
2021-02-12 08:20:45 +01:00
self . assertNotIn ( f " AND message_id = { LARGER_THAN_MAX_MESSAGE_ID } " , sql )
self . assertIn ( " ORDER BY message_id ASC " , sql )
2016-07-23 18:41:39 +02:00
2021-02-12 08:19:30 +01:00
cond = (
2021-02-12 08:20:45 +01:00
f " WHERE user_profile_id = { user_profile . id } AND message_id >= { first_unread_message_id } "
2021-02-12 08:19:30 +01:00
)
2017-02-22 23:32:43 +01:00
self . assertIn ( cond , sql )
2021-02-12 08:20:45 +01:00
cond = f " WHERE user_profile_id = { user_profile . id } AND message_id <= { first_unread_message_id - 1 } "
2018-01-02 18:33:28 +01:00
self . assertIn ( cond , sql )
2021-02-12 08:20:45 +01:00
self . assertIn ( " UNION " , sql )
2018-01-02 18:33:28 +01:00
def test_visible_messages_use_first_unread_anchor_with_some_unread_messages ( self ) - > None :
2021-02-12 08:20:45 +01:00
user_profile = self . example_user ( " hamlet " )
2018-01-02 18:33:28 +01:00
# Have Othello send messages to Hamlet that he hasn't read.
2021-02-12 08:20:45 +01:00
self . subscribe ( self . example_user ( " hamlet " ) , " Scotland " )
2018-01-02 18:33:28 +01:00
2020-03-07 11:43:05 +01:00
first_unread_message_id = self . send_stream_message ( self . example_user ( " othello " ) , " Scotland " )
self . send_stream_message ( self . example_user ( " othello " ) , " Scotland " )
self . send_stream_message ( self . example_user ( " othello " ) , " Scotland " )
2018-01-02 18:33:28 +01:00
self . send_personal_message (
2020-03-07 11:43:05 +01:00
self . example_user ( " othello " ) ,
self . example_user ( " hamlet " ) ,
2018-01-02 18:33:28 +01:00
)
# Add a few messages that help us test that our query doesn't
# look at messages that are irrelevant to Hamlet.
2020-03-07 11:43:05 +01:00
self . send_personal_message ( self . example_user ( " othello " ) , self . example_user ( " cordelia " ) )
self . send_personal_message ( self . example_user ( " othello " ) , self . example_user ( " iago " ) )
2018-01-02 18:33:28 +01:00
query_params = dict (
2020-01-29 03:29:15 +01:00
anchor = " first_unread " ,
2018-01-02 18:33:28 +01:00
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2018-01-02 18:33:28 +01:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2018-01-02 18:33:28 +01:00
first_visible_message_id = first_unread_message_id + 2
2024-07-14 20:30:42 +02:00
with first_visible_id_as ( first_visible_message_id ) , queries_captured ( ) as all_queries :
get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2018-01-02 18:33:28 +01:00
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if " /* get_messages */ " in q . sql ]
2021-05-17 05:41:32 +02:00
self . assert_length ( queries , 1 )
2023-06-06 23:54:19 +02:00
sql = queries [ 0 ] . sql
2021-02-12 08:20:45 +01:00
self . assertNotIn ( f " AND message_id = { LARGER_THAN_MAX_MESSAGE_ID } " , sql )
self . assertIn ( " ORDER BY message_id ASC " , sql )
cond = f " WHERE user_profile_id = { user_profile . id } AND message_id <= { first_unread_message_id - 1 } "
2018-01-02 18:33:28 +01:00
self . assertIn ( cond , sql )
2021-02-12 08:20:45 +01:00
cond = f " WHERE user_profile_id = { user_profile . id } AND message_id >= { first_visible_message_id } "
2016-07-23 18:41:39 +02:00
self . assertIn ( cond , sql )
2017-11-05 10:51:25 +01:00
def test_use_first_unread_anchor_with_no_unread_messages ( self ) - > None :
2021-02-12 08:20:45 +01:00
user_profile = self . example_user ( " hamlet " )
2016-07-23 18:41:39 +02:00
query_params = dict (
2020-01-29 03:29:15 +01:00
anchor = " first_unread " ,
2017-02-22 23:32:43 +01:00
num_before = 10 ,
num_after = 10 ,
2021-02-12 08:20:45 +01:00
narrow = " [] " ,
2016-07-23 18:41:39 +02:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2016-07-23 18:41:39 +02:00
with queries_captured ( ) as all_queries :
2024-06-09 13:10:17 +02:00
get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2016-07-23 18:41:39 +02:00
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if " /* get_messages */ " in q . sql ]
2021-05-17 05:41:32 +02:00
self . assert_length ( queries , 1 )
2018-03-12 20:33:00 +01:00
2023-06-06 23:54:19 +02:00
sql = queries [ 0 ] . sql
2018-03-12 20:33:00 +01:00
2021-02-12 08:20:45 +01:00
self . assertNotIn ( " AND message_id <= " , sql )
self . assertNotIn ( " AND message_id >= " , sql )
2016-07-23 18:41:39 +02:00
2022-05-12 08:28:00 +02:00
request = HostRequestMock ( query_params , user_profile )
2018-01-02 18:33:28 +01:00
first_visible_message_id = 5
2018-03-15 11:58:25 +01:00
with first_visible_id_as ( first_visible_message_id ) :
2018-01-02 18:33:28 +01:00
with queries_captured ( ) as all_queries :
2024-06-09 13:10:17 +02:00
get_messages_backend (
request ,
user_profile ,
num_before = 10 ,
num_after = 10 ,
)
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if " /* get_messages */ " in q . sql ]
sql = queries [ 0 ] . sql
2021-02-12 08:20:45 +01:00
self . assertNotIn ( " AND message_id <= " , sql )
self . assertNotIn ( " AND message_id >= " , sql )
2018-01-02 18:33:28 +01:00
2017-11-05 10:51:25 +01:00
def test_use_first_unread_anchor_with_muted_topics ( self ) - > 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 .
"""
2021-02-12 08:20:45 +01:00
realm = get_realm ( " zulip " )
self . make_stream ( " web stuff " )
self . make_stream ( " bogus " )
user_profile = self . example_user ( " hamlet " )
2017-08-24 17:58:40 +02:00
muted_topics = [
2021-02-12 08:20:45 +01:00
[ " Scotland " , " golf " ] ,
[ " web stuff " , " css " ] ,
[ " bogus " , " bogus " ] ,
2017-08-24 17:58:40 +02:00
]
2023-03-26 15:36:01 +02:00
set_topic_visibility_policy ( user_profile , muted_topics , UserTopic . VisibilityPolicy . MUTED )
2016-06-21 21:05:44 +02:00
query_params = dict (
2020-01-29 03:29:15 +01:00
anchor = " first_unread " ,
2016-06-21 21:05:44 +02:00
num_before = 0 ,
num_after = 0 ,
2024-03-14 16:29:27 +01:00
narrow = ' [[ " channel " , " Scotland " ]] ' ,
2016-06-21 21:05:44 +02:00
)
2021-02-07 21:34:01 +01:00
request = HostRequestMock ( query_params , user_profile )
2016-06-21 21:05:44 +02:00
2016-07-23 18:07:08 +02:00
with queries_captured ( ) as all_queries :
2024-06-09 13:10:17 +02:00
get_messages_backend (
request ,
user_profile ,
num_before = 0 ,
num_after = 0 ,
)
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.
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if q . sql . startswith ( " SELECT message_id, flags " ) ]
2021-05-17 05:41:32 +02:00
self . assert_length ( queries , 1 )
2016-07-23 18:07:08 +02:00
2024-03-14 13:48:06 +01:00
channel = get_stream ( " Scotland " , realm )
assert channel . recipient is not None
recipient_id = channel . recipient . id
2020-06-09 00:25:09 +02:00
cond = f " AND NOT (recipient_id = { recipient_id } AND upper(subject) = upper( ' golf ' )) "
2023-06-06 23:54:19 +02:00
self . assertIn ( cond , queries [ 0 ] . sql )
2016-07-23 18:07:08 +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.
2023-06-06 23:54:19 +02:00
queries = [ q for q in all_queries if " /* get_messages */ " in q . sql ]
2021-05-17 05:41:32 +02:00
self . assert_length ( queries , 1 )
2023-06-06 23:54:19 +02:00
self . assertIn ( f " AND zerver_message.id = { LARGER_THAN_MAX_MESSAGE_ID } " , queries [ 0 ] . sql )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_exclude_muting_conditions ( self ) - > None :
2021-02-12 08:20:45 +01:00
realm = get_realm ( " zulip " )
self . make_stream ( " web stuff " )
user_profile = self . example_user ( " hamlet " )
2016-07-23 17:07:38 +02:00
2024-03-14 13:48:06 +01:00
self . make_stream ( " irrelevant_channel " )
2017-08-30 02:19:34 +02:00
2016-07-23 17:07:38 +02:00
# Test the do-nothing case first.
2017-08-24 17:58:40 +02:00
muted_topics = [
2024-03-14 13:48:06 +01:00
[ " irrelevant_channel " , " irrelevant_topic " ] ,
2017-08-24 17:58:40 +02:00
]
2023-03-26 15:36:01 +02:00
set_topic_visibility_policy ( user_profile , muted_topics , UserTopic . VisibilityPolicy . MUTED )
2016-07-23 17:07:38 +02:00
# If nothing relevant is muted, then exclude_muting_conditions()
# should return an empty list.
2024-07-12 02:30:17 +02:00
narrow : list [ NarrowParameter ] = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " Scotland " ) ,
2016-07-23 17:07:38 +02:00
]
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
self . assertEqual ( muting_conditions , [ ] )
2024-03-14 13:48:06 +01:00
# Also test that passing channel ID works
channel_id = get_stream ( " Scotland " , realm ) . id
2019-08-07 17:32:19 +02:00
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = channel_id ) ,
2019-08-07 17:32:19 +02:00
]
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
self . assertEqual ( muting_conditions , [ ] )
2016-07-23 17:07:38 +02:00
# 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 = [
2021-02-12 08:20:45 +01:00
[ " Scotland " , " golf " ] ,
[ " web stuff " , " css " ] ,
2017-08-24 17:58:40 +02:00
]
2023-03-26 15:36:01 +02:00
set_topic_visibility_policy ( user_profile , muted_topics , UserTopic . VisibilityPolicy . MUTED )
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 = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " Scotland " ) ,
2016-06-21 21:05:44 +02:00
]
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
2022-02-10 04:13:15 +01:00
query = select ( column ( " id " , Integer ) . label ( " message_id " ) ) . select_from (
table ( " zerver_message " )
)
2016-06-21 21:05:44 +02:00
query = query . where ( * muting_conditions )
2021-02-12 08:20:45 +01:00
expected_query = """ \
2019-08-28 22:35:22 +02:00
SELECT id AS message_id \n \
FROM zerver_message \n \
WHERE NOT ( recipient_id = % ( recipient_id_1 ) s AND upper ( subject ) = upper ( % ( param_1 ) s ) ) \
2021-02-12 08:20:45 +01:00
"""
2019-08-28 22:35:22 +02:00
self . assertEqual ( get_sqlalchemy_sql ( query ) , expected_query )
2016-06-21 21:05:44 +02:00
params = get_sqlalchemy_query_params ( query )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2024-03-14 13:48:06 +01:00
params [ " recipient_id_1 " ] , get_recipient_id_for_channel_name ( realm , " Scotland " )
2021-02-12 08:19:30 +01:00
)
2021-02-12 08:20:45 +01:00
self . assertEqual ( params [ " param_1 " ] , " golf " )
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
mute_channel ( realm , user_profile , " Verona " )
2017-09-20 22:02:22 +02:00
2024-03-14 13:48:06 +01:00
# Using a bogus channel name should be similar to using no narrow at
2017-09-20 22:02:22 +02:00
# all, and we'll exclude all mutes.
narrow = [
2024-06-19 15:47:34 +02:00
NarrowParameter ( operator = " channel " , operand = " bogus-channel-name " ) ,
2017-09-20 22:02:22 +02:00
]
2016-06-21 21:05:44 +02:00
muting_conditions = exclude_muting_conditions ( user_profile , narrow )
2022-02-10 04:13:15 +01:00
query = select ( column ( " id " , Integer ) ) . select_from ( table ( " zerver_message " ) )
2016-06-21 21:05:44 +02:00
query = query . where ( and_ ( * muting_conditions ) )
2021-02-12 08:20:45 +01:00
expected_query = """ \
2019-08-28 22:35:22 +02:00
SELECT id \n \
FROM zerver_message \n \
2021-12-02 00:02:25 +01:00
WHERE ( recipient_id NOT IN ( __ [ POSTCOMPILE_recipient_id_1 ] ) ) \
2019-08-28 22:35:22 +02:00
AND NOT \
( recipient_id = % ( recipient_id_2 ) s AND upper ( subject ) = upper ( % ( param_1 ) s ) OR \
recipient_id = % ( recipient_id_3 ) s AND upper ( subject ) = upper ( % ( param_2 ) s ) ) \
2021-02-12 08:20:45 +01:00
"""
2019-08-28 22:35:22 +02:00
self . assertEqual ( get_sqlalchemy_sql ( query ) , expected_query )
2016-06-21 21:05:44 +02:00
params = get_sqlalchemy_query_params ( query )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2024-03-14 13:48:06 +01:00
params [ " recipient_id_1 " ] , [ get_recipient_id_for_channel_name ( realm , " Verona " ) ]
2021-02-12 08:19:30 +01:00
)
self . assertEqual (
2024-03-14 13:48:06 +01:00
params [ " recipient_id_2 " ] , get_recipient_id_for_channel_name ( realm , " Scotland " )
2021-02-12 08:19:30 +01:00
)
2021-02-12 08:20:45 +01:00
self . assertEqual ( params [ " param_1 " ] , " golf " )
2021-02-12 08:19:30 +01:00
self . assertEqual (
2024-03-14 13:48:06 +01:00
params [ " recipient_id_3 " ] , get_recipient_id_for_channel_name ( realm , " web stuff " )
2021-02-12 08:19:30 +01:00
)
2021-02-12 08:20:45 +01:00
self . assertEqual ( params [ " param_2 " ] , " css " )
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_get_messages_queries ( self ) - > None :
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
2021-02-12 08:20:45 +01: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 = 0) AS anon_1 ORDER BY message_id ASC "
2018-03-13 18:14:58 +01:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:20:45 +01:00
self . common_check_get_messages_query ( { " anchor " : 0 , " num_before " : 0 , " num_after " : 0 } , sql )
2018-03-13 18:14:58 +01:00
2021-02-12 08:20:45 +01: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 = 0) AS anon_1 ORDER BY message_id ASC "
2018-03-13 18:14:58 +01:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:20:45 +01:00
self . common_check_get_messages_query ( { " anchor " : 0 , " num_before " : 1 , " num_after " : 0 } , sql )
2018-03-13 18:14:58 +01:00
2021-02-12 08:20:45 +01: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} ORDER BY message_id ASC \n LIMIT 2) AS anon_1 ORDER BY message_id ASC "
2018-03-13 18:14:58 +01:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:20:45 +01:00
self . common_check_get_messages_query ( { " anchor " : 0 , " num_before " : 0 , " num_after " : 1 } , sql )
2018-03-13 18:14:58 +01:00
2021-02-12 08:20:45 +01: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} ORDER BY message_id ASC \n LIMIT 11) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:20:45 +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
2021-02-12 08:20:45 +01: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 "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:20:45 +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
2021-02-12 08:20:45 +01: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 "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2021-02-12 08:20:45 +01:00
{ " anchor " : 100 , " num_before " : 10 , " num_after " : 10 } , sql
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2017-11-05 10:51:25 +01:00
def test_get_messages_with_narrow_queries ( self ) - > None :
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
2021-02-12 08:20:45 +01:00
hamlet_email = self . example_user ( " hamlet " ) . email
othello_email = self . example_user ( " othello " ) . email
2016-06-21 21:05:44 +02:00
2023-09-27 06:02:24 +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 & 2048) != 0 AND realm_id = {realm_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) AS anon_1 ORDER BY message_id ASC "
2018-03-13 18:14:58 +01:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 0 ,
2023-04-03 17:52:19 +02:00
" narrow " : f ' [[ " dm " , " { othello_email } " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2018-03-13 18:14:58 +01:00
2023-09-27 06:02:24 +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 & 2048) != 0 AND realm_id = {realm_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) AS anon_1 ORDER BY message_id ASC "
2018-03-13 18:14:58 +01:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 1 ,
" num_after " : 0 ,
2023-04-03 17:52:19 +02:00
" narrow " : f ' [[ " dm " , " { othello_email } " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2018-03-13 18:14:58 +01:00
2023-09-27 06:02:24 +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 & 2048) != 0 AND realm_id = {realm_id} AND (sender_id = {othello_id} AND recipient_id = {hamlet_recipient} OR sender_id = {hamlet_id} AND recipient_id = {othello_recipient} ) ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2023-04-03 17:52:19 +02:00
" narrow " : f ' [[ " dm " , " { othello_email } " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2023-09-21 21:52:56 +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 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2021-02-12 08:20:45 +01:00
{ " anchor " : 0 , " num_before " : 0 , " num_after " : 9 , " narrow " : ' [[ " is " , " starred " ]] ' } , sql
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2023-09-21 21:52:56 +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} ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
" narrow " : f ' [[ " sender " , " { othello_email } " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2023-09-06 20:10:36 +02:00
sql_template = " SELECT anon_1.message_id \n FROM (SELECT id AS message_id \n FROM zerver_message \n WHERE realm_id = 2 AND recipient_id = {scotland_recipient} ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2024-03-14 16:29:27 +01:00
{ " anchor " : 0 , " num_before " : 0 , " num_after " : 9 , " narrow " : ' [[ " channel " , " Scotland " ]] ' } ,
2021-02-12 08:19:30 +01:00
sql ,
)
2016-06-21 21:05:44 +02:00
2024-03-14 13:48:06 +01:00
sql_template = " SELECT anon_1.message_id \n FROM (SELECT id AS message_id \n FROM zerver_message \n WHERE realm_id = 2 AND recipient_id IN ( {public_channels_recipients} ) ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2019-08-13 20:20:36 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2024-03-14 19:53:40 +01:00
{ " anchor " : 0 , " num_before " : 0 , " num_after " : 9 , " narrow " : ' [[ " channels " , " public " ]] ' } ,
sql ,
2021-02-12 08:19:30 +01:00
)
2019-08-13 20:20:36 +02:00
2024-03-14 13:48:06 +01: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 NOT IN ( {public_channels_recipients} )) ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2019-08-13 20:20:36 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2024-03-14 19:53:40 +01:00
" narrow " : ' [ { " operator " : " channels " , " operand " : " public " , " negated " : true}] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2019-08-13 20:20:36 +02:00
2023-09-21 21:52:56 +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 ' ) ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2021-02-12 08:20:45 +01:00
{ " anchor " : 0 , " num_before " : 0 , " num_after " : 9 , " narrow " : ' [[ " topic " , " blah " ]] ' } , sql
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2023-09-06 20:10:36 +02:00
sql_template = " SELECT anon_1.message_id \n FROM (SELECT id AS message_id \n FROM zerver_message \n WHERE realm_id = 2 AND recipient_id = {scotland_recipient} AND upper(subject) = upper( ' blah ' ) ORDER BY zerver_message.id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2024-03-14 16:29:27 +01:00
" narrow " : ' [[ " channel " , " Scotland " ], [ " topic " , " blah " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2023-04-03 17:52:19 +02:00
# Narrow to direct messages with yourself
2023-09-27 06:02:24 +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 & 2048) != 0 AND realm_id = {realm_id} AND sender_id = {hamlet_id} AND recipient_id = {hamlet_recipient} ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2023-04-03 17:52:19 +02:00
" narrow " : f ' [[ " dm " , " { hamlet_email } " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2023-09-21 21:52:56 +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 ORDER BY message_id ASC \n LIMIT 10) AS anon_1 ORDER BY message_id ASC "
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2024-03-14 16:29:27 +01:00
" narrow " : ' [[ " channel " , " Scotland " ], [ " is " , " starred " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2016-04-24 17:08:51 +02:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_search_queries ( self ) - > None :
2016-06-21 21:05:44 +02:00
query_ids = self . get_query_ids ( )
2019-08-28 11:06:38 +02:00
sql_template = """ \
2023-08-16 02:51:03 +02:00
SELECT anon_1 . message_id , anon_1 . flags , anon_1 . subject , anon_1 . rendered_content , anon_1 . content_matches , anon_1 . topic_matches \n \
FROM ( SELECT message_id , flags , subject , rendered_content , array ( ( SELECT ARRAY [ sum ( length ( anon_3 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_3 , ' </ts-match> ' ) - 1 ] AS anon_2 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , rendered_content , plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_3 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS content_matches , array ( ( SELECT ARRAY [ sum ( length ( anon_5 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_5 , ' </ts-match> ' ) - 1 ] AS anon_4 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , escape_html ( subject ) , plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_5 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS topic_matches \n \
FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage . message_id = zerver_message . id \n \
2023-09-21 21:52:56 +02:00
WHERE user_profile_id = { hamlet_id } AND ( search_tsvector @ @ plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) ) ORDER BY message_id ASC \n \
2019-08-28 11:06:38 +02:00
LIMIT 10 ) AS anon_1 ORDER BY message_id ASC \
"""
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
2021-02-12 08:20:45 +01:00
{ " anchor " : 0 , " num_before " : 0 , " num_after " : 9 , " narrow " : ' [[ " search " , " jumping " ]] ' } , sql
2021-02-12 08:19:30 +01:00
)
2016-06-21 21:05:44 +02:00
2019-08-28 11:06:38 +02:00
sql_template = """ \
2023-08-16 02:51:03 +02:00
SELECT anon_1 . message_id , anon_1 . subject , anon_1 . rendered_content , anon_1 . content_matches , anon_1 . topic_matches \n \
FROM ( SELECT id AS message_id , subject , rendered_content , array ( ( SELECT ARRAY [ sum ( length ( anon_3 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_3 , ' </ts-match> ' ) - 1 ] AS anon_2 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , rendered_content , plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_3 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS content_matches , array ( ( SELECT ARRAY [ sum ( length ( anon_5 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_5 , ' </ts-match> ' ) - 1 ] AS anon_4 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , escape_html ( subject ) , plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_5 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS topic_matches \n \
FROM zerver_message \n \
2023-09-06 20:10:36 +02:00
WHERE realm_id = 2 AND recipient_id = { scotland_recipient } AND ( search_tsvector @ @ plainto_tsquery ( ' zulip.english_us_search ' , ' jumping ' ) ) ORDER BY zerver_message . id ASC \n \
2019-08-28 11:06:38 +02:00
LIMIT 10 ) AS anon_1 ORDER BY message_id ASC \
"""
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
2024-03-14 16:29:27 +01:00
" narrow " : ' [[ " channel " , " Scotland " ], [ " search " , " jumping " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2016-06-21 21:05:44 +02:00
2019-08-28 11:06:38 +02:00
sql_template = """ \
2023-08-16 02:51:03 +02:00
SELECT anon_1 . message_id , anon_1 . flags , anon_1 . subject , anon_1 . rendered_content , anon_1 . content_matches , anon_1 . topic_matches \n \
FROM ( SELECT message_id , flags , subject , rendered_content , array ( ( SELECT ARRAY [ sum ( length ( anon_3 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_3 , ' </ts-match> ' ) - 1 ] AS anon_2 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , rendered_content , plainto_tsquery ( ' zulip.english_us_search ' , ' " jumping " quickly ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_3 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS content_matches , array ( ( SELECT ARRAY [ sum ( length ( anon_5 ) - 11 ) OVER ( ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING ) + 11 , strpos ( anon_5 , ' </ts-match> ' ) - 1 ] AS anon_4 \n \
2021-08-21 01:07:28 +02:00
FROM unnest ( string_to_array ( ts_headline ( ' zulip.english_us_search ' , escape_html ( subject ) , plainto_tsquery ( ' zulip.english_us_search ' , ' " jumping " quickly ' ) , ' HighlightAll = TRUE, StartSel = <ts-match>, StopSel = </ts-match> ' ) , ' <ts-match> ' ) ) AS anon_5 \n \
2019-08-28 11:06:38 +02:00
LIMIT ALL OFFSET 1 ) ) AS topic_matches \n \
FROM zerver_usermessage JOIN zerver_message ON zerver_usermessage . message_id = zerver_message . id \n \
2023-09-21 21:52:56 +02:00
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 ' ) ) ORDER BY message_id ASC \n \
2019-08-28 11:06:38 +02:00
LIMIT 10 ) AS anon_1 ORDER BY message_id ASC \
"""
2016-06-21 21:05:44 +02:00
sql = sql_template . format ( * * query_ids )
2021-02-12 08:19:30 +01:00
self . common_check_get_messages_query (
{
2021-02-12 08:20:45 +01:00
" anchor " : 0 ,
" num_before " : 0 ,
" num_after " : 9 ,
" narrow " : ' [[ " search " , " \\ " jumping \\ " quickly " ]] ' ,
2021-02-12 08:19:30 +01:00
} ,
sql ,
)
2017-04-29 00:03:43 +02:00
@override_settings ( USING_PGROONGA = False )
2017-11-05 10:51:25 +01:00
def test_get_messages_with_search_using_email ( self ) - > None :
2021-02-12 08:20:45 +01:00
self . login ( " cordelia " )
2017-04-29 00:03:43 +02:00
2021-02-12 08:20:45 +01:00
othello = self . example_user ( " othello " )
cordelia = self . example_user ( " cordelia " )
2020-03-12 14:17:25 +01:00
2017-04-29 00:03:43 +02:00
messages_to_search = [
2021-02-12 08:20:45 +01:00
( " say hello " , " How are you doing, @**Othello, the Moor of Venice**? " ) ,
( " lunch plans " , " I am hungry! " ) ,
2017-04-29 00:03:43 +02:00
]
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 (
2024-03-14 13:48:06 +01:00
cordelia ,
" 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 = [
2021-02-12 08:20:45 +01:00
dict ( operator = " sender " , operand = cordelia . email ) ,
dict ( operator = " search " , operand = othello . email ) ,
2017-04-29 00:03:43 +02:00
]
2024-07-12 02:30:17 +02:00
result : dict [ str , Any ] = self . get_and_check_messages (
2021-02-12 08:19:30 +01:00
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 0 )
2017-04-29 00:03:43 +02:00
narrow = [
2021-02-12 08:20:45 +01:00
dict ( operator = " sender " , operand = cordelia . email ) ,
dict ( operator = " search " , operand = " othello " ) ,
2017-04-29 00:03:43 +02:00
]
2021-02-12 08:19:30 +01:00
result = self . get_and_check_messages (
dict (
narrow = orjson . dumps ( narrow ) . decode ( ) ,
anchor = next_message_id ,
num_after = 10 ,
)
)
2021-05-17 05:41:32 +02:00
self . assert_length ( result [ " messages " ] , 1 )
2021-02-12 08:20:45 +01:00
messages = result [ " messages " ]
2017-04-29 00:03:43 +02:00
2021-08-02 23:16:44 +02:00
( hello_message , ) = ( m for m in messages if m [ TOPIC_NAME ] == " say hello " )
2021-02-12 08:20:45 +01:00
self . assertEqual ( hello_message [ MATCH_TOPIC ] , " say hello " )
2017-04-29 00:03:43 +02:00
self . assertEqual (
2021-02-12 08:20:45 +01:00
hello_message [ " match_content " ] ,
2020-06-14 02:57:50 +02:00
f ' <p>How are you doing, <span class= " user-mention " data-user-id= " { othello . id } " > '
' @<span class= " highlight " >Othello</span>, the Moor of Venice</span>?</p> ' ,
)
2020-07-08 00:44:42 +02:00
2021-02-12 08:19:30 +01:00
2020-07-08 00:44:42 +02:00
class MessageHasKeywordsTest ( ZulipTestCase ) :
2021-02-12 08:20:45 +01:00
""" Test for keywords like has_link, has_image, has_attachment. """
2020-07-08 00:44:42 +02:00
2024-07-12 02:30:17 +02:00
def setup_dummy_attachments ( self , user_profile : UserProfile ) - > list [ str ] :
2020-07-08 00:44:42 +02:00
realm_id = user_profile . realm_id
dummy_files = [
2024-06-20 20:09:08 +02:00
( " zulip.txt " , f " { realm_id } /31/4CBjtTLYZhk66pZrF8hnYGwc/zulip.txt " ) ,
( " temp_file.py " , f " { realm_id } /31/4CBjtTLYZhk66pZrF8hnYGwc/temp_file.py " ) ,
( " abc.py " , f " { realm_id } /31/4CBjtTLYZhk66pZrF8hnYGwc/abc.py " ) ,
2020-07-08 00:44:42 +02:00
]
2024-06-20 20:09:08 +02:00
for file_name , path_id in dummy_files :
2024-06-20 18:54:49 +02:00
create_attachment (
2024-06-20 20:09:08 +02:00
file_name , path_id , " text/plain " , b " 1234567890 " , user_profile , user_profile . realm
2024-06-20 18:54:49 +02:00
)
2020-07-08 00:44:42 +02:00
# return path ids
return [ x [ 1 ] for x in dummy_files ]
def test_claim_attachment ( self ) - > None :
2021-02-12 08:20:45 +01:00
user_profile = self . example_user ( " hamlet " )
2020-07-08 00:44:42 +02:00
dummy_path_ids = self . setup_dummy_attachments ( user_profile )
dummy_urls = [ f " http://zulip.testserver/user_uploads/ { x } " for x in dummy_path_ids ]
# Send message referring the attachment
self . subscribe ( user_profile , " Denmark " )
def assert_attachment_claimed ( path_id : str , claimed : bool ) - > None :
attachment = Attachment . objects . get ( path_id = path_id )
self . assertEqual ( attachment . is_claimed ( ) , claimed )
# This message should claim attachments 1 only because attachment 2
# is not being parsed as a link by Markdown.
2021-02-12 08:19:30 +01:00
body = (
f " Some files here ...[zulip.txt]( { dummy_urls [ 0 ] } ) "
f " { dummy_urls [ 1 ] } .... Some more.... "
f " { dummy_urls [ 1 ] } "
)
2020-07-08 00:44:42 +02:00
self . send_stream_message ( user_profile , " Denmark " , body , " test " )
assert_attachment_claimed ( dummy_path_ids [ 0 ] , True )
assert_attachment_claimed ( dummy_path_ids [ 1 ] , False )
# This message tries to claim the third attachment but fails because
# Markdown would not set has_attachments = True here.
body = f " Link in code: ` { dummy_urls [ 2 ] } ` "
self . send_stream_message ( user_profile , " Denmark " , body , " test " )
assert_attachment_claimed ( dummy_path_ids [ 2 ] , False )
# Another scenario where we wouldn't parse the link.
body = f " Link to not parse: . { dummy_urls [ 2 ] } .` "
self . send_stream_message ( user_profile , " Denmark " , body , " test " )
assert_attachment_claimed ( dummy_path_ids [ 2 ] , False )
# Finally, claim attachment 3.
body = f " Link: { dummy_urls [ 2 ] } "
self . send_stream_message ( user_profile , " Denmark " , body , " test " )
assert_attachment_claimed ( dummy_path_ids [ 2 ] , True )
assert_attachment_claimed ( dummy_path_ids [ 1 ] , False )
def test_finds_all_links ( self ) - > None :
msg_contents = [ " foo.org " , " [bar](baz.gov) " , " http://quux.ca " ]
2023-07-31 22:52:35 +02:00
msg_ids = [
self . send_stream_message ( self . example_user ( " hamlet " ) , " Denmark " , content = msg_content )
for msg_content in msg_contents
]
2020-07-08 00:44:42 +02:00
msgs = [ Message . objects . get ( id = id ) for id in msg_ids ]
2020-09-02 06:20:26 +02:00
self . assertTrue ( all ( msg . has_link for msg in msgs ) )
2020-07-08 00:44:42 +02:00
def test_finds_only_links ( self ) - > None :
2021-02-12 08:20:45 +01:00
msg_contents = [ " `example.org` " , " ``example.org``` " , " $$https://example.org$$ " , " foo " ]
2023-07-31 22:52:35 +02:00
msg_ids = [
self . send_stream_message ( self . example_user ( " hamlet " ) , " Denmark " , content = msg_content )
for msg_content in msg_contents
]
2020-07-08 00:44:42 +02:00
msgs = [ Message . objects . get ( id = id ) for id in msg_ids ]
2020-09-02 06:20:26 +02:00
self . assertFalse ( all ( msg . has_link for msg in msgs ) )
2020-07-08 00:44:42 +02:00
def update_message ( self , msg : Message , content : str ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
2020-07-08 00:44:42 +02:00
realm_id = hamlet . realm . id
2023-10-03 03:22:59 +02:00
rendering_result = render_message_markdown ( msg , content )
2021-12-29 13:52:27 +01:00
mention_backend = MentionBackend ( realm_id )
2023-12-02 08:54:36 +01:00
mention_data = MentionData ( mention_backend , content , msg . sender )
2021-02-12 08:19:30 +01:00
do_update_message (
hamlet ,
msg ,
None ,
None ,
" change_one " ,
False ,
False ,
content ,
2021-06-17 12:20:40 +02:00
rendering_result ,
2021-02-12 08:19:30 +01:00
set ( ) ,
mention_data = mention_data ,
)
2020-07-08 00:44:42 +02:00
def test_finds_link_after_edit ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
msg_id = self . send_stream_message ( hamlet , " Denmark " , content = " a " )
2020-07-08 00:44:42 +02:00
msg = Message . objects . get ( id = msg_id )
self . assertFalse ( msg . has_link )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , " a http://foo.com " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msg . has_link )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , " a " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msg . has_link )
# Check in blockquotes work
2021-02-12 08:20:45 +01:00
self . update_message ( msg , " > http://bar.com " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msg . has_link )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , " a `http://foo.com` " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msg . has_link )
def test_has_image ( self ) - > None :
2021-02-12 08:19:30 +01:00
msg_contents = [
" Link: foo.org " ,
" Image: https://www.google.com/images/srpr/logo4w.png " ,
" Image: https://www.google.com/images/srpr/logo4w.pdf " ,
" [Google link](https://www.google.com/images/srpr/logo4w.png) " ,
]
2023-07-31 22:52:35 +02:00
msg_ids = [
self . send_stream_message ( self . example_user ( " hamlet " ) , " Denmark " , content = msg_content )
for msg_content in msg_contents
]
2020-07-08 00:44:42 +02:00
msgs = [ Message . objects . get ( id = id ) for id in msg_ids ]
self . assertEqual ( [ False , True , False , True ] , [ msg . has_image for msg in msgs ] )
2021-02-12 08:20:45 +01:00
self . update_message ( msgs [ 0 ] , " https://www.google.com/images/srpr/logo4w.png " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msgs [ 0 ] . has_image )
2021-02-12 08:20:45 +01:00
self . update_message ( msgs [ 0 ] , " No image again " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msgs [ 0 ] . has_image )
def test_has_attachment ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
2020-07-08 00:44:42 +02:00
dummy_path_ids = self . setup_dummy_attachments ( hamlet )
dummy_urls = [ f " http://zulip.testserver/user_uploads/ { x } " for x in dummy_path_ids ]
self . subscribe ( hamlet , " Denmark " )
2023-07-19 23:06:38 +02:00
body = f " Files ...[zulip.txt]( { dummy_urls [ 0 ] } ) { dummy_urls [ 1 ] } { dummy_urls [ 2 ] } "
2020-07-08 00:44:42 +02:00
msg_id = self . send_stream_message ( hamlet , " Denmark " , body , " test " )
msg = Message . objects . get ( id = msg_id )
self . assertTrue ( msg . has_attachment )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , " No attachments " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msg . has_attachment )
self . update_message ( msg , body )
self . assertTrue ( msg . has_attachment )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " Link in code: ` { dummy_urls [ 1 ] } ` " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msg . has_attachment )
# Test blockquotes
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " > { dummy_urls [ 1 ] } " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msg . has_attachment )
# Additional test to check has_attachment is being set is due to the correct attachment.
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " Outside: { dummy_urls [ 0 ] } . In code: ` { dummy_urls [ 1 ] } `. " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msg . has_attachment )
self . assertTrue ( msg . attachment_set . filter ( path_id = dummy_path_ids [ 0 ] ) )
self . assertEqual ( msg . attachment_set . count ( ) , 1 )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " Outside: { dummy_urls [ 1 ] } . In code: ` { dummy_urls [ 0 ] } `. " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( msg . has_attachment )
self . assertTrue ( msg . attachment_set . filter ( path_id = dummy_path_ids [ 1 ] ) )
self . assertEqual ( msg . attachment_set . count ( ) , 1 )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " Both in code: ` { dummy_urls [ 1 ] } { dummy_urls [ 0 ] } `. " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( msg . has_attachment )
self . assertEqual ( msg . attachment_set . count ( ) , 0 )
def test_potential_attachment_path_ids ( self ) - > None :
2021-02-12 08:20:45 +01:00
hamlet = self . example_user ( " hamlet " )
2020-07-08 00:44:42 +02:00
self . subscribe ( hamlet , " Denmark " )
dummy_path_ids = self . setup_dummy_attachments ( hamlet )
body = " Hello "
msg_id = self . send_stream_message ( hamlet , " Denmark " , body , " test " )
msg = Message . objects . get ( id = msg_id )
2022-04-14 23:43:26 +02:00
with mock . patch (
" zerver.actions.uploads.do_claim_attachments " , wraps = do_claim_attachments
) as m :
2021-02-12 08:19:30 +01:00
self . update_message (
2021-02-12 08:20:45 +01:00
msg , f " [link](http:// { hamlet . realm . host } /user_uploads/ { dummy_path_ids [ 0 ] } ) "
2021-02-12 08:19:30 +01:00
)
2020-07-08 00:44:42 +02:00
self . assertTrue ( m . called )
m . reset_mock ( )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " [link](/user_uploads/ { dummy_path_ids [ 1 ] } ) " )
2020-07-08 00:44:42 +02:00
self . assertTrue ( m . called )
m . reset_mock ( )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " [new text link](/user_uploads/ { dummy_path_ids [ 1 ] } ) " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( m . called )
m . reset_mock ( )
# It's not clear this is correct behavior
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " [link](user_uploads/ { dummy_path_ids [ 2 ] } ) " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( m . called )
m . reset_mock ( )
2021-02-12 08:20:45 +01:00
self . update_message ( msg , f " [link](https://github.com/user_uploads/ { dummy_path_ids [ 0 ] } ) " )
2020-07-08 00:44:42 +02:00
self . assertFalse ( m . called )
m . reset_mock ( )
2020-07-08 01:29:42 +02:00
2024-04-02 06:24:20 +02:00
def test_has_reaction ( self ) - > None :
self . login ( " iago " )
has_reaction_narrow = orjson . dumps ( [ dict ( operator = " has " , operand = " reaction " ) ] ) . decode ( )
msg_id = self . send_stream_message ( self . example_user ( " hamlet " ) , " Denmark " , content = " Hey " )
result = self . client_get (
" /json/messages " ,
dict ( narrow = has_reaction_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 0 )
check_add_reaction (
self . example_user ( " hamlet " ) , msg_id , " hamburger " , " 1f354 " , " unicode_emoji "
)
result = self . client_get (
" /json/messages " ,
dict ( narrow = has_reaction_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 1 )
msg_id = self . send_personal_message (
self . example_user ( " iago " ) , self . example_user ( " cordelia " ) , " Hello Cordelia "
)
result = self . client_get (
" /json/messages " ,
dict ( narrow = has_reaction_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 0 )
check_add_reaction ( self . example_user ( " iago " ) , msg_id , " hamburger " , " 1f354 " , " unicode_emoji " )
result = self . client_get (
" /json/messages " ,
dict ( narrow = has_reaction_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 1 )
2023-10-25 19:44:52 +02:00
class MessageIsTest ( ZulipTestCase ) :
def test_message_is_followed ( self ) - > None :
self . login ( " iago " )
is_followed_narrow = orjson . dumps ( [ dict ( operator = " is " , operand = " followed " ) ] ) . decode ( )
# Sending a message in a topic that isn't followed by the user.
msg_id = self . send_stream_message ( self . example_user ( " hamlet " ) , " Denmark " , topic_name = " hey " )
result = self . client_get (
" /json/messages " ,
dict ( narrow = is_followed_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 0 )
stream_id = self . get_stream_id ( " Denmark " , self . example_user ( " hamlet " ) . realm )
# Following the topic.
payload = {
" stream_id " : stream_id ,
" topic " : " hey " ,
" visibility_policy " : int ( UserTopic . VisibilityPolicy . FOLLOWED ) ,
}
self . client_post ( " /json/user_topics " , payload )
result = self . client_get (
" /json/messages " ,
dict ( narrow = is_followed_narrow , anchor = msg_id , num_before = 0 , num_after = 0 ) ,
)
messages = self . assert_json_success ( result ) [ " messages " ]
self . assert_length ( messages , 1 )
2024-04-02 06:24:20 +02:00
2021-02-12 08:19:30 +01:00
2020-07-08 01:29:42 +02:00
class MessageVisibilityTest ( ZulipTestCase ) :
def test_update_first_visible_message_id ( self ) - > None :
Message . objects . all ( ) . delete ( )
2021-02-12 08:19:30 +01:00
message_ids = [
self . send_stream_message ( self . example_user ( " othello " ) , " Scotland " ) for i in range ( 15 )
]
2020-07-08 01:29:42 +02:00
# If message_visibility_limit is None update_first_visible_message_id
# should set first_visible_message_id to 0
realm = get_realm ( " zulip " )
realm . message_visibility_limit = None
# Setting to a random value other than 0 as the default value of
# first_visible_message_id is 0
realm . first_visible_message_id = 5
realm . save ( )
update_first_visible_message_id ( realm )
self . assertEqual ( get_first_visible_message_id ( realm ) , 0 )
realm . message_visibility_limit = 10
realm . save ( )
expected_message_id = message_ids [ 5 ]
update_first_visible_message_id ( realm )
self . assertEqual ( get_first_visible_message_id ( realm ) , expected_message_id )
# If the message_visibility_limit is greater than number of messages
# get_first_visible_message_id should return 0
realm . message_visibility_limit = 50
realm . save ( )
update_first_visible_message_id ( realm )
self . assertEqual ( get_first_visible_message_id ( realm ) , 0 )
def test_maybe_update_first_visible_message_id ( self ) - > None :
realm = get_realm ( " zulip " )
lookback_hours = 30
realm . message_visibility_limit = None
realm . save ( )
2023-11-19 19:45:19 +01:00
end_time = timezone_now ( ) - timedelta ( hours = lookback_hours - 5 )
2021-02-12 08:20:45 +01:00
stat = COUNT_STATS [ " messages_sent:is_bot:hour " ]
2020-07-08 01:29:42 +02:00
2021-02-12 08:19:30 +01:00
RealmCount . objects . create ( realm = realm , property = stat . property , end_time = end_time , value = 5 )
2020-07-08 01:29:42 +02:00
with mock . patch ( " zerver.lib.message.update_first_visible_message_id " ) as m :
maybe_update_first_visible_message_id ( realm , lookback_hours )
m . assert_not_called ( )
realm . message_visibility_limit = 10
realm . save ( )
RealmCount . objects . all ( ) . delete ( )
with mock . patch ( " zerver.lib.message.update_first_visible_message_id " ) as m :
maybe_update_first_visible_message_id ( realm , lookback_hours )
m . assert_not_called ( )
2021-02-12 08:19:30 +01:00
RealmCount . objects . create ( realm = realm , property = stat . property , end_time = end_time , value = 5 )
2020-07-08 01:29:42 +02:00
with mock . patch ( " zerver.lib.message.update_first_visible_message_id " ) as m :
maybe_update_first_visible_message_id ( realm , lookback_hours )
m . assert_called_once_with ( realm )
2020-07-08 02:38:54 +02:00
2021-02-12 08:19:30 +01:00
2020-07-08 02:38:54 +02:00
class PersonalMessagesNearTest ( ZulipTestCase ) :
def test_near_pm_message_url ( self ) - > None :
2021-02-12 08:20:45 +01:00
realm = get_realm ( " zulip " )
2020-07-08 02:38:54 +02:00
message = dict (
2021-02-12 08:20:45 +01:00
type = " personal " ,
2020-07-08 02:38:54 +02:00
id = 555 ,
display_recipient = [
dict ( id = 77 ) ,
dict ( id = 80 ) ,
] ,
)
url = near_message_url (
realm = realm ,
message = message ,
)
2023-04-14 17:04:06 +02:00
self . assertEqual ( url , " http://zulip.testserver/#narrow/dm/77,80-pm/near/555 " )