Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add tests for restoring the presence state after a restart. #16151

Merged
merged 2 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion changelog.d/16150.misc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Clean-up calling `setup_background_tasks` in unit tests.
Improve presence tests.
1 change: 1 addition & 0 deletions changelog.d/16151.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Improve presence tests.
116 changes: 116 additions & 0 deletions tests/handlers/test_presence.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
from synapse.rest import admin
from synapse.rest.client import room
from synapse.server import HomeServer
from synapse.storage.database import LoggingDatabaseConnection
from synapse.types import JsonDict, UserID, get_domain_from_id
from synapse.util import Clock

Expand Down Expand Up @@ -513,6 +514,121 @@ def test_last_active(self) -> None:
self.assertEqual(state, new_state)


class PresenceHandlerInitTestCase(unittest.HomeserverTestCase):
def default_config(self) -> JsonDict:
config = super().default_config()
# Disable background tasks on this worker so that the PresenceHandler isn't
# loaded until we request it.
config["run_background_tasks_on"] = "other"
return config

def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.user_id = f"@test:{self.hs.config.server.server_name}"

# Move the reactor to the initial time.
self.reactor.advance(1000)
now = self.clock.time_msec()

main_store = hs.get_datastores().main
self.get_success(
main_store.update_presence(
[
UserPresenceState(
user_id=self.user_id,
state=PresenceState.ONLINE,
last_active_ts=now,
last_federation_update_ts=now,
last_user_sync_ts=now,
status_msg=None,
currently_active=True,
)
]
)
)

# Regenerate the preloaded presence information on PresenceStore.
def refill_presence(db_conn: LoggingDatabaseConnection) -> None:
main_store._presence_on_startup = main_store._get_active_presence(db_conn)

self.get_success(main_store.db_pool.runWithConnection(refill_presence))

def test_restored_presence_idles(self) -> None:
"""The presence state restored from the database should not persist forever."""

# Get the handler (which kicks off a bunch of timers).
presence_handler = self.hs.get_presence_handler()

# Assert the user is online.
state = self.get_success(
presence_handler.get_state(UserID.from_string(self.user_id))
)
self.assertEqual(state.state, PresenceState.ONLINE)

# Advance such that the user should timeout.
self.reactor.advance(SYNC_ONLINE_TIMEOUT / 1000)
self.reactor.pump([5])

# Check that the user is now offline.
state = self.get_success(
presence_handler.get_state(UserID.from_string(self.user_id))
)
self.assertEqual(state.state, PresenceState.OFFLINE)

@parameterized.expand(
[
(PresenceState.BUSY, PresenceState.BUSY),
(PresenceState.ONLINE, PresenceState.ONLINE),
(PresenceState.UNAVAILABLE, PresenceState.UNAVAILABLE),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is actually wrong for the same things that #16066 fixes -- we restore to an "online" state, so an "unavailable" state shouldn't override that.

# Offline syncs don't update the state.
(PresenceState.OFFLINE, PresenceState.ONLINE),
]
)
@unittest.override_config({"experimental_features": {"msc3026_enabled": True}})
def test_restored_presence_online_after_sync(
self, sync_state: str, expected_state: str
) -> None:
"""
The presence state restored from the database should be overridden with sync after a timeout.

Args:
sync_state: The presence state of the new sync.
expected_state: The expected presence right after the sync.
"""

# Get the handler (which kicks off a bunch of timers).
presence_handler = self.hs.get_presence_handler()

# Assert the user is online, as restored.
state = self.get_success(
presence_handler.get_state(UserID.from_string(self.user_id))
)
self.assertEqual(state.state, PresenceState.ONLINE)

# Advance slightly and sync.
self.reactor.advance(SYNC_ONLINE_TIMEOUT / 1000 / 2)
self.get_success(
presence_handler.user_syncing(
self.user_id, sync_state != PresenceState.OFFLINE, sync_state
)
)

# Assert the user is in the expected state.
state = self.get_success(
presence_handler.get_state(UserID.from_string(self.user_id))
)
self.assertEqual(state.state, expected_state)

# Advance such that the user's preloaded data times out, but not the new sync.
self.reactor.advance(SYNC_ONLINE_TIMEOUT / 1000 / 2)
self.reactor.pump([5])

# Check that the user is in the sync state (as the client is currently syncing still).
state = self.get_success(
presence_handler.get_state(UserID.from_string(self.user_id))
)
self.assertEqual(state.state, sync_state)


class PresenceHandlerTestCase(BaseMultiWorkerStreamTestCase):
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.presence_handler = hs.get_presence_handler()
Expand Down
Loading