people: Defer loading user data during app load.

This should greatly reduce the symptoms of a bug where a stale
realm_users cache (missing a newly created user) results in the web
client missing the new user and throwing exceptions.
This commit is contained in:
Aman Agrawal
2025-05-16 20:39:52 +05:30
committed by Tim Abbott
parent f94b1d444e
commit a9b51f80ba
6 changed files with 299 additions and 21 deletions

View File

@@ -7,15 +7,15 @@ const {JSDOM} = require("jsdom");
const {zrequire, mock_esm} = require("./lib/namespace.cjs");
const {run_test} = require("./lib/test.cjs");
const clipboard_handler = zrequire("clipboard_handler");
const stream_data = zrequire("stream_data");
const people = zrequire("people");
const settings_config = zrequire("settings_config");
mock_esm("../src/user_settings", {
user_settings: {
web_channel_default_view: settings_config.web_channel_default_view_values.channel_feed.code,
},
});
const clipboard_handler = zrequire("clipboard_handler");
const stream_data = zrequire("stream_data");
const people = zrequire("people");
const hamlet = {
user_id: 15,

View File

@@ -216,9 +216,17 @@ run_test("build_display_recipient", ({override}) => {
user_id: 21,
},
];
const user_group_params = {
realm_user_groups: [
{
is_system_group: true,
members: [123, 21],
},
],
};
params.realm_non_active_users = [];
params.cross_realm_bots = [];
people.initialize(current_user.user_id, params);
people.initialize(current_user.user_id, params, user_group_params);
let message = {
type: "stream",
@@ -232,6 +240,7 @@ run_test("build_display_recipient", ({override}) => {
message = {
type: "private",
to_user_ids: "21",
private_message_recipient: "cordelia@zulip.com",
sender_email: "iago@zulip.com",
sender_full_name: "Iago",
@@ -252,6 +261,7 @@ run_test("build_display_recipient", ({override}) => {
message = {
type: "private",
to_user_ids: "123",
private_message_recipient: "iago@zulip.com",
sender_email: "iago@zulip.com",
sender_full_name: "Iago",
@@ -342,10 +352,24 @@ run_test("insert_local_message direct message", ({override}) => {
full_name: "Iago",
email: "iago@zulip.com",
},
{
email: "cordelia@zulip.com",
full_name: "Cordelia",
user_id: 21,
},
];
const user_group_params = {
realm_user_groups: [
{
is_system_group: true,
members: [123, 21],
},
],
};
params.realm_non_active_users = [];
params.cross_realm_bots = [];
people.initialize(current_user.user_id, params);
people.init();
people.initialize(current_user.user_id, params, user_group_params);
let render_called = false;
let insert_message_called = false;
@@ -362,6 +386,7 @@ run_test("insert_local_message direct message", ({override}) => {
const message_request = {
private_message_recipient: "cordelia@zulip.com",
to_user_ids: "21",
type: "private",
sender_email: "iago@zulip.com",
sender_full_name: "Iago",

View File

@@ -16,6 +16,7 @@ const message_user_ids = mock_esm("../src/message_user_ids");
const settings_data = mock_esm("../src/settings_data", {
user_can_access_all_other_users: () => true,
});
const channel = mock_esm("../src/channel");
const muted_users = zrequire("muted_users");
const people = zrequire("people");
@@ -1342,21 +1343,33 @@ test_people("initialize", () => {
};
params.realm_non_active_users = [retiree];
const current_user = {
email: "my_email@example.com",
user_id: 42,
full_name: "Me Myself",
};
const alice = {
email: "alice@example.com",
user_id: 16,
full_name: "Alice",
};
params.realm_users = [alice];
params.realm_users = [alice, current_user];
const test_bot = {
email: "bot@example.com",
user_id: 17,
full_name: "Test Bot",
};
params.cross_realm_bots = [test_bot];
const user_group_params = {
realm_user_groups: [
{
is_system_group: true,
members: [42, 17, 16, 15],
},
],
};
const my_user_id = 42;
people.initialize(my_user_id, params);
people.initialize(current_user.user_id, params, user_group_params);
assert.equal(people.is_active_user_for_popover(17), true);
assert.ok(people.is_cross_realm_email("bot@example.com"));
@@ -1627,3 +1640,117 @@ test_people("sort_by_username", () => {
run_test("reset MockDate", () => {
MockDate.reset();
});
test_people("fetch_users", async ({override}) => {
people.init();
// Valid users missing from params data sent by server.
const users_in_response = [
{
email: "retiree@example.com",
user_id: 15,
full_name: "Retiree",
delivery_email: "",
date_joined: "",
is_active: true,
is_owner: false,
is_admin: false,
is_guest: false,
role: 1,
avatar_url: "",
avatar_version: 1,
is_bot: false,
},
{
email: "alice@example.com",
user_id: 16,
full_name: "Alice",
delivery_email: "",
date_joined: "",
is_active: false,
is_owner: false,
is_admin: false,
is_guest: false,
role: 1,
avatar_url: "",
avatar_version: 1,
is_bot: false,
},
];
const params = {};
params.realm_users = [
{
email: "my_email@example.com",
user_id: 42,
full_name: "Me Myself",
},
];
params.realm_non_active_users = [];
params.cross_realm_bots = [
{
email: "bot@example.com",
user_id: 17,
full_name: "Test Bot",
},
];
const user_group_params = {
realm_user_groups: [
{
is_system_group: true,
members: [42, 17, 16, 15],
},
],
};
const my_user_id = 42;
override(channel, "get", ({url, data, success, _error}) => {
assert.equal(url, "/json/users");
assert.ok(data.user_ids.includes("15"));
assert.ok(data.user_ids.includes("16"));
assert.ok(!data.user_ids.includes("42"));
assert.ok(!data.user_ids.includes("17"));
success({
members: users_in_response,
result: "success",
msg: "",
});
});
await people.initialize(my_user_id, params, user_group_params);
const retiree = people.get_by_user_id(15);
const alice = people.get_by_user_id(16);
assert.equal(retiree.full_name, "Retiree");
assert.equal(alice.full_name, "Alice");
assert.ok(people.is_valid_user_id(15));
assert.ok(people.is_valid_user_id(16));
assert.ok(people.is_valid_user_id(42));
assert.ok(people.is_valid_user_id(17));
assert.equal(people.get_by_email("alice@example.com").user_id, 16);
assert.equal(people.get_by_email("retiree@example.com").user_id, 15);
override(channel, "get", ({url, _data, _success, error}) => {
assert.equal(url, "/json/users");
// Return error response.
error({responseJSON: {msg: "test error"}});
});
// fetch_users should reject with an Error object
blueslip.expect("error", "test error");
await assert.rejects(
async () => {
await people.fetch_users(new Set([15, 16]));
},
(err) => {
// Check that the error is an instance of Error and has the correct message
assert.ok(err instanceof Error);
assert.equal(err.message, "test error");
return true;
},
);
// Just for coverage, not actually checked by blueslip.
blueslip.expect("error", "Ignored invalid user_ids: 1, 2");
await people.fetch_users(new Set([1, 2]));
});