diff --git a/main.py b/main.py index fde4eb562c41bc8a6ac7c8978d2f92f0d8a922ca..f0ca479b26e8a9384386636486cb9bab8425c9c4 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 5df84bc5e8c7875f0e9bf87cf2957d5e623f7cd0..0b9f95194a5799e987dcc5dca9ad32b5645bfe51 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(