message: Differentiate between new local messages and server messages.

This commit is contained in:
Evy Kassirer
2025-09-02 14:10:58 -07:00
committed by Tim Abbott
parent 80b9cffb3d
commit 3c4fe73470
17 changed files with 348 additions and 140 deletions

View File

@@ -14,6 +14,7 @@ import * as echo_state from "./echo_state.ts";
import * as hash_util from "./hash_util.ts";
import * as local_message from "./local_message.ts";
import * as markdown from "./markdown.ts";
import type {InsertNewMessagesOpts} from "./message_events.ts";
import * as message_events_util from "./message_events_util.ts";
import * as message_list_data_cache from "./message_list_data_cache.ts";
import * as message_lists from "./message_lists.ts";
@@ -87,7 +88,7 @@ export type LocalMessage = MessageRequestObject & {
content_type: "text/html";
sender_email: string;
sender_full_name: string;
avatar_url?: string | null | undefined;
avatar_url: string;
timestamp: number;
local_id: string;
locally_echoed: boolean;
@@ -253,11 +254,7 @@ export function track_local_message(message: Message): void {
export function insert_local_message(
message_request: MessageRequest,
local_id_float: number,
insert_new_messages: (
messages: LocalMessage[],
send_by_this_client: boolean,
deliver_locally: boolean,
) => Message[],
insert_new_messages: (opts: InsertNewMessagesOpts) => Message[],
): Message {
// Shallow clone of message request object that is turned into something suitable
// for zulip.js:add_message
@@ -285,7 +282,11 @@ export function insert_local_message(
local_message.display_recipient = build_display_recipient(local_message);
const [message] = insert_new_messages([local_message], true, true);
const [message] = insert_new_messages({
type: "local_message",
raw_messages: [local_message],
sent_by_this_client: true,
});
assert(message !== undefined);
return message;
@@ -297,11 +298,7 @@ export function is_slash_command(content: string): boolean {
export let try_deliver_locally = (
message_request: MessageRequest,
insert_new_messages: (
messages: LocalMessage[],
send_by_this_client: boolean,
deliver_locally: boolean,
) => Message[],
insert_new_messages: (opts: InsertNewMessagesOpts) => Message[],
): Message | undefined => {
// Checks if the message request can be locally echoed, and if so,
// adds a local echoed copy of the message to appropriate message lists.

View File

@@ -16,6 +16,7 @@ import * as compose_validate from "./compose_validate.ts";
import * as direct_message_group_data from "./direct_message_group_data.ts";
import * as drafts from "./drafts.ts";
import * as echo from "./echo.ts";
import type {LocalMessage} from "./echo.ts";
import type {Filter} from "./filter.ts";
import * as lightbox from "./lightbox.ts";
import * as message_edit from "./message_edit.ts";
@@ -212,7 +213,11 @@ export let update_views_filtered_on_message_property = (
messages_to_remove.delete(raw_message.id);
const message = message_store.get(raw_message.id);
messages_to_add.push(
message ?? message_helper.process_new_message(raw_message),
message ??
message_helper.process_new_message({
type: "server_message",
raw_message,
}),
);
}
msg_list.data.remove([...messages_to_remove]);
@@ -243,7 +248,10 @@ export let update_views_filtered_on_message_property = (
// we reach here but `message_helper.process_new_message`
// already handles that case.
for (const raw_message of parsed_data.messages) {
message_helper.process_new_message(raw_message);
message_helper.process_new_message({
type: "server_message",
raw_message,
});
}
update_views_filtered_on_message_property(
message_ids,
@@ -292,14 +300,37 @@ export function rewire_update_views_filtered_on_message_property(
update_views_filtered_on_message_property = value;
}
export function insert_new_messages(
raw_messages: RawMessage[],
sent_by_this_client: boolean,
deliver_locally: boolean,
): Message[] {
const messages = raw_messages.map((raw_message) =>
message_helper.process_new_message(raw_message, deliver_locally),
);
export type InsertNewMessagesOpts = {
sent_by_this_client: boolean;
} & (
| {
type: "server_message";
raw_messages: RawMessage[];
}
| {
type: "local_message";
raw_messages: LocalMessage[];
}
);
export function insert_new_messages(opts: InsertNewMessagesOpts): Message[] {
const deliver_locally = opts.type === "local_message";
let messages: Message[] = [];
if (opts.type === "server_message") {
messages = opts.raw_messages.map((raw_message) =>
message_helper.process_new_message({
type: opts.type,
raw_message,
}),
);
} else {
messages = opts.raw_messages.map((raw_message) =>
message_helper.process_new_message({
type: opts.type,
raw_message,
}),
);
}
const any_untracked_unread_messages = unread.process_loaded_messages(messages, false);
direct_message_group_data.process_loaded_messages(messages);
@@ -355,7 +386,7 @@ export function insert_new_messages(
// sent_by_this_client will be true if ANY of the messages
// were sent by this client; notifications.notify_local_mixes
// will filter out any not sent by us.
if (sent_by_this_client) {
if (opts.sent_by_this_client) {
compose_notifications.notify_local_mixes(messages, need_user_to_scroll, {
narrow_to_recipient(message_id) {
message_view.narrow_by_topic(message_id, {trigger: "outside_current_view"});

View File

@@ -147,7 +147,10 @@ function process_result(data: MessageFetchResponse, opts: MessageFetchOptions):
const raw_messages = data.messages;
const messages = raw_messages.map((raw_message) =>
message_helper.process_new_message(raw_message),
message_helper.process_new_message({
type: "server_message",
raw_message,
}),
);
const has_found_oldest = opts.msg_list?.data.fetch_status.has_found_oldest() ?? false;
const has_found_newest = opts.msg_list?.data.fetch_status.has_found_newest() ?? false;

View File

@@ -2,7 +2,8 @@ import _ from "lodash";
import assert from "minimalistic-assert";
import * as alert_words from "./alert_words.ts";
import * as blueslip from "./blueslip.ts";
import type {LocalMessage} from "./echo.ts";
import {electron_bridge} from "./electron_bridge.ts";
import * as message_store from "./message_store.ts";
import type {Message, RawMessage} from "./message_store.ts";
import * as message_user_ids from "./message_user_ids.ts";
@@ -15,41 +16,47 @@ import * as stream_topic_history from "./stream_topic_history.ts";
import * as user_status from "./user_status.ts";
import * as util from "./util.ts";
export function process_new_message(raw_message: RawMessage, deliver_locally = false): Message {
export type NewMessage =
| {
type: "server_message";
raw_message: RawMessage;
}
| {
type: "local_message";
raw_message: LocalMessage;
};
export function process_new_message(opts: NewMessage): Message {
// Call this function when processing a new message. After
// a message is processed and inserted into the message store
// cache, most modules use message_store.get to look at
// messages.
const cached_msg = message_store.get_cached_message(raw_message.id);
const cached_msg = message_store.get_cached_message(opts.raw_message.id);
if (cached_msg !== undefined) {
// Copy the match topic and content over if they exist on
// the new message
if (util.get_match_topic(raw_message) !== undefined) {
util.set_match_data(cached_msg, raw_message);
// the new message. Local messages will never have match metadata,
// since we need the server to calculate it.
if (
opts.type === "server_message" &&
util.get_match_topic(opts.raw_message) !== undefined
) {
util.set_match_data(cached_msg, opts.raw_message);
}
return cached_msg;
}
const message_with_booleans =
message_store.convert_raw_message_to_message_with_booleans(raw_message);
people.extract_people_from_message(message_with_booleans);
const message_with_booleans = message_store.convert_raw_message_to_message_with_booleans(opts);
people.extract_people_from_message(message_with_booleans.message);
const sent_by_me = people.is_my_user_id(message_with_booleans.sender_id);
people.maybe_incr_recipient_count({...message_with_booleans, sent_by_me});
const sent_by_me = people.is_my_user_id(message_with_booleans.message.sender_id);
people.maybe_incr_recipient_count({...message_with_booleans.message, sent_by_me});
let status_emoji_info;
const sender = people.maybe_get_user_by_id(message_with_booleans.sender_id);
const sender = people.maybe_get_user_by_id(message_with_booleans.message.sender_id);
if (sender) {
message_with_booleans.sender_full_name = sender.full_name;
message_with_booleans.sender_email = sender.email;
status_emoji_info = user_status.get_status_emoji(message_with_booleans.sender_id);
}
// TODO: Remove this once we've converted more message code to typescript
// and also have confirmed we don't see this error for some period of time.
if (!raw_message.reactions) {
blueslip.error("expected raw_message to have reactions");
raw_message.reactions = [];
message_with_booleans.message.sender_full_name = sender.full_name;
message_with_booleans.message.sender_email = sender.email;
status_emoji_info = user_status.get_status_emoji(message_with_booleans.message.sender_id);
}
// TODO: Rather than adding this field to the message object, it
@@ -57,65 +64,127 @@ export function process_new_message(raw_message: RawMessage, deliver_locally = f
// => clean_reactions data for the message, with care being taken
// to make sure reify_message_id moves the data structure
// properly.
const clean_reactions = reactions.generate_clean_reactions(raw_message);
const clean_reactions = reactions.generate_clean_reactions(opts.raw_message);
let message: Message;
if (message_with_booleans.type === "stream") {
const topic = message_with_booleans.topic ?? message_with_booleans.subject;
if (message_with_booleans.message.type === "stream") {
let topic;
if (message_with_booleans.type === "local_message") {
topic = message_with_booleans.message.topic;
} else {
topic = message_with_booleans.message.topic ?? message_with_booleans.message.subject;
}
assert(topic !== undefined);
// We add fully delivered messages to stream_topic_history,
// being careful to not include locally echoed messages, which
// don't have permanent IDs and don't belong in that structure.
if (!deliver_locally) {
if (message_with_booleans.type === "server_message") {
stream_topic_history.add_message({
stream_id: message_with_booleans.stream_id,
stream_id: message_with_booleans.message.stream_id,
topic_name: topic,
message_id: message_with_booleans.id,
message_id: message_with_booleans.message.id,
});
}
recent_senders.process_stream_message({
stream_id: message_with_booleans.stream_id,
stream_id: message_with_booleans.message.stream_id,
topic,
sender_id: message_with_booleans.sender_id,
id: message_with_booleans.id,
sender_id: message_with_booleans.message.sender_id,
id: message_with_booleans.message.id,
});
message = {
..._.omit(message_with_booleans, ["reactions", "subject"]),
sent_by_me,
status_emoji_info,
is_private: false,
is_stream: true,
reply_to: message_with_booleans.sender_email,
topic,
stream: stream_data.get_stream_name_from_id(message_with_booleans.stream_id),
clean_reactions,
display_reply_to: undefined,
};
message.submessages ??= [];
if (message_with_booleans.type === "server_message") {
const {reactions, subject, ...rest} = message_with_booleans.message;
message = {
...rest,
sent_by_me,
status_emoji_info,
is_private: false,
is_stream: true,
reply_to: message_with_booleans.message.sender_email,
topic,
stream: stream_data.get_stream_name_from_id(
message_with_booleans.message.stream_id,
),
clean_reactions,
display_reply_to: undefined,
};
} else {
const {reactions, ...rest} = message_with_booleans.message;
// Ideally display_recipient would be not optional in LocalMessage
// or MessageWithBooleans, ideally by refactoring the use of
// `build_display_recipient`, but that's complicated to type right now.
// When we have a new format for `display_recipient` in message objects in
// the API itself, we'll naturally clean this up.
assert(rest.display_recipient !== undefined);
message = {
...rest,
sent_by_me,
status_emoji_info,
is_private: false,
is_stream: true,
reply_to: message_with_booleans.message.sender_email,
topic,
stream: stream_data.get_stream_name_from_id(
message_with_booleans.message.stream_id,
),
clean_reactions,
display_reply_to: undefined,
display_recipient: rest.display_recipient,
client: electron_bridge === undefined ? "website" : "ZulipElectron",
submessages: [],
};
}
message_user_ids.add_user_id(message.sender_id);
} else {
const pm_with_user_ids = people.pm_with_user_ids(message_with_booleans);
const pm_with_user_ids = people.pm_with_user_ids(message_with_booleans.message);
assert(pm_with_user_ids !== undefined);
const pm_with_url = people.pm_with_url(message_with_booleans);
const pm_with_url = people.pm_with_url(message_with_booleans.message);
assert(pm_with_url !== undefined);
const to_user_ids = people.pm_reply_user_string(message_with_booleans);
const to_user_ids = people.pm_reply_user_string(message_with_booleans.message);
assert(to_user_ids !== undefined);
message = {
..._.omit(message_with_booleans, "reactions"),
sent_by_me,
status_emoji_info,
is_private: true,
is_stream: false,
reply_to: util.normalize_recipients(message_store.get_pm_emails(message_with_booleans)),
display_reply_to: message_store.get_pm_full_names(pm_with_user_ids),
pm_with_url,
to_user_ids,
clean_reactions,
};
message.submessages ??= [];
if (message_with_booleans.type === "server_message") {
message = {
..._.omit(message_with_booleans.message, "reactions"),
sent_by_me,
status_emoji_info,
is_private: true,
is_stream: false,
reply_to: util.normalize_recipients(
message_store.get_pm_emails(message_with_booleans.message),
),
display_reply_to: message_store.get_pm_full_names(pm_with_user_ids),
pm_with_url,
to_user_ids,
clean_reactions,
};
} else {
const {reactions, topic_links, ...rest} = message_with_booleans.message;
// Ideally display_recipient would be not optional in LocalMessage
// or MessageWithBooleans, ideally by refactoring the use of
// `build_display_recipient`, but that's complicated to type right now.
// When we have a new format for `display_recipient` in message objects in
// the API itself, we'll naturally clean this up.
assert(rest.display_recipient !== undefined);
message = {
...rest,
sent_by_me,
status_emoji_info,
is_private: true,
is_stream: false,
reply_to: util.normalize_recipients(
message_store.get_pm_emails(message_with_booleans.message),
),
display_reply_to: message_store.get_pm_full_names(pm_with_user_ids),
pm_with_url,
to_user_ids,
clean_reactions,
display_recipient: rest.display_recipient,
submessages: [],
client: electron_bridge === undefined ? "website" : "ZulipElectron",
};
}
pm_conversations.process_message(message);

View File

@@ -2,6 +2,8 @@ import _ from "lodash";
import * as z from "zod/mini";
import * as blueslip from "./blueslip.ts";
import type {LocalMessage} from "./echo.ts";
import type {NewMessage} from "./message_helper.ts";
import * as people from "./people.ts";
import {topic_link_schema} from "./types.ts";
import type {UserStatusEmojiInfo} from "./user_status.ts";
@@ -108,10 +110,7 @@ export type RawMessage = z.infer<typeof raw_message_schema>;
// We add these boolean properties to Raw message in
// `message_store.convert_raw_message_to_message_with_booleans` method.
export type MessageWithBooleans = (
| Omit<RawMessage & {type: "private"}, "flags">
| Omit<RawMessage & {type: "stream"}, "flags">
) & {
type Booleans = {
unread: boolean;
historical: boolean;
starred: boolean;
@@ -124,6 +123,20 @@ export type MessageWithBooleans = (
alerted: boolean;
};
type RawMessageWithBooleans = (
| Omit<RawMessage & {type: "private"}, "flags">
| Omit<RawMessage & {type: "stream"}, "flags">
) &
Booleans;
type LocalMessageWithBooleans = (
| Omit<LocalMessage & {type: "private"}, "flags">
| Omit<LocalMessage & {type: "stream"}, "flags">
) &
Booleans;
export type MessageWithBooleans = RawMessageWithBooleans | LocalMessageWithBooleans;
export type MessageCleanReaction = {
class: string;
count: number;
@@ -139,8 +152,8 @@ export type MessageCleanReaction = {
};
export type Message = (
| Omit<MessageWithBooleans & {type: "private"}, "reactions">
| Omit<MessageWithBooleans & {type: "stream"}, "reactions" | "subject">
| Omit<RawMessageWithBooleans & {type: "private"}, "reactions">
| Omit<RawMessageWithBooleans & {type: "stream"}, "reactions" | "subject">
) & {
clean_reactions: Map<string, MessageCleanReaction>;
@@ -214,7 +227,9 @@ export function get(message_id: number): Message | undefined {
return stored_messages.get(message_id);
}
export function get_pm_emails(message: Message | MessageWithBooleans): string {
export function get_pm_emails(
message: Message | MessageWithBooleans | LocalMessageWithBooleans,
): string {
const user_ids = people.pm_with_user_ids(message) ?? [];
const emails = user_ids
.map((user_id) => {
@@ -238,10 +253,16 @@ export function get_pm_full_names(user_ids: number[]): string {
return sorted_names.join(", ");
}
export function convert_raw_message_to_message_with_booleans(
message: RawMessage,
): MessageWithBooleans {
const flags = message.flags ?? [];
export function convert_raw_message_to_message_with_booleans(opts: NewMessage):
| {
type: "server_message";
message: RawMessageWithBooleans;
}
| {
type: "local_message";
message: LocalMessageWithBooleans;
} {
const flags = opts.raw_message.flags ?? [];
function convert_flag(flag_name: string): boolean {
return flags.includes(flag_name);
@@ -268,15 +289,39 @@ export function convert_raw_message_to_message_with_booleans(
// We have to return these separately because of how the `MessageWithBooleans`
// type is set up.
if (message.type === "private") {
if (opts.type === "local_message") {
if (opts.raw_message.type === "private") {
return {
type: "local_message",
message: {
..._.omit(opts.raw_message, "flags"),
...converted_flags,
},
};
}
return {
..._.omit(message, "flags"),
...converted_flags,
type: "local_message",
message: {
..._.omit(opts.raw_message, "flags"),
...converted_flags,
},
};
}
if (opts.raw_message.type === "private") {
return {
type: "server_message",
message: {
..._.omit(opts.raw_message, "flags"),
...converted_flags,
},
};
}
return {
..._.omit(message, "flags"),
...converted_flags,
type: "server_message",
message: {
..._.omit(opts.raw_message, "flags"),
...converted_flags,
},
};
}

View File

@@ -585,7 +585,10 @@ export let show = (raw_terms: NarrowTerm[], show_opts: ShowMessageViewOpts): voi
// message locally available and then call
// message_view.show recursively, setting a flag to
// indicate we've already done this.
message_helper.process_new_message(data.message);
message_helper.process_new_message({
type: "server_message",
raw_message: data.message,
});
show(raw_terms, {
...opts,
fetched_target_message: true,

View File

@@ -616,6 +616,12 @@ export function pm_with_user_ids(message: Message | MessageWithBooleans): number
typeof message.display_recipient !== "string",
"Private messages should have list of recipients",
);
// Ideally display_recipient would be not optional in LocalMessage
// or MessageWithBooleans, ideally by refactoring the use of
// `build_display_recipient`, but that's complicated to type right now.
// When we have a new format for `display_recipient` in message objects in
// the API itself, we'll naturally clean this up.
assert(message.display_recipient !== undefined);
if (message.display_recipient.length === 0) {
blueslip.error("Empty recipient list in message");
@@ -1728,6 +1734,7 @@ function get_involved_people(message: MessageWithBooleans): DisplayRecipientUser
typeof message.display_recipient !== "string",
"Private messages should have list of recipients",
);
assert(message.display_recipient !== undefined);
involved_people = message.display_recipient;
}
@@ -1811,6 +1818,8 @@ export function maybe_incr_recipient_count(
return;
}
assert(message.display_recipient !== undefined);
// Track the number of direct messages we've sent to this person
// to improve autocomplete
for (const recip of message.display_recipient) {

View File

@@ -7,6 +7,7 @@ import render_message_reactions from "../templates/message_reactions.hbs";
import * as blueslip from "./blueslip.ts";
import * as channel from "./channel.ts";
import type {LocalMessage} from "./echo.ts";
import * as emoji from "./emoji.ts";
import type {EmojiRenderingDetails} from "./emoji.ts";
import {$t} from "./i18n.ts";
@@ -518,7 +519,9 @@ export function get_message_reactions(message: Message): MessageCleanReaction[]
return [...message.clean_reactions.values()];
}
export function generate_clean_reactions(message: RawMessage): Map<string, MessageCleanReaction> {
export function generate_clean_reactions(
message: RawMessage | LocalMessage,
): Map<string, MessageCleanReaction> {
/*
generate_clean_reactions processes the raw message.reactions object,
which will contain one object for each individual reaction, even

View File

@@ -122,8 +122,11 @@ function get_events_success(events) {
// But in any case, insert_new_messages handles multiple
// messages, only one of which was sent by this client,
// correctly.
message_events.insert_new_messages(messages, sent_by_this_client, false);
message_events.insert_new_messages({
type: "server_message",
raw_messages: messages,
sent_by_this_client,
});
}
} catch (error) {
blueslip.error("Failed to insert new messages", undefined, error);

View File

@@ -237,7 +237,7 @@ export function sorted_ids(ids: number[]): number[] {
return id_list;
}
export function set_match_data(target: Message, source: MatchedMessage): void {
export function set_match_data(target: Message, source: MatchedMessage | RawMessage): void {
target.match_subject = source.match_subject;
target.match_content = source.match_content;
}

View File

@@ -316,9 +316,10 @@ test_ui("send_message", ({override, override_rewire, mock_template}) => {
override_rewire(echo, "try_deliver_locally", (message_request) => {
const local_id_float = 123.04;
return echo.insert_local_message(message_request, local_id_float, (messages) => {
assert.equal(messages[0].timestamp, fake_now);
return messages;
return echo.insert_local_message(message_request, local_id_float, (messages_data) => {
assert.equal(messages_data.type, "local_message");
assert.equal(messages_data.raw_messages[0].timestamp, fake_now);
return messages_data.raw_messages;
});
});

View File

@@ -316,7 +316,8 @@ run_test("insert_local_message streams", ({override}) => {
get_topic_links_called = true;
});
const insert_new_messages = ([message]) => {
const insert_new_messages = (message_data) => {
const [message] = message_data.raw_messages;
assert.equal(message.display_recipient, "general");
assert.equal(message.timestamp, fake_now);
assert.equal(message.sender_email, "iago@zulip.com");
@@ -375,7 +376,8 @@ run_test("insert_local_message direct message", ({override}) => {
let render_called = false;
let insert_message_called = false;
const insert_new_messages = ([message]) => {
const insert_new_messages = (message_data) => {
const [message] = message_data.raw_messages;
assert.equal(message.display_recipient.length, 2);
insert_message_called = true;
return [message];
@@ -415,7 +417,8 @@ run_test("test reify_message_id", ({override}) => {
sender_id: 123,
draft_id: 100,
};
echo.insert_local_message(message_request, local_id_float, (messages) => {
echo.insert_local_message(message_request, local_id_float, (message_data) => {
const messages = message_data.raw_messages;
messages.map((message) => echo.track_local_message(message));
return messages;
});

View File

@@ -74,7 +74,10 @@ run_test("message_store", () => {
// Let's add a message into our message_store via
// message_helper.process_new_message.
assert.equal(message_store.get(in_message.id), undefined);
message_helper.process_new_message(in_message);
message_helper.process_new_message({
type: "server_message",
raw_message: in_message,
});
const message = message_store.get(in_message.id);
assert.equal(message.alerted, true);
@@ -97,7 +100,10 @@ run_test("unread", () => {
assert.equal(unread.num_unread_for_topic(stream_id, topic_name), 0);
let in_message = {...messages.isaac_to_denmark_stream};
in_message = message_helper.process_new_message(in_message);
in_message = message_helper.process_new_message({
type: "server_message",
raw_message: in_message,
});
unread.process_loaded_messages([in_message]);
assert.equal(unread.num_unread_for_topic(stream_id, topic_name), 1);

View File

@@ -111,7 +111,10 @@ run_test("insert_message", ({override}) => {
helper.redirect(unread_ui, "update_unread_counts");
helper.redirect(activity, "set_received_new_messages");
message_events.insert_new_messages([new_message]);
message_events.insert_new_messages({
type: "server_message",
raw_messages: [new_message],
});
// Even though we have stubbed a *lot* of code, our
// tests can still verify the main "narrative" of how

View File

@@ -81,9 +81,14 @@ run_test("update_messages", ({override, override_rewire}) => {
topic: "lunch",
type: "stream",
reactions: [],
submessages: [],
avatar_url: `/avatar/${alice.user_id}`,
};
const original_message = message_helper.process_new_message(raw_message);
const original_message = message_helper.process_new_message({
type: "server_message",
raw_message,
});
assert.equal(original_message.mentioned, true);
assert.equal(original_message.unread, true);
@@ -141,6 +146,7 @@ run_test("update_messages", ({override, override_rewire}) => {
assert.deepEqual(rendered_mgs, [
{
avatar_url: `/avatar/${alice.user_id}`,
display_reply_to: undefined,
alerted: false,
clean_reactions: new Map(),

View File

@@ -7,12 +7,12 @@ const {mock_esm, set_global, zrequire} = require("./lib/namespace.cjs");
const {run_test, noop} = require("./lib/test.cjs");
const blueslip = require("./lib/zblueslip.cjs");
mock_esm("../src/settings_data", {
user_can_access_all_other_users: () => true,
mock_esm("../src/electron_bridge", {
electron_bridge: {},
});
mock_esm("../src/stream_topic_history", {
add_message: noop,
mock_esm("../src/settings_data", {
user_can_access_all_other_users: () => true,
});
mock_esm("../src/recent_senders", {
@@ -111,8 +111,12 @@ test("process_new_message", () => {
is_me_message: false,
id: 2067,
reactions: [],
avatar_url: `/avatar/${me.user_id}`,
};
message = message_helper.process_new_message(message);
message = message_helper.process_new_message({
type: "local_message",
raw_message: message,
});
assert.deepEqual(message_user_ids.user_ids().sort(), [me.user_id, bob.user_id, cindy.user_id]);
@@ -132,8 +136,13 @@ test("process_new_message", () => {
match_subject: "topic foo",
match_content: "bar content",
reactions: [],
submessages: [],
avatar_url: "/some/path/to/avatar",
};
message = message_helper.process_new_message(message);
message = message_helper.process_new_message({
type: "server_message",
raw_message: message,
});
assert.equal(message.reply_to, "bob@example.com,cindy@example.com");
assert.equal(message.to_user_ids, "103,104");
@@ -150,9 +159,13 @@ test("process_new_message", () => {
subject: "the_subject",
id: 2068,
reactions: [],
avatar_url: `/avatar/${denise.user_id}`,
};
message = message_helper.process_new_message(message);
message = message_helper.process_new_message({
type: "local_message",
raw_message: message,
});
assert.equal(message.reply_to, "denise@example.com");
assert.deepEqual(message.flags, undefined);
assert.equal(message.alerted, false);
@@ -163,18 +176,6 @@ test("process_new_message", () => {
cindy.user_id,
denise.user_id,
]);
message = {
sender_email: denise.email,
sender_id: denise.user_id,
type: "stream",
display_recipient: "Zoolippy",
topic: "cool thing",
subject: "the_subject",
id: 2069,
};
blueslip.expect("error", "expected raw_message to have reactions", 1);
message_helper.process_new_message(message);
});
test("message_booleans_parity", () => {
@@ -184,7 +185,10 @@ test("message_booleans_parity", () => {
const assert_bool_match = (flags, expected_message) => {
let set_message = {topic: "convert_raw_message_to_message_with_booleans", flags};
const update_message = {topic: "update_booleans"};
set_message = message_store.convert_raw_message_to_message_with_booleans(set_message);
set_message = message_store.convert_raw_message_to_message_with_booleans({
type: "server_message",
raw_message: set_message,
}).message;
message_store.update_booleans(update_message, flags);
for (const key of Object.keys(expected_message)) {
assert.equal(
@@ -340,6 +344,7 @@ test("update_property", () => {
display_recipient: devel.name,
id: 100,
reactions: [],
avatar_url: `/avatar/${alice.user_id}`,
};
let message2 = {
type: "stream",
@@ -351,9 +356,16 @@ test("update_property", () => {
display_recipient: denmark.name,
id: 101,
reactions: [],
avatar_url: `/avatar/${bob.user_id}`,
};
message1 = message_helper.process_new_message(message1);
message2 = message_helper.process_new_message(message2);
message1 = message_helper.process_new_message({
type: "local_message",
raw_message: message1,
});
message2 = message_helper.process_new_message({
type: "local_message",
raw_message: message2,
});
assert.equal(message1.sender_full_name, alice.full_name);
assert.equal(message2.sender_full_name, bob.full_name);
@@ -389,6 +401,7 @@ test("remove", () => {
topic: "test",
id: 100,
reactions: [],
avatar_url: `/avatar/${alice.user_id}`,
};
const message2 = {
type: "stream",
@@ -400,6 +413,7 @@ test("remove", () => {
topic: "test",
id: 101,
reactions: [],
avatar_url: `/avatar/${bob.user_id}`,
};
const message3 = {
type: "stream",
@@ -411,9 +425,13 @@ test("remove", () => {
topic: "test",
id: 102,
reactions: [],
avatar_url: `/avatar/${cindy.user_id}`,
};
for (const message of [message1, message2]) {
message_helper.process_new_message(message);
message_helper.process_new_message({
type: "local_message",
raw_message: message,
});
}
const deleted_message_ids = [message1.id, message3.id, 104];
@@ -434,6 +452,7 @@ test("get_message_ids_in_stream", () => {
topic: "test",
id: 100,
reactions: [],
avatar_url: `/avatar/${alice.user_id}`,
};
const message2 = {
sender_email: "me@example.com",
@@ -444,6 +463,7 @@ test("get_message_ids_in_stream", () => {
is_me_message: false,
id: 101,
reactions: [],
avatar_url: `/avatar/${me.user_id}`,
};
const message3 = {
type: "stream",
@@ -455,6 +475,7 @@ test("get_message_ids_in_stream", () => {
topic: "test",
id: 102,
reactions: [],
avatar_url: `/avatar/${cindy.user_id}`,
};
const message4 = {
type: "stream",
@@ -466,10 +487,14 @@ test("get_message_ids_in_stream", () => {
topic: "test",
id: 103,
reactions: [],
avatar_url: `/avatar/${me.user_id}`,
};
for (const message of [message1, message2, message3, message4]) {
message_helper.process_new_message(message);
message_helper.process_new_message({
type: "local_message",
raw_message: message,
});
}
assert.deepEqual(message_store.get_message_ids_in_stream(devel.stream_id), [100, 103]);

View File

@@ -92,7 +92,8 @@ run_test("message_event", ({override}) => {
};
let inserted;
override(message_events, "insert_new_messages", (messages) => {
override(message_events, "insert_new_messages", (message_data) => {
const messages = message_data.raw_messages;
assert.equal(messages[0].content, event.message.content);
inserted = true;
return messages;