Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
1462 lines
52 KiB
JavaScript
1462 lines
52 KiB
JavaScript
"use strict";
|
|
|
|
const assert = require("node:assert/strict");
|
|
|
|
const {make_realm} = require("./lib/example_realm.cjs");
|
|
const {make_stream} = require("./lib/example_stream.cjs");
|
|
const {make_user} = require("./lib/example_user.cjs");
|
|
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/electron_bridge", {
|
|
electron_bridge: {},
|
|
});
|
|
|
|
mock_esm("../src/settings_data", {
|
|
user_can_access_all_other_users: () => true,
|
|
});
|
|
|
|
mock_esm("../src/recent_senders", {
|
|
process_stream_message: noop,
|
|
process_private_message: noop,
|
|
});
|
|
|
|
set_global("document", "document-stub");
|
|
|
|
const util = zrequire("util");
|
|
const people = zrequire("people");
|
|
const pm_conversations = zrequire("pm_conversations");
|
|
const message_helper = zrequire("message_helper");
|
|
const message_store = zrequire("message_store");
|
|
const message_user_ids = zrequire("message_user_ids");
|
|
const muted_users = zrequire("muted_users");
|
|
const {set_realm} = zrequire("state_data");
|
|
const stream_data = zrequire("stream_data");
|
|
const {initialize_user_settings} = zrequire("user_settings");
|
|
|
|
set_realm(make_realm());
|
|
initialize_user_settings({user_settings: {}});
|
|
|
|
const denmark = make_stream({
|
|
subscribed: false,
|
|
name: "Denmark",
|
|
stream_id: 20,
|
|
});
|
|
|
|
const devel = make_stream({
|
|
subscribed: true,
|
|
name: "Devel",
|
|
stream_id: 21,
|
|
});
|
|
stream_data.add_sub_for_tests(denmark);
|
|
stream_data.add_sub_for_tests(devel);
|
|
|
|
const me = make_user({
|
|
email: "me@example.com",
|
|
user_id: 101,
|
|
full_name: "Me Myself",
|
|
});
|
|
|
|
const alice = make_user({
|
|
email: "alice@example.com",
|
|
user_id: 102,
|
|
full_name: "Alice",
|
|
});
|
|
|
|
const bob = make_user({
|
|
email: "bob@example.com",
|
|
user_id: 103,
|
|
full_name: "Bob",
|
|
});
|
|
|
|
const cindy = make_user({
|
|
email: "cindy@example.com",
|
|
user_id: 104,
|
|
full_name: "Cindy",
|
|
});
|
|
|
|
const denise = make_user({
|
|
email: "denise@example.com",
|
|
user_id: 105,
|
|
full_name: "Denise ",
|
|
});
|
|
|
|
people.add_active_user(me);
|
|
people.add_active_user(alice);
|
|
people.add_active_user(bob);
|
|
people.add_active_user(cindy);
|
|
people.add_active_user(denise);
|
|
|
|
people.initialize_current_user(me.user_id);
|
|
|
|
function convert_recipients(people) {
|
|
// Display_recipient uses `id` for user_ids.
|
|
return people.map((p) => ({
|
|
email: p.email,
|
|
id: p.user_id,
|
|
full_name: p.full_name,
|
|
}));
|
|
}
|
|
|
|
function test(label, f) {
|
|
run_test(label, (helpers) => {
|
|
message_store.clear_for_testing();
|
|
message_user_ids.clear_for_testing();
|
|
f(helpers);
|
|
});
|
|
}
|
|
|
|
test("process_new_message", () => {
|
|
let message = {
|
|
sender_email: "me@example.com",
|
|
sender_id: me.user_id,
|
|
type: "private",
|
|
display_recipient: convert_recipients([me, bob, cindy]),
|
|
flags: ["has_alert_word"],
|
|
is_me_message: false,
|
|
id: 2067,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${me.user_id}`,
|
|
submessages: [],
|
|
};
|
|
message = message_helper.process_new_message({
|
|
type: "server_message",
|
|
raw_message: message,
|
|
}).message;
|
|
|
|
assert.deepEqual(message_user_ids.user_ids().toSorted(), [
|
|
me.user_id,
|
|
bob.user_id,
|
|
cindy.user_id,
|
|
]);
|
|
|
|
assert.equal(message.is_private, true);
|
|
assert.equal(message.reply_to, "bob@example.com,cindy@example.com");
|
|
assert.equal(message.to_user_ids, "103,104");
|
|
assert.equal(message.display_reply_to, "Bob, Cindy");
|
|
assert.equal(message.alerted, true);
|
|
assert.equal(message.is_me_message, false);
|
|
|
|
const retrieved_message = message_store.get(2067);
|
|
assert.equal(retrieved_message, message);
|
|
|
|
// access cached previous message, and test match subject/content
|
|
message = {
|
|
id: 2067,
|
|
match_subject: "topic foo",
|
|
match_content: "bar content",
|
|
reactions: [],
|
|
submessages: [],
|
|
avatar_url: "/some/path/to/avatar",
|
|
};
|
|
message = message_helper.process_new_message({
|
|
type: "server_message",
|
|
raw_message: message,
|
|
}).message;
|
|
|
|
assert.equal(message.reply_to, "bob@example.com,cindy@example.com");
|
|
assert.equal(message.to_user_ids, "103,104");
|
|
assert.equal(message.display_reply_to, "Bob, Cindy");
|
|
assert.equal(util.get_match_topic(message), "topic foo");
|
|
assert.equal(message.match_content, "bar content");
|
|
|
|
message = {
|
|
sender_email: denise.email,
|
|
sender_id: denise.user_id,
|
|
type: "stream",
|
|
display_recipient: "Zoolippy",
|
|
topic: "cool thing",
|
|
subject: "the_subject",
|
|
id: 2068,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${denise.user_id}`,
|
|
submessages: [],
|
|
};
|
|
|
|
message = message_helper.process_new_message({
|
|
type: "server_message",
|
|
raw_message: message,
|
|
}).message;
|
|
assert.equal(message.reply_to, "denise@example.com");
|
|
assert.deepEqual(message.flags, undefined);
|
|
assert.equal(message.alerted, false);
|
|
|
|
assert.deepEqual(message_user_ids.user_ids().toSorted(), [
|
|
me.user_id,
|
|
bob.user_id,
|
|
cindy.user_id,
|
|
denise.user_id,
|
|
]);
|
|
});
|
|
|
|
test("message_booleans_parity", () => {
|
|
// We have two code paths that update/set message booleans.
|
|
// This test asserts that both have identical behavior for the
|
|
// flags common between them.
|
|
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({
|
|
type: "server_message",
|
|
raw_message: set_message,
|
|
}).message;
|
|
message_store.update_booleans(update_message, flags);
|
|
for (const [key, expected_value] of Object.entries(expected_message)) {
|
|
assert.equal(set_message[key], expected_value, `'${key}' != ${expected_value}`);
|
|
assert.equal(update_message[key], expected_value);
|
|
}
|
|
assert.equal(set_message.topic, "convert_raw_message_to_message_with_booleans");
|
|
assert.equal(update_message.topic, "update_booleans");
|
|
};
|
|
|
|
assert_bool_match(["stream_wildcard_mentioned"], {
|
|
mentioned: true,
|
|
mentioned_me_directly: false,
|
|
stream_wildcard_mentioned: true,
|
|
topic_wildcard_mentioned: false,
|
|
alerted: false,
|
|
});
|
|
|
|
assert_bool_match(["topic_wildcard_mentioned"], {
|
|
mentioned: true,
|
|
mentioned_me_directly: false,
|
|
stream_wildcard_mentioned: false,
|
|
topic_wildcard_mentioned: true,
|
|
alerted: false,
|
|
});
|
|
|
|
assert_bool_match(["mentioned"], {
|
|
mentioned: true,
|
|
mentioned_me_directly: true,
|
|
stream_wildcard_mentioned: false,
|
|
topic_wildcard_mentioned: false,
|
|
alerted: false,
|
|
});
|
|
|
|
assert_bool_match(["has_alert_word"], {
|
|
mentioned: false,
|
|
mentioned_me_directly: false,
|
|
stream_wildcard_mentioned: false,
|
|
topic_wildcard_mentioned: false,
|
|
alerted: true,
|
|
});
|
|
});
|
|
|
|
test("errors", ({disallow_rewire}) => {
|
|
// Test a user that doesn't exist
|
|
let message = {
|
|
type: "private",
|
|
display_recipient: [{id: 92714}],
|
|
};
|
|
|
|
blueslip.expect("error", "Unknown user_id in maybe_get_user_by_id", 1);
|
|
blueslip.expect("error", "Unknown user id", 1); // From person.js
|
|
|
|
// Expect each to throw two blueslip errors
|
|
// One from message_store.ts, one from person.js
|
|
const emails = message_store.get_pm_emails(message);
|
|
assert.equal(emails, "?");
|
|
|
|
assert.throws(
|
|
() => {
|
|
message_store.get_pm_full_names(people.pm_with_user_ids(message));
|
|
},
|
|
{
|
|
name: "Error",
|
|
message: "Unknown user_id in get_by_user_id: 92714",
|
|
},
|
|
);
|
|
|
|
message = {
|
|
type: "stream",
|
|
display_recipient: [{}],
|
|
};
|
|
|
|
// This should early return and not run pm_conversations.set_partner
|
|
disallow_rewire(pm_conversations, "set_partner");
|
|
pm_conversations.process_message(message, false);
|
|
});
|
|
|
|
test("reify_message_id", () => {
|
|
const message = {type: "private", id: 500, topic: "", queue_id: 5, draft_id: 6};
|
|
|
|
message_store.update_message_cache({
|
|
type: "local_message",
|
|
message,
|
|
});
|
|
assert.equal(message_store.get_cached_message(500).message, message);
|
|
|
|
message_store.reify_message_id({old_id: 500, new_id: 501});
|
|
assert.equal(message_store.get_cached_message(500), undefined);
|
|
assert.equal(message_store.get_cached_message(501).message, message);
|
|
assert.deepEqual(message_store.get_cached_message(501).message, {
|
|
type: "private",
|
|
id: 501,
|
|
locally_echoed: false,
|
|
});
|
|
});
|
|
|
|
test("update_booleans", () => {
|
|
// First, test fields that we do actually want to update.
|
|
const message = {
|
|
mentioned: false,
|
|
mentioned_me_directly: false,
|
|
stream_wildcard_mentioned: false,
|
|
topic_wildcard_mentioned: false,
|
|
alerted: false,
|
|
};
|
|
|
|
let flags = ["mentioned", "has_alert_word", "read"];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.mentioned, true);
|
|
assert.equal(message.mentioned_me_directly, true);
|
|
assert.equal(message.stream_wildcard_mentioned, false);
|
|
assert.equal(message.topic_wildcard_mentioned, false);
|
|
assert.equal(message.alerted, true);
|
|
|
|
flags = ["stream_wildcard_mentioned", "unread"];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.mentioned, true);
|
|
assert.equal(message.mentioned_me_directly, false);
|
|
assert.equal(message.stream_wildcard_mentioned, true);
|
|
assert.equal(message.topic_wildcard_mentioned, false);
|
|
|
|
flags = ["topic_wildcard_mentioned", "unread"];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.mentioned, true);
|
|
assert.equal(message.mentioned_me_directly, false);
|
|
assert.equal(message.stream_wildcard_mentioned, false);
|
|
assert.equal(message.topic_wildcard_mentioned, true);
|
|
|
|
flags = ["read"];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.mentioned, false);
|
|
assert.equal(message.mentioned_me_directly, false);
|
|
assert.equal(message.alerted, false);
|
|
assert.equal(message.stream_wildcard_mentioned, false);
|
|
assert.equal(message.topic_wildcard_mentioned, false);
|
|
|
|
// Make sure we don't muck with unread.
|
|
message.unread = false;
|
|
flags = [""];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.unread, false);
|
|
|
|
message.unread = true;
|
|
flags = ["read"];
|
|
message_store.update_booleans(message, flags);
|
|
assert.equal(message.unread, true);
|
|
});
|
|
|
|
test("update_property", () => {
|
|
let message1 = {
|
|
type: "stream",
|
|
sender_full_name: alice.full_name,
|
|
sender_id: alice.user_id,
|
|
small_avatar_url: "alice_url",
|
|
stream_id: devel.stream_id,
|
|
topic: "",
|
|
display_recipient: devel.name,
|
|
id: 100,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${alice.user_id}`,
|
|
draft_id: 1,
|
|
};
|
|
let message2 = {
|
|
type: "stream",
|
|
sender_full_name: bob.full_name,
|
|
sender_id: bob.user_id,
|
|
small_avatar_url: "bob_url",
|
|
stream_id: denmark.stream_id,
|
|
topic: "",
|
|
display_recipient: denmark.name,
|
|
id: 101,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${bob.user_id}`,
|
|
draft_id: 2,
|
|
};
|
|
message1 = message_helper.process_new_message({
|
|
type: "local_message",
|
|
raw_message: message1,
|
|
}).message;
|
|
message2 = message_helper.process_new_message({
|
|
type: "local_message",
|
|
raw_message: message2,
|
|
}).message;
|
|
|
|
assert.equal(message1.sender_full_name, alice.full_name);
|
|
assert.equal(message2.sender_full_name, bob.full_name);
|
|
message_store.update_sender_full_name(bob.user_id, "Bobby");
|
|
assert.equal(message1.sender_full_name, alice.full_name);
|
|
assert.equal(message2.sender_full_name, "Bobby");
|
|
|
|
assert.equal(message1.small_avatar_url, "alice_url");
|
|
assert.equal(message2.small_avatar_url, "bob_url");
|
|
message_store.update_small_avatar_url(bob.user_id, "bobby_url");
|
|
assert.equal(message1.small_avatar_url, "alice_url");
|
|
assert.equal(message2.small_avatar_url, "bobby_url");
|
|
|
|
assert.equal(message1.stream_id, devel.stream_id);
|
|
assert.equal(message1.display_recipient, devel.name);
|
|
assert.equal(message2.stream_id, denmark.stream_id);
|
|
assert.equal(message2.display_recipient, denmark.name);
|
|
message_store.update_stream_name(devel.stream_id, "Prod");
|
|
assert.equal(message1.stream_id, devel.stream_id);
|
|
assert.equal(message1.display_recipient, "Prod");
|
|
assert.equal(message2.stream_id, denmark.stream_id);
|
|
assert.equal(message2.display_recipient, denmark.name);
|
|
});
|
|
|
|
test("remove", () => {
|
|
const message1 = {
|
|
type: "stream",
|
|
sender_full_name: alice.full_name,
|
|
sender_id: alice.user_id,
|
|
stream_id: devel.stream_id,
|
|
stream: devel.name,
|
|
display_recipient: devel.name,
|
|
topic: "test",
|
|
id: 100,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${alice.user_id}`,
|
|
draft_id: 1,
|
|
};
|
|
const message2 = {
|
|
type: "stream",
|
|
sender_full_name: bob.full_name,
|
|
sender_id: bob.user_id,
|
|
stream_id: denmark.stream_id,
|
|
stream: denmark.name,
|
|
display_recipient: denmark.name,
|
|
topic: "test",
|
|
id: 101,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${bob.user_id}`,
|
|
draft_id: 2,
|
|
};
|
|
const message3 = {
|
|
type: "stream",
|
|
sender_full_name: cindy.full_name,
|
|
sender_id: cindy.user_id,
|
|
stream_id: denmark.stream_id,
|
|
stream: denmark.name,
|
|
display_recipient: denmark.name,
|
|
topic: "test",
|
|
id: 102,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${cindy.user_id}`,
|
|
draft_id: 3,
|
|
};
|
|
for (const message of [message1, message2]) {
|
|
message_helper.process_new_message({
|
|
type: "local_message",
|
|
raw_message: message,
|
|
});
|
|
}
|
|
|
|
const deleted_message_ids = [message1.id, message3.id, 104];
|
|
message_store.remove(deleted_message_ids);
|
|
assert.equal(message_store.get(message1.id), undefined);
|
|
assert.equal(message_store.get(message2.id).id, message2.id);
|
|
assert.equal(message_store.get(message3.id), undefined);
|
|
});
|
|
|
|
test("get_message_ids_in_stream", () => {
|
|
const message1 = {
|
|
type: "stream",
|
|
sender_full_name: alice.full_name,
|
|
sender_id: alice.user_id,
|
|
stream_id: devel.stream_id,
|
|
stream: devel.name,
|
|
display_recipient: devel.name,
|
|
topic: "test",
|
|
id: 100,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${alice.user_id}`,
|
|
draft_id: 1,
|
|
};
|
|
const message2 = {
|
|
sender_email: "me@example.com",
|
|
sender_id: me.user_id,
|
|
type: "private",
|
|
display_recipient: convert_recipients([me, bob, cindy]),
|
|
flags: ["has_alert_word"],
|
|
is_me_message: false,
|
|
id: 101,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${me.user_id}`,
|
|
draft_id: 2,
|
|
};
|
|
const message3 = {
|
|
type: "stream",
|
|
sender_full_name: cindy.full_name,
|
|
sender_id: cindy.user_id,
|
|
stream_id: denmark.stream_id,
|
|
stream: denmark.name,
|
|
display_recipient: denmark.name,
|
|
topic: "test",
|
|
id: 102,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${cindy.user_id}`,
|
|
draft_id: 3,
|
|
};
|
|
const message4 = {
|
|
type: "stream",
|
|
sender_full_name: me.full_name,
|
|
sender_id: me.user_id,
|
|
stream_id: devel.stream_id,
|
|
stream: devel.name,
|
|
display_recipient: devel.name,
|
|
topic: "test",
|
|
id: 103,
|
|
reactions: [],
|
|
avatar_url: `/avatar/${me.user_id}`,
|
|
draft_id: 4,
|
|
};
|
|
|
|
for (const message of [message1, message2, message3, message4]) {
|
|
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]);
|
|
assert.deepEqual(message_store.get_message_ids_in_stream(denmark.stream_id), [102]);
|
|
});
|
|
|
|
test("maybe_update_raw_content", () => {
|
|
const message1 = {
|
|
id: 1,
|
|
raw_content: undefined,
|
|
type: "stream",
|
|
stream: devel.name,
|
|
stream_id: devel.stream_id,
|
|
};
|
|
|
|
const message2 = {
|
|
id: 2,
|
|
raw_content: undefined,
|
|
type: "stream",
|
|
stream: denmark.name,
|
|
stream_id: denmark.stream_id,
|
|
};
|
|
|
|
const message3 = {
|
|
id: 3,
|
|
raw_content: "should be reset",
|
|
type: "stream",
|
|
stream: denmark.name,
|
|
stream_id: denmark.stream_id,
|
|
};
|
|
for (const message of [message1, message2, message3]) {
|
|
message_store.update_message_cache({message});
|
|
}
|
|
message_store.maybe_update_raw_content(message1.id, "hello world");
|
|
message_store.maybe_update_raw_content(message2.id, "hello world");
|
|
message_store.maybe_update_raw_content(message3.id, "hello world");
|
|
// It is safe to update raw_content of messages
|
|
// we will be receiving events for.
|
|
assert.equal(message1.raw_content, "hello world");
|
|
// It is not safe to update raw_content of messages
|
|
// we won't be receiving events for.
|
|
assert.equal(message2.raw_content, undefined);
|
|
// We should reset accidentally cached raw_content for messages
|
|
// we don't receive update events for.
|
|
assert.equal(message3.raw_content, undefined);
|
|
|
|
// Deleting a message from the message store, should
|
|
// no longer update the raw_content of the message
|
|
// object.
|
|
message_store.remove([1]);
|
|
message_store.maybe_update_raw_content(message1.id, "bye world");
|
|
assert.equal(message1.raw_content, "hello world");
|
|
});
|
|
|
|
test("save_topic_links", () => {
|
|
function assert_maps_empty() {
|
|
assert.deepEqual(message_store.topic_links_by_to_for_testing(), new Map());
|
|
assert.deepEqual(message_store.topic_links_by_from_for_testing(), new Map());
|
|
}
|
|
|
|
function message_with_content(content) {
|
|
return {
|
|
id: 5,
|
|
type: "stream",
|
|
stream_id: 9,
|
|
sender_id: bob.user_id,
|
|
topic: "worldly issues",
|
|
content,
|
|
};
|
|
}
|
|
|
|
// If the link isn't a narrow link, it doesn't save anything.
|
|
let link = "bad-link";
|
|
message_store.save_topic_links(message_with_content(`<div><a href='${link}'>a link!</a>`));
|
|
assert_maps_empty();
|
|
|
|
// Malformed link doesn't throw an error
|
|
link = "/#narrow/which-channel/10-design/bad-topic/hello/";
|
|
message_store.save_topic_links(message_with_content(`<div><a href='${link}'>a link!</a>`));
|
|
assert_maps_empty();
|
|
|
|
// A message for a stream we don't know exists (including private streams)
|
|
// doesn't save anything.
|
|
link = "/#narrow/channel/10-design/topic/hello/";
|
|
message_store.save_topic_links(message_with_content(`<div><a href='${link}'>a link!</a>`));
|
|
assert_maps_empty();
|
|
|
|
// If the stream does exist and we know about it, we save the data.
|
|
stream_data.add_sub_for_tests({name: "design", subscribed: true, stream_id: 10});
|
|
let message = message_with_content(`<div><a href='${link}'>a link!</a>`);
|
|
message_store.save_topic_links(message);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([[10, new Map([["hello", new Map([[0, [message.id]]])]])]]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
message.stream_id,
|
|
new Map([
|
|
[
|
|
message.topic,
|
|
new Map([
|
|
[
|
|
message.id,
|
|
[
|
|
{
|
|
stream_id: 10,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
message_store.clear_topic_links_for_testing();
|
|
|
|
// A link to a message we don't know about doesn't save
|
|
link = "/#narrow/channel/10-design/topic/hello/near/22";
|
|
message = message_with_content(`<div><a href='${link}'>a link!</a>`);
|
|
message_store.save_topic_links(message);
|
|
assert_maps_empty();
|
|
|
|
// A link to a message we do know about does save
|
|
const linked_message = {
|
|
id: 22,
|
|
sender_id: alice.user_id,
|
|
type: "stream",
|
|
stream_id: 10,
|
|
topic: "hello",
|
|
content: "<ignore>",
|
|
};
|
|
message_store.update_message_cache({
|
|
type: "server_message",
|
|
message: linked_message,
|
|
});
|
|
message_store.save_topic_links(message);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([[10, new Map([["hello", new Map([[linked_message.id, [message.id]]])]])]]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
message.stream_id,
|
|
new Map([
|
|
[
|
|
message.topic,
|
|
new Map([
|
|
[
|
|
message.id,
|
|
[
|
|
{
|
|
stream_id: 10,
|
|
topic: "hello",
|
|
message_id: linked_message.id,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
message_store.clear_topic_links_for_testing();
|
|
|
|
// A link to a message from a muted user doesn't save
|
|
muted_users.set_muted_users([{id: linked_message.sender_id}]);
|
|
message_store.save_topic_links(message);
|
|
assert_maps_empty();
|
|
|
|
// A link from a message from a muted user doesn't save
|
|
muted_users.set_muted_users([{id: message.sender_id}]);
|
|
message_store.save_topic_links(message);
|
|
assert_maps_empty();
|
|
|
|
muted_users.set_muted_users([]);
|
|
});
|
|
|
|
test("get and update topic links to/from narrow", () => {
|
|
function save_message_link(from_stream_id, from_topic, from_message_id, to_link) {
|
|
if (from_message_id) {
|
|
const from_message = message_store.get(from_message_id);
|
|
assert.ok(from_message !== undefined);
|
|
assert.ok(from_message.stream_id === from_stream_id);
|
|
assert.ok(from_message.topic === from_topic);
|
|
}
|
|
message_store.save_topic_links({
|
|
id: from_message_id,
|
|
type: "stream",
|
|
stream_id: from_stream_id,
|
|
sender_id: bob.user_id,
|
|
topic: from_topic,
|
|
content: `<div><a href='${to_link}'>a link!</a>`,
|
|
});
|
|
}
|
|
|
|
function add_message(message_id, stream_id, topic) {
|
|
const message = {
|
|
id: message_id,
|
|
sender_id: alice.user_id,
|
|
type: "stream",
|
|
stream_id,
|
|
topic,
|
|
content: "<ignore>",
|
|
};
|
|
message_store.update_message_cache({
|
|
type: "server_message",
|
|
message,
|
|
});
|
|
return message;
|
|
}
|
|
|
|
const design = {name: "design", subscribed: true, stream_id: 10};
|
|
stream_data.add_sub_for_tests(design);
|
|
const development = {name: "development", subscribed: true, stream_id: 11};
|
|
stream_data.add_sub_for_tests(development);
|
|
const sales = {name: "sales", subscribed: true, stream_id: 12};
|
|
stream_data.add_sub_for_tests(sales);
|
|
|
|
// set_message_links([]);
|
|
const message1 = add_message(201, development.stream_id, "engineering");
|
|
const message2 = add_message(5, design.stream_id, "goodbye");
|
|
const message3 = add_message(100, development.stream_id, "engineering");
|
|
const message4 = add_message(99, development.stream_id, "engineering");
|
|
const message5 = add_message(3, sales.stream_id, "new client");
|
|
const message6 = add_message(20, sales.stream_id, "new client");
|
|
const message7 = add_message(400, development.stream_id, "testing");
|
|
const NO_MESSAGE_ID = 0;
|
|
|
|
// Save a link from a message in #development>engineering
|
|
save_message_link(
|
|
development.stream_id,
|
|
"engineering",
|
|
message1.id,
|
|
"/#narrow/channel/10-design/topic/goodbye/",
|
|
);
|
|
// Test that we ignore when the exact same data added again
|
|
// (e.g. two links to the same narrow in the same message)
|
|
save_message_link(
|
|
development.stream_id,
|
|
"engineering",
|
|
message1.id,
|
|
"/#narrow/channel/10-design/topic/goodbye/",
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([
|
|
[design.stream_id, new Map([["goodbye", new Map([[NO_MESSAGE_ID, [message1.id]]])]])],
|
|
]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
development.stream_id,
|
|
new Map([
|
|
[
|
|
"engineering",
|
|
new Map([
|
|
[
|
|
message1.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
// Save a second link *to* the same narrow, but *from* a different narrow
|
|
save_message_link(
|
|
sales.stream_id,
|
|
"new client",
|
|
message5.id,
|
|
"/#narrow/channel/10-design/topic/goodbye/",
|
|
);
|
|
// Save a second link *from* the same message, but *to* a different narrow
|
|
save_message_link(
|
|
development.stream_id,
|
|
"engineering",
|
|
message1.id,
|
|
"/#narrow/channel/10-design/topic/second-goodbye/",
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([
|
|
[
|
|
design.stream_id,
|
|
new Map([
|
|
[
|
|
"goodbye",
|
|
new Map([
|
|
// from message5 is new
|
|
[NO_MESSAGE_ID, [message1.id, message5.id]],
|
|
]),
|
|
],
|
|
// to "second-goodbye" is new
|
|
["second-goodbye", new Map([[NO_MESSAGE_ID, [message1.id]]])],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
development.stream_id,
|
|
new Map([
|
|
[
|
|
"engineering",
|
|
new Map([
|
|
[
|
|
message1.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
// to "second-goodbye" is new
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "second-goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
// from sales is new
|
|
[
|
|
sales.stream_id,
|
|
new Map([
|
|
[
|
|
"new client",
|
|
new Map([
|
|
[
|
|
message5.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
// Save a link from an earlier message in #development (smaller id). Even though
|
|
// we saved the later message first, this should be sorted to show up first in
|
|
// the list of outgoing links.
|
|
assert.ok(message3.id < message1.id);
|
|
save_message_link(
|
|
development.stream_id,
|
|
"engineering",
|
|
message3.id,
|
|
"/#narrow/channel/10-design/topic/hello/",
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([
|
|
[
|
|
design.stream_id,
|
|
new Map([
|
|
// Link to "hello" is new and earlier
|
|
["hello", new Map([[NO_MESSAGE_ID, [message3.id]]])],
|
|
["goodbye", new Map([[NO_MESSAGE_ID, [message1.id, message5.id]]])],
|
|
["second-goodbye", new Map([[NO_MESSAGE_ID, [message1.id]]])],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
development.stream_id,
|
|
new Map([
|
|
[
|
|
"engineering",
|
|
new Map([
|
|
// message3 is new and earlier
|
|
[
|
|
message3.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
[
|
|
message1.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "second-goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
[
|
|
sales.stream_id,
|
|
new Map([
|
|
[
|
|
"new client",
|
|
new Map([
|
|
[
|
|
message5.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
// A link from the same narrow to the same narrow, but in a different message.
|
|
// In links to this narrow we only keep the message with the smaller/earlier id
|
|
// (which will be this one). In links from this narrow, we show the link only
|
|
// once even though it's linked from multiple messages. But the high level topic
|
|
// maps will have data from both messages.
|
|
assert.deepEqual(message_store.topic_links_to_narrow(design.stream_id, "hello"), [
|
|
{stream_id: development.stream_id, topic: "engineering", message_id: message3.id},
|
|
]);
|
|
// There are currently three links from #development>engineering
|
|
assert.equal(
|
|
message_store.topic_links_from_narrow(development.stream_id, "engineering").length,
|
|
3,
|
|
);
|
|
// Now save an earlier message with the same link
|
|
assert.ok(message4.id < message3.id);
|
|
save_message_link(
|
|
development.stream_id,
|
|
"engineering",
|
|
message4.id,
|
|
"/#narrow/channel/10-design/topic/hello/",
|
|
);
|
|
// Now only message4 will show up, since it occurs first.
|
|
assert.deepEqual(message_store.topic_links_to_narrow(design.stream_id, "hello"), [
|
|
{stream_id: development.stream_id, topic: "engineering", message_id: message4.id},
|
|
]);
|
|
// * There are still only three links from #development>engineering, since message4's
|
|
// "hello" link was redundant.
|
|
// * We sort the data by message id, so message 99 to "hello" is listed before message
|
|
// 201 to goodbye and second-goodbye, even though we added the "hello" link after.
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "engineering"), [
|
|
{stream_id: design.stream_id, topic: "hello", message_id: undefined},
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
{stream_id: design.stream_id, topic: "second-goodbye", message_id: undefined},
|
|
]);
|
|
|
|
// The high level topic maps will have data from both messages.
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([
|
|
[
|
|
design.stream_id,
|
|
new Map([
|
|
[
|
|
"hello",
|
|
new Map([
|
|
// message4 is new
|
|
[NO_MESSAGE_ID, [message3.id, message4.id]],
|
|
]),
|
|
],
|
|
["goodbye", new Map([[NO_MESSAGE_ID, [message1.id, message5.id]]])],
|
|
["second-goodbye", new Map([[NO_MESSAGE_ID, [message1.id]]])],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
development.stream_id,
|
|
new Map([
|
|
[
|
|
"engineering",
|
|
new Map([
|
|
[
|
|
message3.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
[
|
|
message1.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "second-goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
// message4 is new
|
|
[
|
|
message4.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
[
|
|
sales.stream_id,
|
|
new Map([
|
|
[
|
|
"new client",
|
|
new Map([
|
|
[
|
|
message5.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
// Save a link to a specific message.
|
|
save_message_link(
|
|
development.stream_id,
|
|
"testing",
|
|
message7.id,
|
|
"/#narrow/channel/10-design/topic/goodbye/near/5",
|
|
);
|
|
// A link to a whole channel (no topic) isn't tracked at all, so it
|
|
// won't be saved in either link map (asserted below).
|
|
save_message_link(sales.stream_id, "new client", message5.id, "/#narrow/channel/10-design");
|
|
// Links from a topic to itself are ignored when we fetch to/from links for a narrow,
|
|
// but we still save them in case those messages are moved later (and, in a new narrow,
|
|
// could stop being a link from a narrrow to itself).
|
|
save_message_link(
|
|
sales.stream_id,
|
|
"new client",
|
|
message5.id,
|
|
"/#narrow/channel/12-sales/topic/new.20client/",
|
|
);
|
|
save_message_link(
|
|
sales.stream_id,
|
|
"new client",
|
|
message6.id,
|
|
`/#narrow/channel/12-sales/topic/new.20client/near/${message5.id}`,
|
|
);
|
|
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_to_for_testing(),
|
|
new Map([
|
|
[
|
|
design.stream_id,
|
|
new Map([
|
|
["hello", new Map([[NO_MESSAGE_ID, [message3.id, message4.id]]])],
|
|
[
|
|
"goodbye",
|
|
new Map([
|
|
[NO_MESSAGE_ID, [message1.id, message5.id]],
|
|
// This link to a specific message is new
|
|
[5, [message7.id]],
|
|
]),
|
|
],
|
|
["second-goodbye", new Map([[NO_MESSAGE_ID, [message1.id]]])],
|
|
]),
|
|
],
|
|
// The links to sales are new
|
|
[
|
|
sales.stream_id,
|
|
new Map([
|
|
[
|
|
"new client",
|
|
new Map([
|
|
[NO_MESSAGE_ID, [message5.id]],
|
|
[message5.id, [message6.id]],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_by_from_for_testing(),
|
|
new Map([
|
|
[
|
|
development.stream_id,
|
|
new Map([
|
|
[
|
|
"engineering",
|
|
new Map([
|
|
[
|
|
message3.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
[
|
|
message1.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "second-goodbye",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
[
|
|
message4.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "hello",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
// link from "testing" to a specific message is new
|
|
[
|
|
"testing",
|
|
new Map([
|
|
[
|
|
message7.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: 5,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
[
|
|
sales.stream_id,
|
|
new Map([
|
|
[
|
|
"new client",
|
|
new Map([
|
|
[
|
|
message5.id,
|
|
[
|
|
{
|
|
stream_id: design.stream_id,
|
|
topic: "goodbye",
|
|
message_id: undefined,
|
|
},
|
|
// The link to the #design channel (no topic) is not tracked.
|
|
// Link to #sales>new-client is new
|
|
{
|
|
stream_id: sales.stream_id,
|
|
topic: "new client",
|
|
message_id: undefined,
|
|
},
|
|
],
|
|
],
|
|
// Link from message6 is new
|
|
[
|
|
message6.id,
|
|
[
|
|
{
|
|
stream_id: sales.stream_id,
|
|
topic: "new client",
|
|
message_id: message5.id,
|
|
},
|
|
],
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
],
|
|
]),
|
|
);
|
|
|
|
// ------ Now let's test `topic_links_from_narrow` and `topic_links_to_narrow`
|
|
|
|
// Test empty cases.
|
|
assert.deepEqual(
|
|
message_store.topic_links_from_narrow(development.stream_id, "nothing here"),
|
|
[],
|
|
);
|
|
assert.deepEqual(
|
|
message_store.topic_links_to_narrow(development.stream_id, "nothing here"),
|
|
[],
|
|
);
|
|
|
|
// Links are sorted by message id even though the earlier message
|
|
// was added after the later message.
|
|
assert.deepEqual(message_store.topic_links_to_narrow(design.stream_id, "goodbye"), [
|
|
{stream_id: sales.stream_id, topic: "new client", message_id: 3},
|
|
{stream_id: development.stream_id, topic: "engineering", message_id: 201},
|
|
{stream_id: development.stream_id, topic: "testing", message_id: 400},
|
|
]);
|
|
|
|
// Links from #sales>new-client to #sales>new-client don't show up in either list
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "new client"), [
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
]);
|
|
assert.deepEqual(message_store.topic_links_to_narrow(sales.stream_id, "new client"), []);
|
|
|
|
// ------ Now let's test editing a message's topic
|
|
message_store.process_topic_edit({
|
|
message_ids: [message1.id],
|
|
// previously #development
|
|
new_stream_id: sales.stream_id,
|
|
// previously "engineering"
|
|
new_topic: "super engineering",
|
|
});
|
|
// Mirroring how message_events.ts updates the message object after.
|
|
message1.stream_id = sales.stream_id;
|
|
message1.topic = "super engineering";
|
|
assert.deepEqual(message_store.topic_links_to_narrow(design.stream_id, "goodbye"), [
|
|
{stream_id: sales.stream_id, topic: "new client", message_id: 3},
|
|
// This existing link is updated to the new stream and topic for message 201
|
|
{stream_id: sales.stream_id, topic: "super engineering", message_id: 201},
|
|
{stream_id: development.stream_id, topic: "testing", message_id: 400},
|
|
]);
|
|
|
|
// This message has the link to the message with id 400 in #development>testing
|
|
// and we'll move it to a new stream/topic.
|
|
message_store.process_topic_edit({
|
|
message_ids: [message2.id],
|
|
// previously #design
|
|
new_stream_id: sales.stream_id,
|
|
// previously "goodbye"
|
|
new_topic: "signing off",
|
|
});
|
|
// Mirroring how message_events.ts updates the message object after.
|
|
message2.stream_id = sales.stream_id;
|
|
message2.topic = "signing off";
|
|
assert.deepEqual(
|
|
message_store.topic_links_to_narrow(design.stream_id, "goodbye"),
|
|
// Links to the original narrow aren't affected
|
|
[
|
|
{stream_id: sales.stream_id, topic: "new client", message_id: 3},
|
|
{stream_id: sales.stream_id, topic: "super engineering", message_id: 201},
|
|
],
|
|
);
|
|
assert.deepEqual(message_store.topic_links_to_narrow(sales.stream_id, "signing off"), [
|
|
// This link from the moved message now links from the new topic
|
|
{stream_id: development.stream_id, topic: "testing", message_id: 400},
|
|
]);
|
|
|
|
// Moving message5, which has both these links in it (which you can see above in the map)
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "new client"), [
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
]);
|
|
message_store.process_topic_edit({
|
|
message_ids: [message5.id],
|
|
// previously #sales
|
|
new_stream_id: design.stream_id,
|
|
// previously "new client"
|
|
new_topic: "client logo",
|
|
});
|
|
// Mirroring how message_events.ts updates the message object after.
|
|
message5.stream_id = design.stream_id;
|
|
message5.topic = "client logo";
|
|
// message5's own links moved with it, but message6's link to message5
|
|
// (which was hidden as a link to its own topic) now shows up, following
|
|
// message5 to its new conversation.
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "new client"), [
|
|
{stream_id: design.stream_id, topic: "client logo", message_id: message5.id},
|
|
]);
|
|
// Now they're both in the new stream/topic links, but we get an extra link too!
|
|
assert.deepEqual(message_store.topic_links_from_narrow(design.stream_id, "client logo"), [
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
// This was an existing link to the old topic from message5. It still links to the
|
|
// old topic (the topic still exists, separately from the message that was moved)
|
|
// and previously wasn't showing up because it was a link from a topic to itself.
|
|
// Now it does show up here because it's a link to a topic different from itself,
|
|
// now that the message is moved.
|
|
{stream_id: sales.stream_id, topic: "new client", message_id: undefined},
|
|
]);
|
|
|
|
// ------ Now let's test editing a message's id
|
|
// We already have a link to this message, but let's also make a link from
|
|
// this message (to a topic) to test that both update correctly.
|
|
save_message_link(
|
|
sales.stream_id,
|
|
"signing off",
|
|
message2.id,
|
|
"/#narrow/channel/10-design/topic/goodbye/",
|
|
);
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "testing"), [
|
|
{stream_id: sales.stream_id, topic: "signing off", message_id: 5},
|
|
]);
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "signing off"), [
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
]);
|
|
message_store.reify_message_id({old_id: message2.id, new_id: 55});
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "testing"), [
|
|
{stream_id: sales.stream_id, topic: "signing off", message_id: 55},
|
|
]);
|
|
// This looks the same but is now stored under a message by a different id,
|
|
// which will let us delete it shortly.
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "signing off"), [
|
|
{stream_id: design.stream_id, topic: "goodbye", message_id: undefined},
|
|
]);
|
|
|
|
// ------ Now let's test removing a message
|
|
message_store.remove([55]);
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "testing"), []);
|
|
assert.deepEqual(message_store.topic_links_from_narrow(sales.stream_id, "signing off"), []);
|
|
|
|
// ------ Now let's test that links to a message in the same conversation
|
|
// stay hidden when the whole conversation moves (e.g. the topic is resolved).
|
|
const message8 = add_message(500, development.stream_id, "budget");
|
|
const message9 = add_message(501, development.stream_id, "budget");
|
|
// A link to a message in the same topic (e.g. from quoting it) isn't shown.
|
|
save_message_link(
|
|
development.stream_id,
|
|
"budget",
|
|
message9.id,
|
|
`/#narrow/channel/11-development/topic/budget/near/${message8.id}`,
|
|
);
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "budget"), []);
|
|
// Resolve the topic, i.e. move all of its messages to "✔ budget".
|
|
message_store.process_topic_edit({
|
|
message_ids: [message8.id, message9.id],
|
|
new_stream_id: development.stream_id,
|
|
new_topic: "✔ budget",
|
|
});
|
|
// Mirroring how message_events.ts updates the message objects after.
|
|
message8.topic = "✔ budget";
|
|
message9.topic = "✔ budget";
|
|
// The linked message is still in the same conversation, so the link
|
|
// stays hidden even though it was saved under the unresolved topic name.
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "✔ budget"), []);
|
|
assert.deepEqual(message_store.topic_links_to_narrow(development.stream_id, "✔ budget"), []);
|
|
// But once the linked message moves to another conversation, the link
|
|
// is shown, pointing to the message's new location.
|
|
message_store.process_topic_edit({
|
|
message_ids: [message8.id],
|
|
new_stream_id: design.stream_id,
|
|
new_topic: "spending",
|
|
});
|
|
// Mirroring how message_events.ts updates the message object after.
|
|
message8.stream_id = design.stream_id;
|
|
message8.topic = "spending";
|
|
assert.deepEqual(message_store.topic_links_from_narrow(development.stream_id, "✔ budget"), [
|
|
{stream_id: design.stream_id, topic: "spending", message_id: message8.id},
|
|
]);
|
|
assert.deepEqual(message_store.topic_links_to_narrow(design.stream_id, "spending"), [
|
|
{stream_id: development.stream_id, topic: "✔ budget", message_id: message9.id},
|
|
]);
|
|
});
|
|
|
|
test("process_topic_edit ignores messages missing from the local cache", () => {
|
|
message_store.clear_topic_links_for_testing();
|
|
|
|
// A channel or topic move can include messages the current client
|
|
// hasn't fetched. The link maps only track stored messages, so such a
|
|
// move is a no-op for them and must not throw.
|
|
const missing_message_id = 987654;
|
|
assert.equal(message_store.get(missing_message_id), undefined);
|
|
|
|
message_store.process_topic_edit({
|
|
message_ids: [missing_message_id],
|
|
new_stream_id: 10,
|
|
new_topic: "moved",
|
|
});
|
|
|
|
assert.deepEqual(message_store.topic_links_by_from_for_testing(), new Map());
|
|
assert.deepEqual(message_store.topic_links_by_to_for_testing(), new Map());
|
|
});
|