"""Tests for activity bus handler registry.""" from __future__ import annotations import pytest from collections import defaultdict from shared.events.bus import ( register_activity_handler, get_activity_handlers, _activity_handlers, ) @pytest.fixture(autouse=True) def _clean_handlers(): """Clear handler registry before each test.""" _activity_handlers.clear() yield _activity_handlers.clear() # Dummy handlers async def handler_a(activity, session): pass async def handler_b(activity, session): pass async def handler_c(activity, session): pass async def handler_global(activity, session): pass # --------------------------------------------------------------------------- # register_activity_handler # --------------------------------------------------------------------------- class TestRegisterHandler: def test_register_with_type_only(self): register_activity_handler("Create", handler_a) assert ("Create", "*") in _activity_handlers assert handler_a in _activity_handlers[("Create", "*")] def test_register_with_object_type(self): register_activity_handler("Create", handler_a, object_type="Note") assert ("Create", "Note") in _activity_handlers def test_multiple_handlers_same_key(self): register_activity_handler("Create", handler_a) register_activity_handler("Create", handler_b) assert len(_activity_handlers[("Create", "*")]) == 2 def test_wildcard_type(self): register_activity_handler("*", handler_global) assert ("*", "*") in _activity_handlers # --------------------------------------------------------------------------- # get_activity_handlers — cascading wildcard lookup # --------------------------------------------------------------------------- class TestGetHandlers: def test_exact_match(self): register_activity_handler("Create", handler_a, object_type="Note") handlers = get_activity_handlers("Create", "Note") assert handler_a in handlers def test_type_wildcard_match(self): register_activity_handler("Create", handler_a) # key: ("Create", "*") handlers = get_activity_handlers("Create", "Note") assert handler_a in handlers def test_global_wildcard_match(self): register_activity_handler("*", handler_global) # key: ("*", "*") handlers = get_activity_handlers("Create", "Note") assert handler_global in handlers def test_cascading_order(self): """Handlers should come in order: exact → type-wildcard → global-wildcard.""" register_activity_handler("Create", handler_a, object_type="Note") # exact register_activity_handler("Create", handler_b) # type-wildcard register_activity_handler("*", handler_c) # global wildcard handlers = get_activity_handlers("Create", "Note") assert handlers == [handler_a, handler_b, handler_c] def test_no_match(self): register_activity_handler("Create", handler_a, object_type="Note") handlers = get_activity_handlers("Delete", "Article") assert handlers == [] def test_no_object_type_skips_exact(self): register_activity_handler("Create", handler_a, object_type="Note") register_activity_handler("Create", handler_b) handlers = get_activity_handlers("Create") # Should get type-wildcard only (since object_type defaults to "*") assert handler_b in handlers assert handler_a not in handlers def test_global_wildcard_not_duplicated(self): """Global wildcard should not fire when activity_type is already '*'.""" register_activity_handler("*", handler_global) handlers = get_activity_handlers("*") # Should not include global wildcard twice assert handlers.count(handler_global) == 1 def test_type_wildcard_plus_global(self): register_activity_handler("Follow", handler_a) register_activity_handler("*", handler_global) handlers = get_activity_handlers("Follow") assert handler_a in handlers assert handler_global in handlers def test_only_global_wildcard(self): register_activity_handler("*", handler_global) handlers = get_activity_handlers("Like", "Post") assert handlers == [handler_global]