fix: stop panel description churn
This commit is contained in:
@@ -74,7 +74,7 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertTrue(result)
|
||||
panel_service.update_user_details_on_panel.assert_awaited_once()
|
||||
_, payload = panel_service.update_user_details_on_panel.await_args.args[:2]
|
||||
self.assertEqual(payload["description"], "alice")
|
||||
self.assertNotIn("description", payload)
|
||||
self.assertEqual(payload["email"], "linked@example.com")
|
||||
|
||||
def test_panel_description_for_user_excludes_email(self):
|
||||
@@ -87,6 +87,16 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
self.assertEqual(_panel_description_for_user(user), "alice\nAlice")
|
||||
|
||||
def test_panel_description_for_user_filters_broken_lines(self):
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
username="alice??",
|
||||
first_name="????",
|
||||
last_name="Smith",
|
||||
)
|
||||
|
||||
self.assertEqual(_panel_description_for_user(user), "alice??\nSmith")
|
||||
|
||||
async def test_merged_panel_identity_deletes_source_before_updating_target(self):
|
||||
calls = []
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from bot.handlers.admin.sync_admin import (
|
||||
_format_panel_update_changes,
|
||||
_identity_panel_update_reasons,
|
||||
_panel_description_for_user,
|
||||
_panel_identity_fields_update_payload,
|
||||
_panel_identity_matches_user,
|
||||
_panel_identity_needs_full_fetch,
|
||||
_panel_identity_needs_legacy_description_cleanup,
|
||||
@@ -58,6 +59,17 @@ def test_panel_description_for_user_excludes_email():
|
||||
assert _panel_description_for_user(user) == "alice\nAlice\nSmith"
|
||||
|
||||
|
||||
def test_panel_description_for_user_filters_broken_lines():
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
username="alice??",
|
||||
first_name="????",
|
||||
last_name="Smith",
|
||||
)
|
||||
|
||||
assert _panel_description_for_user(user) == "alice??\nSmith"
|
||||
|
||||
|
||||
def test_panel_update_change_summary_is_compact_and_field_based():
|
||||
changes = _panel_update_changes(
|
||||
{
|
||||
@@ -133,7 +145,7 @@ def test_panel_identity_match_accepts_list_description_without_email():
|
||||
)
|
||||
|
||||
|
||||
def test_panel_identity_payload_with_expiry_keeps_email_out_of_description():
|
||||
def test_panel_identity_payload_with_expiry_excludes_description_updates():
|
||||
expire_at = datetime(2026, 6, 1, 12, 0, tzinfo=timezone.utc)
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
@@ -145,11 +157,26 @@ def test_panel_identity_payload_with_expiry_keeps_email_out_of_description():
|
||||
|
||||
payload = _panel_identity_payload_with_expiry(user, expire_at=expire_at)
|
||||
|
||||
assert payload["description"] == "alice\nAlice"
|
||||
assert "description" not in payload
|
||||
assert payload["email"] == "linked@example.com"
|
||||
assert payload["telegramId"] == 42
|
||||
|
||||
|
||||
def test_panel_identity_fields_update_payload_excludes_description():
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
telegram_id=42,
|
||||
username="alice",
|
||||
first_name="Alice",
|
||||
last_name=None,
|
||||
)
|
||||
|
||||
assert _panel_identity_fields_update_payload(user) == {
|
||||
"email": "linked@example.com",
|
||||
"telegramId": 42,
|
||||
}
|
||||
|
||||
|
||||
def test_panel_identity_detects_legacy_full_description_cleanup_need():
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
|
||||
@@ -47,6 +47,20 @@ class PanelApiServiceLoggingTests(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
self.assertTrue(service._request.await_args.kwargs["log_full_response"])
|
||||
|
||||
async def test_create_panel_user_omits_empty_description(self):
|
||||
service = self._make_service()
|
||||
service._request = AsyncMock(return_value={"response": {"uuid": "user-uuid"}})
|
||||
|
||||
await service.create_panel_user(
|
||||
username_on_panel="tg_42",
|
||||
telegram_id=42,
|
||||
description="",
|
||||
)
|
||||
|
||||
payload = service._request.await_args.kwargs["json"]
|
||||
self.assertNotIn("description", payload)
|
||||
self.assertEqual(payload["telegramId"], 42)
|
||||
|
||||
async def test_get_user_by_uuid_uses_short_ttl_cache_and_update_invalidates(self):
|
||||
service = self._make_service()
|
||||
service._request = AsyncMock(return_value={"response": {"uuid": "user-uuid"}})
|
||||
|
||||
@@ -3,7 +3,7 @@ from types import SimpleNamespace
|
||||
from bot.services.subscription_service_impl.panel_identity import PanelIdentityMixin
|
||||
|
||||
|
||||
def test_subscription_panel_description_excludes_email():
|
||||
def test_subscription_panel_identity_payload_excludes_description_updates():
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
username="alice",
|
||||
@@ -15,6 +15,19 @@ def test_subscription_panel_description_excludes_email():
|
||||
|
||||
payload = PanelIdentityMixin()._panel_identity_payload_for_user(user)
|
||||
|
||||
assert payload["description"] == "alice\nAlice\nSmith"
|
||||
assert "description" not in payload
|
||||
assert payload["email"] == "linked@example.com"
|
||||
assert payload["telegramId"] == 42
|
||||
|
||||
|
||||
def test_subscription_panel_description_filters_broken_lines_for_creation():
|
||||
user = SimpleNamespace(
|
||||
email="linked@example.com",
|
||||
username="alice??",
|
||||
first_name="????",
|
||||
last_name="Smith",
|
||||
telegram_id=42,
|
||||
user_id=42,
|
||||
)
|
||||
|
||||
assert PanelIdentityMixin()._panel_description_for_user(user) == "alice??\nSmith"
|
||||
|
||||
@@ -69,7 +69,7 @@ class ProfileSyncMiddlewareCacheTests(unittest.IsolatedAsyncioTestCase):
|
||||
get_user.assert_awaited_once()
|
||||
self.assertEqual(handler.await_count, 2)
|
||||
|
||||
async def test_profile_sync_keeps_email_out_of_panel_description(self):
|
||||
async def test_profile_sync_does_not_rewrite_panel_description(self):
|
||||
middleware = ProfileSyncMiddleware()
|
||||
handler = AsyncMock(return_value="ok")
|
||||
event = SimpleNamespace()
|
||||
@@ -124,8 +124,9 @@ class ProfileSyncMiddlewareCacheTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertEqual(result, "ok")
|
||||
panel_service.update_user_details_on_panel.assert_awaited_once()
|
||||
_, payload = panel_service.update_user_details_on_panel.await_args.args[:2]
|
||||
self.assertEqual(payload["description"], "alice\nAlice\nSmith")
|
||||
self.assertNotIn("description", payload)
|
||||
self.assertEqual(payload["email"], "linked@example.com")
|
||||
self.assertEqual(payload["telegramId"], 42)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -3,7 +3,13 @@ from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "backend"))
|
||||
|
||||
from bot.utils.text_sanitizer import sanitize_display_name, sanitize_username, username_for_display
|
||||
from bot.utils.text_sanitizer import (
|
||||
looks_like_broken_panel_text,
|
||||
panel_description_from_profile,
|
||||
sanitize_display_name,
|
||||
sanitize_username,
|
||||
username_for_display,
|
||||
)
|
||||
|
||||
|
||||
def test_sanitize_username_preserves_underscore_suffixes():
|
||||
@@ -22,3 +28,21 @@ def test_sanitize_username_rejects_free_form_values_instead_of_truncating():
|
||||
|
||||
def test_display_name_filters_still_apply_to_free_form_names():
|
||||
assert sanitize_display_name("Name service") == "Name"
|
||||
|
||||
|
||||
def test_panel_broken_text_detection_is_conservative_about_question_marks():
|
||||
assert not looks_like_broken_panel_text("?")
|
||||
assert not looks_like_broken_panel_text("alice??")
|
||||
assert not looks_like_broken_panel_text("??? 123")
|
||||
assert not looks_like_broken_panel_text("\U0001f0cf")
|
||||
|
||||
|
||||
def test_panel_broken_text_detection_filters_replacement_garbage():
|
||||
assert looks_like_broken_panel_text("????")
|
||||
assert looks_like_broken_panel_text("???!")
|
||||
assert looks_like_broken_panel_text("\ufffd\ufffd")
|
||||
|
||||
|
||||
def test_panel_description_filters_only_broken_lines():
|
||||
assert panel_description_from_profile("alice??", "????", "Smith") == "alice??\nSmith"
|
||||
assert panel_description_from_profile(None, "????", "\ufffd\ufffd") == ""
|
||||
|
||||
Reference in New Issue
Block a user