From 971ea3eac20dae597e241adbbf9277de0a0f4c24 Mon Sep 17 00:00:00 2001 From: baterflyrity Date: Sat, 24 Feb 2024 19:31:15 +0300 Subject: [PATCH] Perform client time sync on login (#1004) * Perform client time sync on login Add current_time to the command welcome sent to the client on login. Closes #889 * Update test_login.py for client login sync Update tests to suit #60d684a8ad3441c89f947a7aad4efffe291f4de6: Add current_time to the command welcome sent to the client on login. * Fix E261 at least two spaces before inline comment Fix E261 at least two spaces before inline comment * Fix line comment I dont know wtf with that linter so just remove comments at all... * Migrate from datetime to server.timing Co-authored-by: Askaholic * Migrate to server.timing * Add fixed_time(...) context manager for mocking server.timing. * Migrate from real-time login tests to fixed_time(...) context mock. * Fix flake8 alerts. * FDowngrade datetime to python 3.10. * Fixed typo in test_server_login_token_valid. * Changed fixed_time from context manager to callable fixture. See docstring. * Remove redundant imports. * Fix typos. * Fix typos. * Revert "Fix typos." This reverts commit 320224779e4140c5d5bc282ee02a7409bc8d3b51. * Fix typos. --------- Co-authored-by: Askaholic --- server/lobbyconnection.py | 2 ++ tests/integration_tests/conftest.py | 32 +++++++++++++++++++++++++++ tests/integration_tests/test_login.py | 12 ++++++---- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/server/lobbyconnection.py b/server/lobbyconnection.py index bf028d8b8..ad87aeca7 100644 --- a/server/lobbyconnection.py +++ b/server/lobbyconnection.py @@ -18,6 +18,7 @@ import server.metrics as metrics from server.db import FAFDatabase +from server.timing import datetime_now from .config import TRACE, config from .db.models import ( @@ -693,6 +694,7 @@ async def on_player_login( await self.send({ "command": "welcome", "me": self.player.to_dict(), + "current_time": datetime_now().isoformat(), # For backwards compatibility for old clients. For now. "id": self.player.id, diff --git a/tests/integration_tests/conftest.py b/tests/integration_tests/conftest.py index 4008037bb..60f2c5b89 100644 --- a/tests/integration_tests/conftest.py +++ b/tests/integration_tests/conftest.py @@ -1,4 +1,5 @@ import asyncio +import datetime import hashlib import json import logging @@ -240,6 +241,37 @@ def lobby_contexts_proxy(lobby_setup_proxy): return contexts +@pytest.fixture +def fixed_time(monkeypatch): + """ + Fixture to fix server.timing value. By default, fixes all timings at 1970-01-01T00:00:00+00:00. Additionally, returned function can be called unbound times to change timing value, e.g.: + + def test_time(fixed_time): + assert server.lobbyconnection.datetime_now().timestamp == 0. + fixed_time(1) + assert server.lobbyconnection.datetime_now().timestamp == 1. + """ + + def fix_time(iso_utc_time: str | float | int | datetime.datetime = 0): + """ + Fix server.timing value. + + :param iso_utc_time: UTC time to use. Can be isoformat, timestamp or native object. + """ + if isinstance(iso_utc_time, str): + iso_utc_time = datetime.datetime.fromisoformat(iso_utc_time) + elif isinstance(iso_utc_time, (float, int)): + iso_utc_time = datetime.datetime.fromtimestamp(iso_utc_time, datetime.timezone.utc) + + def mock_datetime_now() -> datetime: + return iso_utc_time + + monkeypatch.setattr("server.lobbyconnection.datetime_now", mock_datetime_now) + + fix_time() + return fix_time + + # TODO: This fixture is poorly named since it returns a ServerContext, however, # it is used in almost every tests, so renaming it is a large task. @pytest.fixture(params=("qstream", "json")) diff --git a/tests/integration_tests/test_login.py b/tests/integration_tests/test_login.py index 179d71214..d51a9205d 100644 --- a/tests/integration_tests/test_login.py +++ b/tests/integration_tests/test_login.py @@ -92,7 +92,7 @@ async def test_server_ban_revoked_or_expired(lobby_server, user): assert msg["login"] == user -async def test_server_login_valid(lobby_server): +async def test_server_login_valid(lobby_server, fixed_time): proto = await connect_client(lobby_server) await perform_login(proto, ("Rhiza", "puff_the_magic_dragon")) msg = await proto.read_message() @@ -118,6 +118,7 @@ async def test_server_login_valid(lobby_server): assert msg == { "command": "welcome", "me": me, + "current_time": "1970-01-01T00:00:00+00:00", "id": 3, "login": "Rhiza" } @@ -137,7 +138,7 @@ async def test_server_login_valid(lobby_server): } -async def test_server_login_valid_admin(lobby_server): +async def test_server_login_valid_admin(lobby_server, fixed_time): proto = await connect_client(lobby_server) await perform_login(proto, ("test", "test_password")) msg = await proto.read_message() @@ -163,6 +164,7 @@ async def test_server_login_valid_admin(lobby_server): assert msg == { "command": "welcome", "me": me, + "current_time": "1970-01-01T00:00:00+00:00", "id": 1, "login": "test" } @@ -182,7 +184,7 @@ async def test_server_login_valid_admin(lobby_server): } -async def test_server_login_valid_moderator(lobby_server): +async def test_server_login_valid_moderator(lobby_server, fixed_time): proto = await connect_client(lobby_server) await perform_login(proto, ("moderator", "moderator")) msg = await proto.read_message() @@ -207,6 +209,7 @@ async def test_server_login_valid_moderator(lobby_server): assert msg == { "command": "welcome", "me": me, + "current_time": "1970-01-01T00:00:00+00:00", "id": 20, "login": "moderator" } @@ -261,7 +264,7 @@ async def test_server_login_double(lobby_server): } -async def test_server_login_token_valid(lobby_server, jwk_priv_key, jwk_kid): +async def test_server_login_token_valid(lobby_server, jwk_priv_key, jwk_kid, fixed_time): proto = await connect_client(lobby_server) await proto.send_message({ "command": "auth", @@ -305,6 +308,7 @@ async def test_server_login_token_valid(lobby_server, jwk_priv_key, jwk_kid): assert msg == { "command": "welcome", "me": me, + "current_time": "1970-01-01T00:00:00+00:00", "id": 3, "login": "Rhiza" }