zulip/web/tests/presence.test.js

330 lines
8.4 KiB
JavaScript
Raw Normal View History

"use strict";
const assert = require("node:assert/strict");
const {mock_esm, zrequire} = require("./lib/namespace");
const {run_test} = require("./lib/test");
const {realm, user_settings} = require("./lib/zpage_params");
mock_esm("../src/settings_data", {
user_can_access_all_other_users: () => true,
});
const people = zrequire("people");
const presence = zrequire("presence");
presence: Tweak and document presence tuning values. We're changing the ping interval from 50s to 60s, because that's what the mobile apps have hardcoded currently, and backwards-compatibility is more important there than the web app's previously hardcoded 50s. For PRESENCE_PING_INTERVAL_SECS, the previous value hardcoded in both clients was 140s, selected as "plenty of network/other latency more than 2 x ACTIVE_PING_INTERVAL_MS". This is a pretty aggressive value; even a single request being missed or 500ing can result in a user appearing offline incorrectly. (There's a lag of up to one full ping interval between when the other client checks in and when you check in, and so we'll be at almost 2 ping intervals when you issue your next request that might get an updated connection time from that user). To increase failure tolerance, we want to change the offline threshhold from 2 x ACTIVE_PING_INTERVAL + 20s to 3 x ACTIVE_PING_INTERVAL + 20s, aka 140s => 200s, to be more robust to temporary failures causing us to display other users as offline. Since the mobile apps currently have 140s and 60s hardcoded, it should be safe to make this particular change; the mobile apps will just remain more aggressive than the web app in marking users offline until it uses the new API parameters. The end result in that Zulip will be slightly less aggressive at marking other users as offline if they go off the Internet. We will likely be able to tune ACTIVE_PING_INTERVAL downwards once #16381 and its follow-ups are completed, because it'll likely make these requests much cheaper.
2023-02-21 12:20:41 +01:00
const OFFLINE_THRESHOLD_SECS = 200;
2017-05-24 17:55:31 +02:00
const me = {
email: "me@zulip.com",
user_id: 101,
full_name: "Me Myself",
2017-05-24 17:55:31 +02:00
};
const alice = {
email: "alice@zulip.com",
2017-05-24 17:55:31 +02:00
user_id: 1,
full_name: "Alice Smith",
2017-05-24 17:55:31 +02:00
};
const fred = {
email: "fred@zulip.com",
2017-05-24 17:55:31 +02:00
user_id: 2,
full_name: "Fred Flintstone",
};
const sally = {
email: "sally@example.com",
user_id: 3,
full_name: "Sally Jones",
};
const zoe = {
email: "zoe@example.com",
2017-05-24 17:55:31 +02:00
user_id: 6,
full_name: "Zoe Yang",
2017-05-24 17:55:31 +02:00
};
const bot = {
email: "bot@zulip.com",
user_id: 7,
full_name: "The Bot",
is_bot: true,
};
const john = {
email: "john@zulip.com",
user_id: 8,
full_name: "John Doe",
};
const jane = {
email: "jane@zulip.com",
user_id: 9,
full_name: "Jane Doe",
};
people.add_active_user(me);
people.add_active_user(alice);
people.add_active_user(fred);
people.add_active_user(sally);
people.add_active_user(zoe);
people.add_active_user(bot);
people.add_active_user(john);
people.add_active_user(jane);
const inaccessible_user_id = 9999;
const inaccessible_user = people.add_inaccessible_user(inaccessible_user_id);
inaccessible_user.is_inaccessible_user = true;
2017-05-24 17:55:31 +02:00
people.initialize_current_user(me.user_id);
function test(label, f) {
run_test(label, (helpers) => {
realm.server_presence_offline_threshold_seconds = OFFLINE_THRESHOLD_SECS;
user_settings.presence_enabled = true;
presence.clear_internal_data();
f(helpers);
});
}
test("my user", () => {
assert.equal(presence.get_status(me.user_id), "active");
});
test("status_from_raw", () => {
const status_from_raw = presence.status_from_raw;
2017-05-24 17:55:31 +02:00
const now = 5000;
let raw;
2017-05-24 17:55:31 +02:00
raw = {
server_timestamp: now,
active_timestamp: now - OFFLINE_THRESHOLD_SECS / 2,
2017-05-24 17:55:31 +02:00
};
assert.deepEqual(status_from_raw(raw), {
status: "active",
last_active: raw.active_timestamp,
});
raw = {
server_timestamp: now,
active_timestamp: now - OFFLINE_THRESHOLD_SECS * 2,
};
assert.deepEqual(status_from_raw(raw), {
status: "offline",
last_active: raw.active_timestamp,
});
raw = {
server_timestamp: now,
idle_timestamp: now - OFFLINE_THRESHOLD_SECS / 2,
};
assert.deepEqual(status_from_raw(raw), {
status: "idle",
last_active: raw.idle_timestamp,
});
});
2017-05-24 17:55:31 +02:00
test("set_presence_info", () => {
const presences = {};
const now = 5000;
const recent = now + 1 - OFFLINE_THRESHOLD_SECS;
const a_while_ago = now - OFFLINE_THRESHOLD_SECS * 2;
2017-05-24 17:55:31 +02:00
const unknown_user_id = 999;
presences[alice.user_id.toString()] = {
active_timestamp: recent,
2017-05-24 17:55:31 +02:00
};
presences[fred.user_id.toString()] = {
active_timestamp: a_while_ago,
idle_timestamp: now,
2017-05-24 17:55:31 +02:00
};
presences[me.user_id.toString()] = {
active_timestamp: now,
};
presences[sally.user_id.toString()] = {
active_timestamp: a_while_ago,
};
presences[john.user_id.toString()] = {
idle_timestamp: a_while_ago,
};
presences[jane.user_id.toString()] = {
idle_timestamp: now,
};
// Unknown user ids can also be in the presence data.
presences[unknown_user_id.toString()] = {
idle_timestamp: now,
};
presences[inaccessible_user_id.toString()] = {
idle_timestamp: now,
};
const params = {};
params.presences = presences;
params.server_timestamp = now;
presence.initialize(params);
2017-05-24 17:55:31 +02:00
assert.deepEqual(presence.presence_info.get(alice.user_id), {
status: "active",
last_active: recent,
});
assert.equal(presence.get_status(alice.user_id), "active");
assert.deepEqual(presence.last_active_date(alice.user_id), new Date(recent * 1000));
assert.deepEqual(presence.presence_info.get(fred.user_id), {status: "idle", last_active: now});
assert.equal(presence.get_status(fred.user_id), "idle");
2017-05-24 17:55:31 +02:00
assert.deepEqual(presence.presence_info.get(me.user_id), {status: "active", last_active: now});
assert.equal(presence.get_status(me.user_id), "active");
assert.deepEqual(presence.presence_info.get(sally.user_id), {
status: "offline",
last_active: a_while_ago,
});
assert.equal(presence.get_status(sally.user_id), "offline");
assert.deepEqual(presence.presence_info.get(zoe.user_id), {
status: "offline",
last_active: undefined,
});
assert.equal(presence.get_status(zoe.user_id), "offline");
assert.equal(presence.last_active_date(zoe.user_id), undefined);
assert.ok(!presence.presence_info.has(bot.user_id));
assert.equal(presence.get_status(bot.user_id), "offline");
assert.deepEqual(presence.presence_info.get(john.user_id), {
status: "offline",
last_active: a_while_ago,
});
assert.equal(presence.get_status(john.user_id), "offline");
assert.deepEqual(presence.presence_info.get(jane.user_id), {status: "idle", last_active: now});
assert.equal(presence.get_status(jane.user_id), "idle");
assert.deepEqual(presence.presence_info.get(unknown_user_id), {
status: "idle",
last_active: now,
});
assert.equal(presence.get_status(unknown_user_id), "idle");
assert.equal(presence.presence_info.get(inaccessible_user_id), undefined);
});
test("missing values", () => {
/*
When a user does not have a relevant active timestamp,
the server just leaves off the `active_timestamp` field
to save bandwidth, which looks like `undefined` to us
if we try to dereference it.
*/
const now = 2000000;
const a_bit_ago = now - 5;
const presences = {};
presences[zoe.user_id.toString()] = {
idle_timestamp: a_bit_ago,
};
presence.set_info(presences, now);
assert.deepEqual(presence.presence_info.get(zoe.user_id), {
status: "idle",
last_active: a_bit_ago,
});
presences[zoe.user_id.toString()] = {};
presence.set_info(presences, now);
assert.deepEqual(presence.presence_info.get(zoe.user_id), {
status: "offline",
last_active: undefined,
});
});
test("big realms", ({override_rewire}) => {
const presences = {};
const now = 5000;
presences[sally.user_id.toString()] = {
active_timestamp: now,
};
// Make it seem like realm has a lot of people, in
// which case we will not provide default values for
// users that aren't in our presences payload.
override_rewire(people, "get_active_human_count", () => 1000);
presence.set_info(presences, now);
assert.ok(presence.presence_info.has(sally.user_id));
assert.ok(!presence.presence_info.has(zoe.user_id));
});
2017-05-24 17:55:31 +02:00
test("last_active_date", () => {
const unknown_id = 42;
presence.presence_info.clear();
presence.presence_info.set(alice.user_id, {last_active: 500});
presence.presence_info.set(fred.user_id, {});
assert.equal(presence.last_active_date(unknown_id), undefined);
assert.equal(presence.last_active_date(fred.user_id), undefined);
assert.deepEqual(presence.last_active_date(alice.user_id), new Date(500 * 1000));
});
test("update_info_from_event", () => {
let info;
info = {
website: {
status: "active",
timestamp: 500,
},
};
presence.presence_info.delete(alice.user_id);
presence.update_info_from_event(alice.user_id, info, 500);
assert.deepEqual(presence.presence_info.get(alice.user_id), {
status: "active",
last_active: 500,
});
info = {
mobile: {
status: "idle",
timestamp: 510,
},
};
presence.update_info_from_event(alice.user_id, info, 510);
assert.deepEqual(presence.presence_info.get(alice.user_id), {
status: "active",
last_active: 510,
});
info = {
mobile: {
status: "idle",
timestamp: 1000,
},
};
presence.update_info_from_event(alice.user_id, info, 1000);
assert.deepEqual(presence.presence_info.get(alice.user_id), {
status: "idle",
last_active: 1000,
});
});