From 0116a4726d31f1c2f978cfe17da198bcef522cef Mon Sep 17 00:00:00 2001
From: Daniel Bimschas <daniel@bimschas.com>
Date: Mon, 27 Jan 2025 07:34:11 +0100
Subject: [PATCH] HOLI-10900: ignore/delete users without name#

---
 main.py      |  8 +++++++-
 test_main.py | 47 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 1 deletion(-)

diff --git a/main.py b/main.py
index fde4eb5..f0ca479 100644
--- a/main.py
+++ b/main.py
@@ -24,7 +24,13 @@ SEMVER_1XX_SERIES = r"^1\.\d+\.\d+$"
 
 def process_user_updated_event(client: TypesenseClient, event: UserUpdatedEvent) -> None:
     logger.debug(f"Processing {event}")
-    client.upsert(event.as_typesense_document())
+    if event.user.name is None or event.user.name.strip() == "":
+        try:
+            client.delete(f"profile_{event.user.id}")
+        except ObjectNotFound:
+            pass
+    else:
+        client.upsert(event.as_typesense_document())
     logger.info(f"Upserted user {event.user.id} in Typesense")
 
 
diff --git a/test_main.py b/test_main.py
index 5df84bc..0b9f951 100644
--- a/test_main.py
+++ b/test_main.py
@@ -1,9 +1,11 @@
 import base64
 import json
 from unittest.mock import patch
+from pytest import fail
 
 # noinspection PyPackageRequirements
 from cloudevents.http.event import CloudEvent
+from typesense.exceptions import ObjectNotFound, RequestUnauthorized
 
 from main import process_event
 
@@ -74,6 +76,51 @@ def test_user_updated(mock_client):
         }
     )
 
+
+@patch("typesense.client.Client")
+def test_ignores_deletes_user_without_name(mock_client):
+    # users without names must not show up in search results
+    user = user_payload.copy()
+    # noinspection PyTypeChecker
+    user['name'] = None
+    process_event(
+        mock_client, CloudEvent(attributes, message_data("UserUpdated", "1.1.0", {"user": user }))
+    )
+    mock_client.upsert.assert_not_called()
+    mock_client.delete.assert_called_with(f'profile_{user["id"]}')
+
+    user['name'] = ''
+    process_event(
+        mock_client, CloudEvent(attributes, message_data("UserUpdated", "1.1.0", {"user": user }))
+    )
+    mock_client.upsert.assert_not_called()
+    mock_client.delete.assert_called_with(f'profile_{user["id"]}')
+
+@patch("typesense.client.Client")
+def test_ignores_404_on_deletion(mock_client):
+    mock_client.delete.side_effect = ObjectNotFound('Object not found')
+    user = user_payload.copy()
+    user['name'] = ''
+    process_event(
+        mock_client, CloudEvent(attributes, message_data("UserUpdated", "1.1.0", {"user": user}))
+    )
+    mock_client.delete.assert_called_with(f'profile_{user["id"]}')
+
+@patch("typesense.client.Client")
+def test_raises_others_error_responses_on_deletion(mock_client):
+    # obviously does not cover *all* other errors but one to check if not all exceptions are "swallowed"
+    mock_client.delete.side_effect = RequestUnauthorized('Unauthorized')
+    user = user_payload.copy()
+    user['name'] = ''
+    try:
+        process_event(
+            mock_client, CloudEvent(attributes, message_data("UserUpdated", "1.1.0", {"user": user}))
+        )
+    except RequestUnauthorized:
+        pass
+    except Exception:
+        fail('Unexpected exception raised:')
+
 @patch("typesense.client.Client")
 def test_user_name_updated_ignored(mock_client):
     process_event(
-- 
GitLab