diff --git a/hikari/api/entity_factory.py b/hikari/api/entity_factory.py index a14a4efce7..6823222b21 100644 --- a/hikari/api/entity_factory.py +++ b/hikari/api/entity_factory.py @@ -56,12 +56,7 @@ class GatewayGuildDefinition(abc.ABC): - """Structure for handling entities within guild create and update events. - - !!! warning - The methods on this class may raise `builtins.LookupError` if called - when the relevant resource isn't available in the inner payload. - """ + """Structure for handling entities within guild create and update events.""" __slots__: typing.Sequence[str] = () @@ -71,7 +66,7 @@ def id(self) -> snowflakes.Snowflake: """ID of the guild the definition is for.""" @abc.abstractmethod - def channels(self) -> typing.Mapping[snowflakes.Snowflake, channel_models.GuildChannel]: + def channels(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, channel_models.GuildChannel]]: """Get a mapping of channel IDs to the channels that belong to the guild.""" @abc.abstractmethod @@ -83,7 +78,7 @@ def guild(self) -> guild_models.GatewayGuild: """Get the object of the guild this definition is for.""" @abc.abstractmethod - def members(self) -> typing.Mapping[snowflakes.Snowflake, guild_models.Member]: + def members(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, guild_models.Member]]: """Get a mapping of user IDs to the members that belong to the guild. !!! note @@ -91,7 +86,7 @@ def members(self) -> typing.Mapping[snowflakes.Snowflake, guild_models.Member]: """ @abc.abstractmethod - def presences(self) -> typing.Mapping[snowflakes.Snowflake, presence_models.MemberPresence]: + def presences(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, presence_models.MemberPresence]]: """Get a mapping of user IDs to the presences that are active in the guild. !!! note @@ -103,7 +98,7 @@ def roles(self) -> typing.Mapping[snowflakes.Snowflake, guild_models.Role]: """Get a mapping of role IDs to the roles that belong to the guild.""" @abc.abstractmethod - def voice_states(self) -> typing.Mapping[snowflakes.Snowflake, voice_models.VoiceState]: + def voice_states(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, voice_models.VoiceState]]: """Get a mapping of user IDs to the voice states that are active in the guild.""" diff --git a/hikari/api/event_manager.py b/hikari/api/event_manager.py index 1443133bcd..543206eef3 100644 --- a/hikari/api/event_manager.py +++ b/hikari/api/event_manager.py @@ -356,6 +356,7 @@ async def on_message(event): Wait_for: `hikari.api.event_manager.EventManager.wait_for` """ + # FIXME: Breaking change here, make sure to document @abc.abstractmethod def get_listeners( self, @@ -372,8 +373,8 @@ def get_listeners( The event type to look for. `T` must be a subclass of `hikari.events.base_events.Event`. polymorphic : builtins.bool - If `builtins.True`, this will also return the listeners of the - subclasses of the given event type. If `builtins.False`, then + If `builtins.True`, this will also return the listeners for all the + event types `event_type` will dispatch. If `builtins.False`, then only listeners for this class specifically are returned. The default is `builtins.True`. diff --git a/hikari/events/base_events.py b/hikari/events/base_events.py index 62aeed7af3..ccc068dc5b 100644 --- a/hikari/events/base_events.py +++ b/hikari/events/base_events.py @@ -51,6 +51,8 @@ REQUIRED_INTENTS_ATTR: typing.Final[str] = "___requiresintents___" NO_RECURSIVE_THROW_ATTR: typing.Final[str] = "___norecursivethrow___" +_id_counter = 1 # We start at 1 since Event is 0 + class Event(abc.ABC): """Base event type that all Hikari events should subclass.""" @@ -58,6 +60,7 @@ class Event(abc.ABC): __slots__: typing.Sequence[str] = () __dispatches: typing.ClassVar[typing.Tuple[typing.Type[Event], ...]] + __bitmask: typing.ClassVar[int] def __init_subclass__(cls) -> None: super().__init_subclass__() @@ -68,11 +71,16 @@ def __init_subclass__(cls) -> None: Event.__dispatches except AttributeError: Event.__dispatches = (Event,) + Event.__bitmask = 1 << 0 + + global _id_counter mro = cls.mro() # We don't have to explicitly include Event here as issubclass(Event, Event) returns True. # Non-event classes should be ignored. - cls.__dispatches = tuple(cls for cls in mro if issubclass(cls, Event)) + cls.__dispatches = tuple(sub_cls for sub_cls in mro if issubclass(sub_cls, Event)) + cls.__bitmask = 1 << _id_counter + _id_counter += 1 @property @abc.abstractmethod @@ -90,6 +98,11 @@ def dispatches(cls) -> typing.Sequence[typing.Type[Event]]: """Sequence of the event classes this event is dispatched as.""" return cls.__dispatches + @classmethod + def bitmask(cls) -> int: + """Bitmask for this event.""" + return cls.__bitmask + def get_required_intents_for(event_type: typing.Type[Event]) -> typing.Collection[intents.Intents]: """Retrieve the intents that are required to listen to an event type. diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py index c3157b241c..1b0f623562 100644 --- a/hikari/impl/entity_factory.py +++ b/hikari/impl/entity_factory.py @@ -291,9 +291,6 @@ def members(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, guild_ for m in self._payload["members"] } - for member_payload in self._payload["members"]: - member = self._entity_factory.deserialize_member(member_payload, guild_id=self.id) - self._members[member.user.id] = member else: self._members = None @@ -309,21 +306,17 @@ def presences(self) -> typing.Optional[typing.Mapping[snowflakes.Snowflake, pres for p in self._payload["presences"] } - for presence_payload in self._payload["presences"]: - presence = self._entity_factory.deserialize_member_presence(presence_payload, guild_id=self.id) - self._presences[presence.user_id] = presence else: self._presences = None return self._presences def roles(self) -> typing.Mapping[snowflakes.Snowflake, guild_models.Role]: - if self._roles is None: - if self._roles is undefined.UNDEFINED: - self._roles = { - snowflakes.Snowflake(r["id"]): self._entity_factory.deserialize_role(r, guild_id=self.id) - for r in self._payload["roles"] - } + if self._roles is undefined.UNDEFINED: + self._roles = { + snowflakes.Snowflake(r["id"]): self._entity_factory.deserialize_role(r, guild_id=self.id) + for r in self._payload["roles"] + } return self._roles diff --git a/hikari/impl/event_manager.py b/hikari/impl/event_manager.py index f07b1a3052..231e13ab42 100644 --- a/hikari/impl/event_manager.py +++ b/hikari/impl/event_manager.py @@ -162,13 +162,33 @@ async def on_channel_pins_update(self, shard: gateway_shard.GatewayShard, payloa # TODO: we need a method for this specifically await self.dispatch(self._event_factory.deserialize_channel_pins_update_event(shard, payload)) - # Internal granularity is preferred for GUILD_CREATE over decorator based filtering due to its large cache scope. - async def on_guild_create(self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject) -> None: + # Internal granularity is preferred for GUILD_CREATE over decorator based filtering due to its large scope. + async def on_guild_create( # noqa: C901 - Function too complex + self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject + ) -> None: """See https://discord.com/developers/docs/topics/gateway#guild-create for more info.""" - enabled_for_event = self._enabled_for_event(guild_events.GuildAvailableEvent) - if not enabled_for_event and self._cache: + event: typing.Union[guild_events.GuildAvailableEvent, guild_events.GuildJoinEvent, None] + + if "unavailable" in payload and self._enabled_for_event(guild_events.GuildAvailableEvent): + event = self._event_factory.deserialize_guild_available_event(shard, payload) + elif "unavailable" not in payload and self._enabled_for_event(guild_events.GuildJoinEvent): + event = self._event_factory.deserialize_guild_join_event(shard, payload) + else: + event = None + + if event: + # We also filter here to prevent iterating over them and calling a function that won't do anything + channels = event.channels if self._cache_enabled_for(config.CacheComponents.GUILD_CHANNELS) else None + emojis = event.emojis if self._cache_enabled_for(config.CacheComponents.EMOJIS) else None + guild = event.guild if self._cache_enabled_for(config.CacheComponents.GUILDS) else None + guild_id = event.guild.id + members = event.members if self._cache_enabled_for(config.CacheComponents.MEMBERS) else None + presences = event.presences if self._cache_enabled_for(config.CacheComponents.PRESENCES) else None + roles = event.roles if self._cache_enabled_for(config.CacheComponents.ROLES) else None + voice_states = event.voice_states if self._cache_enabled_for(config.CacheComponents.VOICE_STATES) else None + + elif self._cache: _LOGGER.log(ux.TRACE, "Skipping on_guild_create dispatch due to lack of any registered listeners") - event: typing.Union[guild_events.GuildAvailableEvent, guild_events.GuildJoinEvent, None] = None gd = self._entity_factory.deserialize_gateway_guild(payload) channels = gd.channels() if self._cache_enabled_for(config.CacheComponents.GUILD_CHANNELS) else None @@ -180,23 +200,11 @@ async def on_guild_create(self, shard: gateway_shard.GatewayShard, payload: data roles = gd.roles() if self._cache_enabled_for(config.CacheComponents.ROLES) else None voice_states = gd.voice_states() if self._cache_enabled_for(config.CacheComponents.VOICE_STATES) else None - elif enabled_for_event: - if "unavailable" in payload: - event = self._event_factory.deserialize_guild_available_event(shard, payload) - else: - event = self._event_factory.deserialize_guild_join_event(shard, payload) - - channels = event.channels - emojis = event.emojis - guild = event.guild - guild_id = guild.id - members = event.members - presences = event.presences - roles = event.roles - voice_states = event.voice_states - else: - event = None + _LOGGER.log( + ux.TRACE, "Skipping on_guild_create raw dispatch due to lack of any registered listeners or cache need" + ) + channels = None emojis = None guild = None @@ -241,16 +249,19 @@ async def on_guild_create(self, shard: gateway_shard.GatewayShard, payload: data for voice_state in voice_states.values(): self._cache.set_voice_state(voice_state) - recv_chunks = self._enabled_for_event(shard_events.MemberChunkEvent) or self._cache_enabled_for( - config.CacheComponents.MEMBERS - ) - members_declared = self._intents & intents_.Intents.GUILD_MEMBERS presences_declared = self._intents & intents_.Intents.GUILD_PRESENCES - # When intents are enabled discord will only send other member objects on the guild create + # When intents are enabled Discord will only send other member objects on the guild create # payload if presence intents are also declared, so if this isn't the case then we also want # to chunk small guilds. - if recv_chunks and members_declared and (payload.get("large") or not presences_declared): + if ( + self._intents & intents_.Intents.GUILD_MEMBERS + and (payload.get("large") or not presences_declared) + and ( + self._cache_enabled_for(config.CacheComponents.MEMBERS) + or self._enabled_for_event(shard_events.MemberChunkEvent) + ) + ): # We create a task here instead of awaiting the result to avoid any rate-limits from delaying dispatch. nonce = f"{shard.id}.{_fixed_size_nonce()}" @@ -263,28 +274,30 @@ async def on_guild_create(self, shard: gateway_shard.GatewayShard, payload: data if event: await self.dispatch(event) - # Internal granularity is preferred for GUILD_UPDATE over decorator based filtering due to its large cache scope. + # Internal granularity is preferred for GUILD_UPDATE over decorator based filtering due to its large scope. async def on_guild_update(self, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject) -> None: """See https://discord.com/developers/docs/topics/gateway#guild-update for more info.""" - enabled_for_event = self._enabled_for_event(guild_events.GuildUpdateEvent) + event: typing.Optional[guild_events.GuildUpdateEvent] + if self._enabled_for_event(guild_events.GuildUpdateEvent): + guild_id = snowflakes.Snowflake(payload["id"]) + old = self._cache.get_guild(guild_id) if self._cache else None + event = self._event_factory.deserialize_guild_update_event(shard, payload, old_guild=old) + + # We also filter here to prevent iterating over them and calling a function that won't do anything + emojis = event.emojis if self._cache_enabled_for(config.CacheComponents.EMOJIS) else None + guild = event.guild if self._cache_enabled_for(config.CacheComponents.GUILDS) else None + roles = event.roles if self._cache_enabled_for(config.CacheComponents.ROLES) else None - if not enabled_for_event and self._cache: + elif self._cache: _LOGGER.log(ux.TRACE, "Skipping on_guild_update raw dispatch due to lack of any registered listeners") - event: typing.Optional[guild_events.GuildUpdateEvent] = None + event = None + gd = self._entity_factory.deserialize_gateway_guild(payload) emojis = gd.emojis() if self._cache_enabled_for(config.CacheComponents.EMOJIS) else None guild = gd.guild() if self._cache_enabled_for(config.CacheComponents.GUILDS) else None guild_id = gd.id roles = gd.roles() if self._cache_enabled_for(config.CacheComponents.ROLES) else None - elif enabled_for_event: - guild_id = snowflakes.Snowflake(payload["id"]) - old = self._cache.get_guild(guild_id) if self._cache else None - event = self._event_factory.deserialize_guild_update_event(shard, payload, old_guild=old) - emojis = event.emojis - guild = event.guild - roles = event.roles - else: _LOGGER.log( ux.TRACE, "Skipping on_guild_update raw dispatch due to lack of any registered listeners or cache need" diff --git a/hikari/impl/event_manager_base.py b/hikari/impl/event_manager_base.py index f1c275b92b..bc09714a1e 100644 --- a/hikari/impl/event_manager_base.py +++ b/hikari/impl/event_manager_base.py @@ -39,7 +39,6 @@ from hikari import config from hikari import errors from hikari import iterators -from hikari import undefined from hikari.api import event_manager as event_manager_ from hikari.events import base_events from hikari.events import shard_events @@ -56,9 +55,6 @@ from hikari.api import shard as gateway_shard from hikari.internal import data_binding -_LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.event_manager") - -if typing.TYPE_CHECKING: ConsumerT = typing.Callable[ [gateway_shard.GatewayShard, data_binding.JSONObject], typing.Coroutine[typing.Any, typing.Any, None] ] @@ -67,7 +63,7 @@ typing.List[event_manager_.CallbackT[event_manager_.EventT_co]], ] WaiterT = typing.Tuple[ - event_manager_.PredicateT[event_manager_.EventT_co], asyncio.Future[event_manager_.EventT_co] + typing.Optional[event_manager_.PredicateT[event_manager_.EventT_co]], asyncio.Future[event_manager_.EventT_co] ] WaiterMapT = typing.Dict[typing.Type[event_manager_.EventT_co], typing.Set[WaiterT[event_manager_.EventT_co]]] @@ -78,6 +74,8 @@ ] _EventStreamT = typing.TypeVar("_EventStreamT", bound="EventStream[typing.Any]") +_LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.event_manager") + @typing.runtime_checkable class _FilteredMethodT(fast_protocol.FastProtocolChecking, typing.Protocol): @@ -89,7 +87,7 @@ def __cache_components__(self) -> config.CacheComponents: raise NotImplementedError @property - def __event_types__(self) -> typing.Sequence[typing.Type[base_events.Event]]: + def __events_bitmask__(self) -> int: raise NotImplementedError @@ -268,10 +266,6 @@ def open(self) -> None: self._active = True -def _default_predicate(_: event_manager_.EventT_inv) -> bool: - return True - - def _assert_is_listener(parameters: typing.Iterator[inspect.Parameter], /) -> None: if next(parameters, None) is None: raise TypeError("Event listener must have one positional argument for the event object.") @@ -306,26 +300,41 @@ def filtered( else: event_types = event_types.dispatches() + bitmask = 0 + for event_type in event_types: + bitmask |= event_type.bitmask() + + # https://github.com/python/mypy/issues/2087 def decorator(method: UnboundMethodT[EventManagerBaseT], /) -> UnboundMethodT[EventManagerBaseT]: method.__cache_components__ = cache_components # type: ignore[attr-defined] - method.__event_types__ = event_types # type: ignore[attr-defined] + method.__events_bitmask__ = bitmask # type: ignore[attr-defined] assert isinstance(method, _FilteredMethodT), "Incorrect attribute(s) set for a filtered method" return method # type: ignore[unreachable] return decorator -@attr.define(hash=True) +@attr.define(weakref_slot=False, hash=True) class _Consumer: callback: ConsumerT = attr.ib(hash=True) """The callback function for this consumer.""" - event_types: undefined.UndefinedOr[typing.Sequence[typing.Type[base_events.Event]]] = attr.ib(hash=False) - """A sequence of the types of events this consumer dispatches to, if set.""" + events_bitmask: int = attr.ib(hash=False) + """The registered events bitmask.""" is_caching: bool = attr.ib(hash=False) """Cached value of whether or not this consumer is making cache calls in the current env.""" + listener_group_count: int = attr.ib(init=False, hash=False, default=0) + """The number of listener groups registered to this consumer.""" + + waiter_group_count: int = attr.ib(init=False, hash=False, default=0) + """The number of waiters groups registered to this consumer.""" + + @property + def is_enabled(self) -> bool: + return self.is_caching or self.listener_group_count > 0 or self.waiter_group_count > 0 + class EventManagerBase(event_manager_.EventManager): """Provides functionality to consume and dispatch events. @@ -336,7 +345,6 @@ class EventManagerBase(event_manager_.EventManager): __slots__: typing.Sequence[str] = ( "_consumers", - "_enabled_consumers_cache", "_event_factory", "_intents", "_listeners", @@ -351,7 +359,6 @@ def __init__( cache_components: config.CacheComponents = config.CacheComponents.NONE, ) -> None: self._consumers: typing.Dict[str, _Consumer] = {} - self._enabled_consumers_cache: typing.Dict[_Consumer, bool] = {} self._event_factory = event_factory self._intents = intents self._listeners: ListenerMapT[base_events.Event] = {} @@ -362,15 +369,29 @@ def __init__( event_name = name[3:] if isinstance(member, _FilteredMethodT): caching = (member.__cache_components__ & cache_components) != 0 - self._consumers[event_name] = _Consumer(member, member.__event_types__, caching) + + self._consumers[event_name] = _Consumer(member, member.__events_bitmask__, caching) else: - self._consumers[event_name] = _Consumer( - member, undefined.UNDEFINED, cache_components != cache_components.NONE - ) + self._consumers[event_name] = _Consumer(member, -1, cache_components != cache_components.NONE) - def _clear_enabled_cache(self) -> None: - self._enabled_consumers_cache = {} + def _increment_listener_group_count( + self, + event_type: typing.Type[base_events.Event], + count: typing.Literal[-1, 1], + ) -> None: + event_bitmask = event_type.bitmask() + for consumer in self._consumers.values(): + if (consumer.events_bitmask & event_bitmask) == event_bitmask: + consumer.listener_group_count += count + + def _increment_waiter_group_count( + self, event_type: typing.Type[base_events.Event], count: typing.Literal[-1, 1] + ) -> None: + event_bitmask = event_type.bitmask() + for consumer in self._consumers.values(): + if (consumer.events_bitmask & event_bitmask) == event_bitmask: + consumer.waiter_group_count += count def _enabled_for_event(self, event_type: typing.Type[base_events.Event], /) -> bool: for cls in event_type.dispatches(): @@ -379,24 +400,6 @@ def _enabled_for_event(self, event_type: typing.Type[base_events.Event], /) -> b return False - def _enabled_for_consumer(self, consumer: _Consumer, /) -> bool: - # If undefined then we can only assume that this may link to registered listeners. - if consumer.event_types is undefined.UNDEFINED or consumer.is_caching: - return True - - if (cached_value := self._enabled_consumers_cache.get(consumer)) is not None: - return cached_value - - # The behaviour here where an empty sequence for event_types will lead to this always - # being skipped unless there's a relevant enabled cache resource is intended behaviour. - for event_type in consumer.event_types: - if event_type in self._listeners or event_type in self._waiters: - self._enabled_consumers_cache[consumer] = True - return True - - self._enabled_consumers_cache[consumer] = False - return False - def consume_raw_event( self, event_name: str, shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject ) -> None: @@ -413,7 +416,7 @@ def subscribe( *, _nested: int = 0, ) -> None: - if not issubclass(event_type, base_events.Event): + if not inspect.isclass(event_type) or not issubclass(event_type, base_events.Event): raise TypeError("Cannot subscribe to a non-Event type") if not inspect.iscoroutinefunction(callback): @@ -435,7 +438,7 @@ def subscribe( self._listeners[event_type].append(callback) # type: ignore[arg-type] except KeyError: self._listeners[event_type] = [callback] # type: ignore[list-item] - self._clear_enabled_cache() + self._increment_listener_group_count(event_type, 1) def _check_intents(self, event_type: typing.Type[event_manager_.EventT_co], nested: int) -> None: # Collection of combined bitfield combinations of intents that @@ -465,16 +468,16 @@ def get_listeners( ) -> typing.Collection[event_manager_.CallbackT[event_manager_.EventT_co]]: if polymorphic: listeners: typing.List[event_manager_.CallbackT[event_manager_.EventT_co]] = [] - for subscribed_event_type, subscribed_listeners in self._listeners.items(): - if issubclass(subscribed_event_type, event_type): - listeners += subscribed_listeners + for event in event_type.dispatches(): + if subscribed_listeners := self._listeners.get(event): + listeners.extend(subscribed_listeners) + return listeners - else: - if items := self._listeners.get(event_type): - return items.copy() + if items := self._listeners.get(event_type): + return items.copy() - return [] + return [] def unsubscribe( self, @@ -492,7 +495,7 @@ def unsubscribe( listeners.remove(callback) # type: ignore[arg-type] if not listeners: del self._listeners[event_type] - self._clear_enabled_cache() + self._increment_listener_group_count(event_type, -1) def listen( self, @@ -531,7 +534,6 @@ def dispatch(self, event: event_manager_.EventT_inv) -> asyncio.Future[typing.An raise TypeError(f"Events must be subclasses of {base_events.Event.__name__}, not {type(event).__name__}") tasks: typing.List[typing.Coroutine[None, typing.Any, None]] = [] - clear_cache = False for cls in event.dispatches(): if listeners := self._listeners.get(cls): @@ -546,8 +548,7 @@ def dispatch(self, event: event_manager_.EventT_inv) -> asyncio.Future[typing.An predicate, future = waiter if not future.done(): try: - result = predicate(event) - if not result: + if predicate is not None and not predicate(event): continue except Exception as ex: future.set_exception(ex) @@ -558,10 +559,7 @@ def dispatch(self, event: event_manager_.EventT_inv) -> asyncio.Future[typing.An if not waiter_set: del self._waiters[cls] - clear_cache = True - - if clear_cache: - self._clear_enabled_cache() + self._increment_waiter_group_count(cls, -1) return asyncio.gather(*tasks) if tasks else aio.completed_future() @@ -582,9 +580,6 @@ async def wait_for( timeout: typing.Union[float, int, None], predicate: typing.Optional[event_manager_.PredicateT[event_manager_.EventT_co]] = None, ) -> event_manager_.EventT_co: - if predicate is None: - predicate = _default_predicate - self._check_intents(event_type, 1) future: asyncio.Future[event_manager_.EventT_co] = asyncio.get_running_loop().create_future() @@ -592,9 +587,9 @@ async def wait_for( try: waiter_set = self._waiters[event_type] except KeyError: - self._clear_enabled_cache() waiter_set = set() self._waiters[event_type] = waiter_set + self._increment_waiter_group_count(event_type, 1) pair = (predicate, future) @@ -605,7 +600,7 @@ async def wait_for( waiter_set.remove(pair) # type: ignore[arg-type] if not waiter_set: del self._waiters[event_type] - self._clear_enabled_cache() + self._increment_waiter_group_count(event_type, -1) raise @@ -615,7 +610,7 @@ async def _handle_dispatch( shard: gateway_shard.GatewayShard, payload: data_binding.JSONObject, ) -> None: - if not self._enabled_for_consumer(consumer): + if not consumer.is_enabled: name = consumer.callback.__name__ _LOGGER.log( ux.TRACE, "Skipping raw dispatch for %s due to lack of any registered listeners or cache need", name diff --git a/tests/hikari/impl/test_event_factory.py b/tests/hikari/impl/test_event_factory.py index f138d94f09..fb73d6c5fe 100644 --- a/tests/hikari/impl/test_event_factory.py +++ b/tests/hikari/impl/test_event_factory.py @@ -234,16 +234,16 @@ def test_deserialize_guild_join_event(self, event_factory, mock_app, mock_shard) event = event_factory.deserialize_guild_join_event(mock_shard, mock_payload) mock_app.entity_factory.deserialize_gateway_guild.assert_called_once_with(mock_payload) - mock_gateway_guild = mock_app.entity_factory.deserialize_gateway_guild.return_value assert isinstance(event, guild_events.GuildJoinEvent) assert event.shard is mock_shard - assert event.guild is mock_gateway_guild.guild.return_value - assert event.emojis is mock_gateway_guild.emojis.return_value - assert event.roles is mock_gateway_guild.roles.return_value - assert event.channels is mock_gateway_guild.channels.return_value - assert event.members is mock_gateway_guild.members.return_value - assert event.presences is mock_gateway_guild.presences.return_value - assert event.voice_states is mock_gateway_guild.voice_states.return_value + guild_definition = mock_app.entity_factory.deserialize_gateway_guild.return_value + assert event.guild is guild_definition.guild.return_value + assert event.emojis is guild_definition.emojis.return_value + assert event.roles is guild_definition.roles.return_value + assert event.channels is guild_definition.channels.return_value + assert event.members is guild_definition.members.return_value + assert event.presences is guild_definition.presences.return_value + assert event.voice_states is guild_definition.voice_states.return_value def test_deserialize_guild_update_event(self, event_factory, mock_app, mock_shard): mock_payload = mock.Mock(app=mock_app) diff --git a/tests/hikari/impl/test_event_manager.py b/tests/hikari/impl/test_event_manager.py index eab2b7507c..8060639f31 100644 --- a/tests/hikari/impl/test_event_manager.py +++ b/tests/hikari/impl/test_event_manager.py @@ -34,7 +34,6 @@ from hikari import intents from hikari import presences from hikari.events import guild_events -from hikari.events import shard_events from hikari.impl import event_manager from hikari.internal import time from tests.hikari import hikari_test_helpers @@ -97,7 +96,7 @@ def event_factory(self): return mock.Mock() @pytest.fixture() - def event_manager(self, entity_factory, event_factory): + def event_manager_impl(self, entity_factory, event_factory): obj = hikari_test_helpers.mock_class_namespace(event_manager.EventManagerImpl, slots_=False)( entity_factory, event_factory, intents.Intents.ALL, cache=mock.Mock(settings=config.CacheSettings()) ) @@ -106,7 +105,7 @@ def event_manager(self, entity_factory, event_factory): return obj @pytest.fixture() - def stateless_event_manager(self, event_factory, entity_factory): + def stateless_event_manager_impl(self, event_factory, entity_factory): obj = hikari_test_helpers.mock_class_namespace(event_manager.EventManagerImpl, slots_=False)( entity_factory, event_factory, intents.Intents.ALL, cache=None ) @@ -115,1301 +114,1092 @@ def stateless_event_manager(self, event_factory, entity_factory): return obj @pytest.mark.asyncio() - async def test_on_ready_stateful(self, event_manager, shard, event_factory): + async def test_on_ready_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(my_user=mock.Mock()) event_factory.deserialize_ready_event.return_value = event - await event_manager.on_ready(shard, payload) + await event_manager_impl.on_ready(shard, payload) - event_manager._cache.update_me.assert_called_once_with(event.my_user) + event_manager_impl._cache.update_me.assert_called_once_with(event.my_user) event_factory.deserialize_ready_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_ready_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_ready_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_ready(shard, payload) + await stateless_event_manager_impl.on_ready(shard, payload) event_factory.deserialize_ready_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_ready_event.return_value) + stateless_event_manager_impl.dispatch.assert_awaited_once_with( + event_factory.deserialize_ready_event.return_value + ) @pytest.mark.asyncio() - async def test_on_resumed(self, event_manager, shard, event_factory): + async def test_on_resumed(self, event_manager_impl, shard, event_factory): payload = {} - await event_manager.on_resumed(shard, payload) + await event_manager_impl.on_resumed(shard, payload) event_factory.deserialize_resumed_event.assert_called_once_with(shard) - event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_resumed_event.return_value) + event_manager_impl.dispatch.assert_awaited_once_with(event_factory.deserialize_resumed_event.return_value) @pytest.mark.asyncio() - async def test_on_channel_create_stateful(self, event_manager, shard, event_factory): + async def test_on_channel_create_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(channel=mock.Mock(channels.GuildChannel)) event_factory.deserialize_guild_channel_create_event.return_value = event - await event_manager.on_channel_create(shard, payload) + await event_manager_impl.on_channel_create(shard, payload) - event_manager._cache.set_guild_channel.assert_called_once_with(event.channel) + event_manager_impl._cache.set_guild_channel.assert_called_once_with(event.channel) event_factory.deserialize_guild_channel_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_channel_create_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_channel_create_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_channel_create(shard, payload) + await stateless_event_manager_impl.on_channel_create(shard, payload) event_factory.deserialize_guild_channel_create_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_channel_create_event.return_value ) @pytest.mark.asyncio() - async def test_on_channel_update_stateful(self, event_manager, shard, event_factory): + async def test_on_channel_update_stateful(self, event_manager_impl, shard, event_factory): payload = {"id": 123} old_channel = object() event = mock.Mock(channel=mock.Mock(channels.GuildChannel)) event_factory.deserialize_guild_channel_update_event.return_value = event - event_manager._cache.get_guild_channel.return_value = old_channel + event_manager_impl._cache.get_guild_channel.return_value = old_channel - await event_manager.on_channel_update(shard, payload) + await event_manager_impl.on_channel_update(shard, payload) - event_manager._cache.get_guild_channel.assert_called_once_with(123) - event_manager._cache.update_guild_channel.assert_called_once_with(event.channel) + event_manager_impl._cache.get_guild_channel.assert_called_once_with(123) + event_manager_impl._cache.update_guild_channel.assert_called_once_with(event.channel) event_factory.deserialize_guild_channel_update_event.assert_called_once_with( shard, payload, old_channel=old_channel ) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_channel_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_channel_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"id": 123} - await stateless_event_manager.on_channel_update(shard, payload) + await stateless_event_manager_impl.on_channel_update(shard, payload) event_factory.deserialize_guild_channel_update_event.assert_called_once_with(shard, payload, old_channel=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_channel_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_channel_delete_stateful(self, event_manager, shard, event_factory): + async def test_on_channel_delete_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(channel=mock.Mock(id=123)) event_factory.deserialize_guild_channel_delete_event.return_value = event - await event_manager.on_channel_delete(shard, payload) + await event_manager_impl.on_channel_delete(shard, payload) - event_manager._cache.delete_guild_channel.assert_called_once_with(123) + event_manager_impl._cache.delete_guild_channel.assert_called_once_with(123) event_factory.deserialize_guild_channel_delete_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_channel_delete_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_channel_delete_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_channel_delete(shard, payload) + await stateless_event_manager_impl.on_channel_delete(shard, payload) event_factory.deserialize_guild_channel_delete_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_channel_delete_event.return_value ) @pytest.mark.asyncio() - async def test_on_channel_pins_update(self, stateless_event_manager, shard, event_factory): + async def test_on_channel_pins_update(self, stateless_event_manager_impl, shard, event_factory): payload = {} - event = mock.Mock() - event_factory.deserialize_channel_pins_update_event.return_value = event - - await stateless_event_manager.on_channel_pins_update(shard, payload) + await stateless_event_manager_impl.on_channel_pins_update(shard, payload) event_factory.deserialize_channel_pins_update_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with(event) - - @pytest.mark.asyncio() - async def test_on_guild_create_stateful_with_unavailable_field(self, event_manager, shard, event_factory): - payload = {"unavailable": False} - event = mock.Mock( - guild=mock.Mock(id=123, is_large=False), - channels={"TestChannel": 456}, - emojis={"TestEmoji": 789}, - roles={"TestRole": 1234}, - members={"TestMember": 5678}, - presences={"TestPresence": 9012}, - voice_states={"TestState": 345}, - chunk_nonce=None, + stateless_event_manager_impl.dispatch.assert_awaited_once_with( + event_factory.deserialize_channel_pins_update_event.return_value ) - event_factory.deserialize_guild_available_event.return_value = event - shard.request_guild_members = mock.AsyncMock() - - await event_manager.on_guild_create(shard, payload) - - assert event.chunk_nonce is None - shard.request_guild_members.assert_not_called() - - event_manager._cache.update_guild.assert_called_once_with(event.guild) - - event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123) - event_manager._cache.set_guild_channel.assert_called_once_with(456) - - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(789) - - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(1234) - - event_manager._cache.clear_members_for_guild.assert_called_once_with(123) - event_manager._cache.set_member.assert_called_once_with(5678) - - event_manager._cache.clear_presences_for_guild.assert_called_once_with(123) - event_manager._cache.set_presence.assert_called_once_with(9012) - - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.set_voice_state.assert_called_once_with(345) - - event_factory.deserialize_guild_available_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) - - async def test_on_guild_create_stateful_and_dispatching(self, event_manager, shard, event_factory): - payload = {} - event = mock.Mock( - guild=mock.Mock(id=123, is_large=False), - channels={"TestChannel": 456}, - emojis={"TestEmoji": 789}, - roles={"TestRole": 1234}, - members={"TestMember": 5678}, - presences={"TestPresence": 9012}, - voice_states={"TestState": 345}, - chunk_nonce=None, - ) - - event_factory.deserialize_guild_join_event.return_value = event - event_manager._enabled_for_event = mock.Mock(return_value=True) - event_factory.deserialize_guild_join_event.return_value = event - shard.request_guild_members = mock.AsyncMock() - - await event_manager.on_guild_create(shard, payload) - - assert event.chunk_nonce is None - shard.request_guild_members.assert_not_called() - event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - - event_manager._cache.update_guild.assert_called_once_with(event.guild) - - event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123) - event_manager._cache.set_guild_channel.assert_called_once_with(456) - - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(789) - - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(1234) - - event_manager._cache.clear_members_for_guild.assert_called_once_with(123) - event_manager._cache.set_member.assert_called_once_with(5678) - - event_manager._cache.clear_presences_for_guild.assert_called_once_with(123) - event_manager._cache.set_presence.assert_called_once_with(9012) - - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.set_voice_state.assert_called_once_with(345) - - event_factory.deserialize_guild_join_event.assert_called_once_with(shard, payload) - event_factory.deserialize_gateway_guild.assert_not_called() - event_factory.deserialize_guild_join_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) - + @pytest.mark.parametrize("unavailable", [True, False]) @pytest.mark.asyncio() - async def test_on_guild_create_when_request_chunks_with_unavailable_field( - self, event_manager, shard, event_factory + async def test_on_guild_create_when_not_dispatching_and_not_caching( + self, event_manager_impl, shard, event_factory, entity_factory, unavailable ): - payload = {"unavailable": False} - event = mock.Mock( - guild=mock.Mock(id=123, is_large=True), - channels={"TestChannel": 456}, - emojis={"TestEmoji": 789}, - roles={"TestRole": 1234}, - members={"TestMember": 5678}, - presences={"TestPresence": 9012}, - voice_states={"TestState": 345}, - chunk_nonce=None, - ) + payload = {"unavailable": False} if unavailable else {} + event_manager_impl._intents = intents.Intents.NONE + event_manager_impl._cache_enabled_for = mock.Mock(return_value=False) + event_manager_impl._enabled_for_event = mock.Mock(return_value=False) - event_factory.deserialize_guild_available_event.return_value = event - shard.request_guild_members = mock.Mock() + with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members: + await event_manager_impl.on_guild_create(shard, payload) - stack = contextlib.ExitStack() - create_task = stack.enter_context(mock.patch.object(asyncio, "create_task")) - uuid = stack.enter_context(mock.patch("hikari.impl.event_manager._fixed_size_nonce", return_value="uuid")) - _request_guild_members = stack.enter_context( - mock.patch("hikari.impl.event_manager._request_guild_members", new_callable=mock.Mock) - ) + if unavailable: + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent) + else: + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent) - with stack: - await event_manager.on_guild_create(shard, payload) - - uuid.assert_called_once_with() - nonce = "987.uuid" - assert event.chunk_nonce == nonce - _request_guild_members.assert_called_once_with(shard, event.guild, include_presences=True, nonce=nonce) - create_task.assert_called_once_with( - _request_guild_members.return_value, name="987:123 guild create members request" - ) - - event_manager._cache.update_guild.assert_called_once_with(event.guild) - - event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123) - event_manager._cache.set_guild_channel.assert_called_once_with(456) - - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(789) - - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(1234) - - event_manager._cache.clear_members_for_guild.assert_called_once_with(123) - event_manager._cache.set_member.assert_called_once_with(5678) - - event_manager._cache.clear_presences_for_guild.assert_called_once_with(123) - event_manager._cache.set_presence.assert_called_once_with(9012) - - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.set_voice_state.assert_called_once_with(345) - - event_factory.deserialize_guild_available_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) - - @pytest.mark.asyncio() - async def test_on_guild_create_stateful_and_not_dispatching_with_all_cache_components( - self, event_manager, shard, entity_factory, event_factory + event_factory.deserialize_guild_join_event.assert_not_called() + event_factory.deserialize_guild_available_event.assert_not_called() + entity_factory.deserialize_gateway_guild.assert_called_once_with(payload) + event_manager_impl._cache.update_guild.assert_not_called() + event_manager_impl._cache.clear_guild_channels_for_guild.assert_not_called() + event_manager_impl._cache.set_guild_channel.assert_not_called() + event_manager_impl._cache.clear_emojis_for_guild.assert_not_called() + event_manager_impl._cache.set_emoji.assert_not_called() + event_manager_impl._cache.clear_roles_for_guild.assert_not_called() + event_manager_impl._cache.set_role.assert_not_called() + event_manager_impl._cache.clear_members_for_guild.assert_not_called() + event_manager_impl._cache.set_member.assert_not_called() + event_manager_impl._cache.clear_presences_for_guild.assert_not_called() + event_manager_impl._cache.set_presence.assert_not_called() + event_manager_impl._cache.clear_voice_states_for_guild.assert_not_called() + event_manager_impl._cache.set_voice_state.assert_not_called() + request_guild_members.assert_not_called() + + event_manager_impl.dispatch.assert_not_called() + + @pytest.mark.parametrize("unavailable", [True, False]) + @pytest.mark.asyncio() + async def test_on_guild_create_when_not_dispatching_and_caching( + self, event_manager_impl, shard, event_factory, entity_factory, unavailable ): - payload = {"id": "123"} - mock_channel = object() - mock_emoji = object() - mock_role = object() - mock_member = object() - mock_presence = object() - mock_voice_state = object() - guild_definition = entity_factory.deserialize_gateway_guild.return_value - guild_definition.id = 123 - guild_definition.guild.return_value = mock.Mock(id=123, is_large=False) - guild_definition.channels.return_value = {456: mock_channel} - guild_definition.emojis.return_value = {789: mock_emoji} - guild_definition.roles.return_value = {1234: mock_role} - guild_definition.members.return_value = {5678: mock_member} - guild_definition.presences.return_value = {9012: mock_presence} - guild_definition.voice_states.return_value = {345: mock_voice_state} - - event_manager._enabled_for_event = mock.Mock(return_value=False) - shard.request_guild_members = mock.AsyncMock() - - await event_manager.on_guild_create(shard, payload) - - shard.request_guild_members.assert_not_called() - event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - - event_manager._cache.update_guild.assert_called_once_with(guild_definition.guild.return_value) - - event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123) - event_manager._cache.set_guild_channel.assert_called_once_with(mock_channel) - - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(mock_emoji) - - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(mock_role) - - event_manager._cache.clear_members_for_guild.assert_called_once_with(123) - event_manager._cache.set_member.assert_called_once_with(mock_member) - - event_manager._cache.clear_presences_for_guild.assert_called_once_with(123) - event_manager._cache.set_presence.assert_called_once_with(mock_presence) - - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.set_voice_state.assert_called_once_with(mock_voice_state) + payload = {"unavailable": False} if unavailable else {} + event_manager_impl._intents = intents.Intents.NONE + event_manager_impl._cache_enabled_for = mock.Mock(return_value=True) + event_manager_impl._enabled_for_event = mock.Mock(return_value=False) + gateway_guild = entity_factory.deserialize_gateway_guild.return_value + gateway_guild.channels.return_value = {1: "channel1", 2: "channel2"} + gateway_guild.emojis.return_value = {1: "emoji1", 2: "emoji2"} + gateway_guild.roles.return_value = {1: "role1", 2: "role2"} + gateway_guild.voice_states.return_value = {1: "voice1", 2: "voice2"} + gateway_guild.presences.return_value = {1: "presence1", 2: "presence2"} + gateway_guild.members.return_value = {1: "member1", 2: "member2"} + + with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members: + await event_manager_impl.on_guild_create(shard, payload) + + if unavailable: + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent) + else: + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent) + event_factory.deserialize_guild_join_event.assert_not_called() + event_factory.deserialize_guild_available_event.assert_not_called() entity_factory.deserialize_gateway_guild.assert_called_once_with(payload) - event_factory.deserialize_guild_create_event.assert_not_called() - event_manager.dispatch.assert_not_called() - - @pytest.mark.asyncio() - async def test_on_guild_create_stateful_and_not_dispatching_with_no_cache_components( - self, event_manager, shard, event_factory, entity_factory + event_manager_impl._cache.update_guild.assert_called_once_with(gateway_guild.guild.return_value) + event_manager_impl._cache.clear_guild_channels_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_guild_channel.assert_has_calls([mock.call("channel1"), mock.call("channel2")]) + event_manager_impl._cache.clear_emojis_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_emoji.assert_has_calls([mock.call("emoji1"), mock.call("emoji2")]) + event_manager_impl._cache.clear_roles_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_role.assert_has_calls([mock.call("role1"), mock.call("role2")]) + event_manager_impl._cache.clear_members_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_member.assert_has_calls([mock.call("member1"), mock.call("member2")]) + event_manager_impl._cache.clear_presences_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_presence.assert_has_calls([mock.call("presence1"), mock.call("presence2")]) + event_manager_impl._cache.clear_voice_states_for_guild.assert_called_once_with(gateway_guild.id) + event_manager_impl._cache.set_voice_state.assert_has_calls([mock.call("voice1"), mock.call("voice2")]) + request_guild_members.assert_not_called() + + event_manager_impl.dispatch.assert_not_called() + + @pytest.mark.parametrize("unavailable", [True, False]) + @pytest.mark.asyncio() + async def test_on_guild_create_when_stateless( + self, stateless_event_manager_impl, shard, event_factory, entity_factory, unavailable ): - payload = {"id": "123"} - event_manager._cache_enabled_for = mock.Mock(return_value=False) - event_manager._enabled_for_event = mock.Mock(return_value=False) - shard.request_guild_members = mock.AsyncMock() - - await event_manager.on_guild_create(shard, payload) - - shard.request_guild_members.assert_not_called() - event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - - event_manager._cache.update_guild.assert_not_called() - - event_manager._cache.clear_guild_channels_for_guild.assert_not_called() - event_manager._cache.set_guild_channel.assert_not_called() - - event_manager._cache.clear_emojis_for_guild.assert_not_called() - event_manager._cache.set_emoji.assert_not_called() + payload = {"id": 123} + if unavailable: + payload["unavailable"] = False - event_manager._cache.clear_roles_for_guild.assert_not_called() - event_manager._cache.set_role.assert_not_called() + stateless_event_manager_impl._intents = intents.Intents.NONE + stateless_event_manager_impl._cache_enabled_for = mock.Mock(return_value=True) + stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=False) - event_manager._cache.clear_members_for_guild.assert_not_called() - event_manager._cache.set_member.assert_not_called() + with mock.patch.object(event_manager, "_request_guild_members") as request_guild_members: + await stateless_event_manager_impl.on_guild_create(shard, payload) - event_manager._cache.clear_presences_for_guild.assert_not_called() - event_manager._cache.set_presence.assert_not_called() + if unavailable: + stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildAvailableEvent) + else: + stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildJoinEvent) - event_manager._cache.clear_voice_states_for_guild.assert_not_called() - event_manager._cache.set_voice_state.assert_not_called() + event_factory.deserialize_guild_join_event.assert_not_called() + event_factory.deserialize_guild_available_event.assert_not_called() + request_guild_members.assert_not_called() - entity_factory.deserialize_gateway_guild.assert_called_once_with(payload) - event_factory.deserialize_guild_create_event.assert_not_called() - event_manager.dispatch.assert_not_called() + stateless_event_manager_impl.dispatch.assert_not_called() @pytest.mark.asyncio() - async def test_on_guild_create_when_request_chunks_when_dispatching_available_event( - self, event_manager, shard, event_factory + async def test_on_guild_create_when_members_declared_and_member_cache_enabled( + self, + stateless_event_manager_impl, + shard, + event_factory, + entity_factory, ): - payload = {"large": True, "id": 123} - event = mock.Mock( - guild=mock.Mock(id=123, is_large=True), - channels={"TestChannel": 456}, - emojis={"TestEmoji": 789}, - roles={"TestRole": 1234}, - members={"TestMember": 5678}, - presences={"TestPresence": 9012}, - voice_states={"TestState": 345}, - chunk_nonce=None, - ) - - event_manager._enabled_for_event = mock.Mock(return_value=True) - event_factory.deserialize_guild_join_event.return_value = event - event_manager._cache.settings.components = config.CacheComponents.MEMBERS - shard.request_guild_members = mock.Mock() - - stack = contextlib.ExitStack() - create_task = stack.enter_context(mock.patch.object(asyncio, "create_task")) - uuid = stack.enter_context(mock.patch("hikari.impl.event_manager._fixed_size_nonce", return_value="uuid")) - _request_guild_members = stack.enter_context( - mock.patch("hikari.impl.event_manager._request_guild_members", new_callable=mock.Mock) - ) - - with stack: - await event_manager.on_guild_create(shard, payload) - - event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - uuid.assert_called_once_with() - nonce = "987.uuid" - assert event.chunk_nonce == nonce - _request_guild_members.assert_called_once_with(shard, 123, include_presences=True, nonce=nonce) + shard.id = 123 + stateless_event_manager_impl._intents = intents.Intents.GUILD_MEMBERS + stateless_event_manager_impl._cache_enabled_for = mock.Mock(return_value=True) + stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=False) + mock_request_guild_members = mock.Mock() + + with mock.patch.object(asyncio, "create_task") as create_task: + with mock.patch.object(event_manager, "_fixed_size_nonce", return_value="abc"): + with mock.patch.object(event_manager, "_request_guild_members", new=mock_request_guild_members): + await stateless_event_manager_impl.on_guild_create(shard, {"id": 456, "large": False}) + + mock_request_guild_members.assert_called_once_with(shard, 456, include_presences=False, nonce="123.abc") create_task.assert_called_once_with( - _request_guild_members.return_value, name="987:123 guild create members request" + mock_request_guild_members.return_value, name="123:456 guild create members request" ) - event_factory.deserialize_guild_create_event.assert_called_once_with(shard, payload) - @pytest.mark.asyncio() - async def test_on_guild_create_when_request_chunks_when_not_dispatching_available_event( - self, stateless_event_manager, shard, entity_factory, event_factory, event_manager + async def test_on_guild_create_when_members_declared_and_enabled_for_member_chunk_event( + self, + stateless_event_manager_impl, + shard, + event_factory, + entity_factory, ): - payload = {"large": True, "id": 123} - - stateless_event_manager._enabled_for_event = mock.Mock(side_effect=[False, True]) - entity_factory.deserialize_gateway_guild.return_value.id = 123 - - stack = contextlib.ExitStack() - _request_guild_members = stack.enter_context( - mock.patch("hikari.impl.event_manager._request_guild_members", new_callable=mock.Mock) - ) - create_task = stack.enter_context(mock.patch.object(asyncio, "create_task")) - uuid = stack.enter_context(mock.patch("hikari.impl.event_manager._fixed_size_nonce", return_value="uuid")) - - with stack: - await stateless_event_manager.on_guild_create(shard, payload) - - stateless_event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - uuid.assert_called_once_with() - _request_guild_members.assert_called_once_with(shard, 123, include_presences=True, nonce="987.uuid") + shard.id = 123 + stateless_event_manager_impl._intents = intents.Intents.GUILD_MEMBERS + stateless_event_manager_impl._cache_enabled_for = mock.Mock(return_value=False) + stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=True) + mock_event = mock.Mock() + mock_event.guild.id = 456 + event_factory.deserialize_guild_join_event.return_value = mock_event + + mock_request_guild_members = mock.Mock() + + with mock.patch.object(asyncio, "create_task") as create_task: + with mock.patch.object(event_manager, "_fixed_size_nonce", return_value="abc"): + with mock.patch.object(event_manager, "_request_guild_members", new=mock_request_guild_members): + await stateless_event_manager_impl.on_guild_create(shard, {"large": True}) + + mock_request_guild_members.assert_called_once_with(shard, 456, include_presences=False, nonce="123.abc") create_task.assert_called_once_with( - _request_guild_members.return_value, name="987:123 guild create members request" + mock_request_guild_members.return_value, name="123:456 guild create members request" ) - - event_factory.deserialize_guild_join_event.assert_not_called() - entity_factory.deserialize_gateway_guild.assert_not_called() - - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.set_voice_state.assert_called_once_with(345) - - event_factory.deserialize_guild_join_event.assert_called_once_with(shard, payload) + assert mock_event.chunk_nonce == "123.abc" @pytest.mark.asyncio() - async def test_on_guild_create_stateless_with_unavailable_field( - self, stateless_event_manager, shard, event_factory + async def test_on_guild_update_when_stateless( + self, stateless_event_manager_impl, shard, event_factory, entity_factory ): - payload = {"unavailable": False} + stateless_event_manager_impl._intents = intents.Intents.NONE + stateless_event_manager_impl._cache_enabled_for = mock.Mock(return_value=True) + stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=False) - shard.request_guild_members = mock.AsyncMock() + await stateless_event_manager_impl.on_guild_update(shard, {}) - await stateless_event_manager.on_guild_create(shard, payload) - - event_factory.deserialize_guild_available_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( - event_factory.deserialize_guild_available_event.return_value - ) - - async def test_on_guild_create_stateless_and_dispatching( - self, stateless_event_manager, shard, event_factory, entity_factory - ): - payload = {"id": "123123"} - stateless_event_manager._enabled_for_event = mock.Mock(return_value=True) - - shard.request_guild_members = mock.AsyncMock() + stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) + event_factory.deserialize_guild_update_event.assert_not_called() - await stateless_event_manager.on_guild_create(shard, payload) - - shard.request_guild_members.assert_not_called() - stateless_event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - entity_factory.deserialize_gateway_guild.assert_not_called() - event_factory.deserialize_guild_join_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( - event_factory.deserialize_guild_join_event.return_value - ) + stateless_event_manager_impl.dispatch.assert_not_called() @pytest.mark.asyncio() - async def test_on_guild_create_stateless_and_not_dispatching( - self, stateless_event_manager, shard, event_factory, entity_factory + async def test_on_guild_update_stateful_and_dispatching( + self, event_manager_impl, shard, event_factory, entity_factory ): - payload = {"id": "123123"} - stateless_event_manager._enabled_for_event = mock.Mock(return_value=False) - - shard.request_guild_members = mock.AsyncMock() - - await stateless_event_manager.on_guild_create(shard, payload) - - shard.request_guild_members.assert_not_called() - stateless_event_manager._enabled_for_event.assert_has_calls( - [mock.call(guild_events.GuildAvailableEvent), mock.call(shard_events.MemberChunkEvent)] - ) - entity_factory.deserialize_gateway_guild.assert_not_called() - event_factory.deserialize_guild_create_event.assert_not_called() - stateless_event_manager.dispatch.assert_not_called() - - @pytest.mark.asyncio() - async def test_on_guild_update_stateful_and_dispatching(self, event_manager, shard, event_factory, entity_factory): payload = {"id": 123} old_guild = object() mock_role = object() mock_emoji = object() - event_manager._enabled_for_event = mock.Mock(return_value=True) + event_manager_impl._enabled_for_event = mock.Mock(return_value=True) event = mock.Mock(roles={555: mock_role}, emojis={333: mock_emoji}, guild=mock.Mock(id=123)) event_factory.deserialize_guild_update_event.return_value = event - event_manager._cache.get_guild.return_value = old_guild + event_manager_impl._cache.get_guild.return_value = old_guild - await event_manager.on_guild_update(shard, payload) + await event_manager_impl.on_guild_update(shard, payload) - event_manager._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) - event_manager._cache.get_guild.assert_called_once_with(123) - event_manager._cache.update_guild.assert_called_once_with(event.guild) - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(mock_role) - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(mock_emoji) + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) + event_manager_impl._cache.get_guild.assert_called_once_with(123) + event_manager_impl._cache.update_guild.assert_called_once_with(event.guild) + event_manager_impl._cache.clear_roles_for_guild.assert_called_once_with(123) + event_manager_impl._cache.set_role.assert_called_once_with(mock_role) + event_manager_impl._cache.clear_emojis_for_guild.assert_called_once_with(123) + event_manager_impl._cache.set_emoji.assert_called_once_with(mock_emoji) entity_factory.deserialize_gateway_guild.assert_not_called() event_factory.deserialize_guild_update_event.assert_called_once_with(shard, payload, old_guild=old_guild) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() async def test_on_guild_update_all_cache_components_and_not_dispatching( - self, event_manager, shard, event_factory, entity_factory + self, event_manager_impl, shard, event_factory, entity_factory ): payload = {"id": 123} mock_role = object() mock_emoji = object() - event_manager._enabled_for_event = mock.Mock(return_value=False) + event_manager_impl._enabled_for_event = mock.Mock(return_value=False) guild_definition = entity_factory.deserialize_gateway_guild.return_value guild_definition.id = 123 guild_definition.emojis.return_value = {0: mock_emoji} guild_definition.roles.return_value = {1: mock_role} - await event_manager.on_guild_update(shard, payload) + await event_manager_impl.on_guild_update(shard, payload) entity_factory.deserialize_gateway_guild.assert_called_once_with({"id": 123}) - event_manager._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) - event_manager._cache.update_guild.assert_called_once_with(guild_definition.guild.return_value) - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(mock_emoji) - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) - event_manager._cache.set_role.assert_called_once_with(mock_role) + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) + event_manager_impl._cache.update_guild.assert_called_once_with(guild_definition.guild.return_value) + event_manager_impl._cache.clear_emojis_for_guild.assert_called_once_with(123) + event_manager_impl._cache.set_emoji.assert_called_once_with(mock_emoji) + event_manager_impl._cache.clear_roles_for_guild.assert_called_once_with(123) + event_manager_impl._cache.set_role.assert_called_once_with(mock_role) event_factory.deserialize_guild_update_event.assert_not_called() - event_manager.dispatch.assert_not_called() + event_manager_impl.dispatch.assert_not_called() guild_definition.emojis.assert_called_once_with() guild_definition.roles.assert_called_once_with() guild_definition.guild.assert_called_once_with() @pytest.mark.asyncio() async def test_on_guild_update_no_cache_components_and_not_dispatching( - self, event_manager, shard, event_factory, entity_factory + self, event_manager_impl, shard, event_factory, entity_factory ): payload = {"id": 123} - event_manager._cache_enabled_for = mock.Mock(return_value=False) - event_manager._enabled_for_event = mock.Mock(return_value=False) + event_manager_impl._cache_enabled_for = mock.Mock(return_value=False) + event_manager_impl._enabled_for_event = mock.Mock(return_value=False) guild_definition = entity_factory.deserialize_gateway_guild.return_value - await event_manager.on_guild_update(shard, payload) + await event_manager_impl.on_guild_update(shard, payload) entity_factory.deserialize_gateway_guild.assert_called_once_with({"id": 123}) - event_manager._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) - event_manager._cache.update_guild.assert_not_called() - event_manager._cache.clear_emojis_for_guild.assert_not_called() - event_manager._cache.set_emoji.assert_not_called() - event_manager._cache.clear_roles_for_guild.assert_not_called() - event_manager._cache.set_role.assert_not_called() + event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) + event_manager_impl._cache.update_guild.assert_not_called() + event_manager_impl._cache.clear_emojis_for_guild.assert_not_called() + event_manager_impl._cache.set_emoji.assert_not_called() + event_manager_impl._cache.clear_roles_for_guild.assert_not_called() + event_manager_impl._cache.set_role.assert_not_called() event_factory.deserialize_guild_update_event.assert_not_called() - event_manager.dispatch.assert_not_called() + event_manager_impl.dispatch.assert_not_called() guild_definition.emojis.assert_not_called() guild_definition.roles.assert_not_called() guild_definition.guild.assert_not_called() @pytest.mark.asyncio() async def test_on_guild_update_stateless_and_dispatching( - self, stateless_event_manager, shard, event_factory, entity_factory + self, stateless_event_manager_impl, shard, event_factory, entity_factory ): payload = {"id": 123} - stateless_event_manager._enabled_for_event = mock.Mock(return_value=True) + stateless_event_manager_impl._enabled_for_event = mock.Mock(return_value=True) - await stateless_event_manager.on_guild_update(shard, payload) + await stateless_event_manager_impl.on_guild_update(shard, payload) - stateless_event_manager._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) + stateless_event_manager_impl._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) entity_factory.deserialize_gateway_guild.assert_not_called() event_factory.deserialize_guild_update_event.assert_called_once_with(shard, payload, old_guild=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_update_stateless_and_not_dispatching( - self, stateless_event_manager, shard, entity_factory, event_factory - ): - stateless_event_manager._enabled_for_event = mock.Mock(return_value=False) - - await stateless_event_manager.on_guild_update(shard, {"id": 123}) - - stateless_event_manager._enabled_for_event.assert_called_once_with(guild_events.GuildUpdateEvent) - entity_factory.deserialize_gateway_guild.assert_not_called() - event_factory.deserialize_guild_update_event.assert_not_called() - stateless_event_manager.dispatch.assert_not_called() - - @pytest.mark.asyncio() - async def test_on_guild_delete_stateful_when_available(self, event_manager, shard, event_factory): + async def test_on_guild_delete_stateful_when_available(self, event_manager_impl, shard, event_factory): payload = {"unavailable": False, "id": "123"} event = mock.Mock(guild_id=123) event_factory.deserialize_guild_leave_event.return_value = event - await event_manager.on_guild_delete(shard, payload) + await event_manager_impl.on_guild_delete(shard, payload) - event_manager._cache.delete_guild.assert_called_once_with(123) - event_manager._cache.clear_voice_states_for_guild.assert_called_once_with(123) - event_manager._cache.clear_invites_for_guild.assert_called_once_with(123) - event_manager._cache.clear_members_for_guild.assert_called_once_with(123) - event_manager._cache.clear_presences_for_guild.assert_called_once_with(123) - event_manager._cache.clear_guild_channels_for_guild.assert_called_once_with(123) - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.clear_roles_for_guild.assert_called_once_with(123) + event_manager_impl._cache.delete_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_voice_states_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_invites_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_members_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_presences_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_guild_channels_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_emojis_for_guild.assert_called_once_with(123) + event_manager_impl._cache.clear_roles_for_guild.assert_called_once_with(123) event_factory.deserialize_guild_leave_event.assert_called_once_with( - shard, payload, old_guild=event_manager._cache.delete_guild.return_value + shard, payload, old_guild=event_manager_impl._cache.delete_guild.return_value ) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_delete_stateful_when_unavailable(self, event_manager, shard, event_factory): + async def test_on_guild_delete_stateful_when_unavailable(self, event_manager_impl, shard, event_factory): payload = {"unavailable": True, "id": "123"} event = mock.Mock(guild_id=123) event_factory.deserialize_guild_unavailable_event.return_value = event - await event_manager.on_guild_delete(shard, payload) + await event_manager_impl.on_guild_delete(shard, payload) - event_manager._cache.set_guild_availability.assert_called_once_with(event.guild_id, False) + event_manager_impl._cache.set_guild_availability.assert_called_once_with(event.guild_id, False) event_factory.deserialize_guild_unavailable_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_delete_stateless_when_available(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_delete_stateless_when_available(self, stateless_event_manager_impl, shard, event_factory): payload = {"unavailable": False, "id": "123"} - await stateless_event_manager.on_guild_delete(shard, payload) + await stateless_event_manager_impl.on_guild_delete(shard, payload) event_factory.deserialize_guild_leave_event.assert_called_once_with(shard, payload, old_guild=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_leave_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_delete_stateless_when_unavailable(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_delete_stateless_when_unavailable(self, stateless_event_manager_impl, shard, event_factory): payload = {"unavailable": True} - await stateless_event_manager.on_guild_delete(shard, payload) + await stateless_event_manager_impl.on_guild_delete(shard, payload) event_factory.deserialize_guild_unavailable_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_unavailable_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_ban_add(self, event_manager, shard, event_factory): + async def test_on_guild_ban_add(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_guild_ban_add_event.return_value = event - await event_manager.on_guild_ban_add(shard, payload) + await event_manager_impl.on_guild_ban_add(shard, payload) event_factory.deserialize_guild_ban_add_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_ban_remove(self, event_manager, shard, event_factory): + async def test_on_guild_ban_remove(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_guild_ban_remove_event.return_value = event - await event_manager.on_guild_ban_remove(shard, payload) + await event_manager_impl.on_guild_ban_remove(shard, payload) event_factory.deserialize_guild_ban_remove_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_emojis_update_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_emojis_update_stateful(self, event_manager_impl, shard, event_factory): payload = {"guild_id": 123} old_emojis = {"Test": 123} mock_emoji = object() event = mock.Mock(emojis=[mock_emoji], guild_id=123) event_factory.deserialize_guild_emojis_update_event.return_value = event - event_manager._cache.clear_emojis_for_guild.return_value = old_emojis + event_manager_impl._cache.clear_emojis_for_guild.return_value = old_emojis - await event_manager.on_guild_emojis_update(shard, payload) + await event_manager_impl.on_guild_emojis_update(shard, payload) - event_manager._cache.clear_emojis_for_guild.assert_called_once_with(123) - event_manager._cache.set_emoji.assert_called_once_with(mock_emoji) + event_manager_impl._cache.clear_emojis_for_guild.assert_called_once_with(123) + event_manager_impl._cache.set_emoji.assert_called_once_with(mock_emoji) event_factory.deserialize_guild_emojis_update_event.assert_called_once_with(shard, payload, old_emojis=[123]) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_emojis_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_emojis_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"guild_id": 123} - await stateless_event_manager.on_guild_emojis_update(shard, payload) + await stateless_event_manager_impl.on_guild_emojis_update(shard, payload) event_factory.deserialize_guild_emojis_update_event.assert_called_once_with(shard, payload, old_emojis=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_emojis_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_integrations_update(self, event_manager, shard): + async def test_on_guild_integrations_update(self, event_manager_impl, shard): with pytest.raises(NotImplementedError): - await event_manager.on_guild_integrations_update(shard, {}) + await event_manager_impl.on_guild_integrations_update(shard, {}) - event_manager.dispatch.assert_not_called() + event_manager_impl.dispatch.assert_not_called() @pytest.mark.asyncio() - async def test_on_integration_create(self, event_manager, shard, event_factory): + async def test_on_integration_create(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_integration_create_event.return_value = event - await event_manager.on_integration_create(shard, payload) + await event_manager_impl.on_integration_create(shard, payload) event_factory.deserialize_integration_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_integration_delete(self, event_manager, shard, event_factory): + async def test_on_integration_delete(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_integration_delete_event.return_value = event - await event_manager.on_integration_delete(shard, payload) + await event_manager_impl.on_integration_delete(shard, payload) event_factory.deserialize_integration_delete_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_integration_update(self, event_manager, shard, event_factory): + async def test_on_integration_update(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_integration_update_event.return_value = event - await event_manager.on_integration_update(shard, payload) + await event_manager_impl.on_integration_update(shard, payload) event_factory.deserialize_integration_update_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_member_add_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_member_add_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(user=object(), member=object()) event_factory.deserialize_guild_member_add_event.return_value = event - await event_manager.on_guild_member_add(shard, payload) + await event_manager_impl.on_guild_member_add(shard, payload) - event_manager._cache.update_member.assert_called_once_with(event.member) + event_manager_impl._cache.update_member.assert_called_once_with(event.member) event_factory.deserialize_guild_member_add_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_member_add_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_member_add_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_guild_member_add(shard, payload) + await stateless_event_manager_impl.on_guild_member_add(shard, payload) event_factory.deserialize_guild_member_add_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_member_add_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_member_remove_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_member_remove_stateful(self, event_manager_impl, shard, event_factory): payload = {"guild_id": "456", "user": {"id": "123"}} - await event_manager.on_guild_member_remove(shard, payload) + await event_manager_impl.on_guild_member_remove(shard, payload) - event_manager._cache.delete_member.assert_called_once_with(456, 123) + event_manager_impl._cache.delete_member.assert_called_once_with(456, 123) event_factory.deserialize_guild_member_remove_event.assert_called_once_with( - shard, payload, old_member=event_manager._cache.delete_member.return_value + shard, payload, old_member=event_manager_impl._cache.delete_member.return_value ) - event_manager.dispatch.assert_awaited_once_with( + event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_member_remove_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_member_remove_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_member_remove_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_guild_member_remove(shard, payload) + await stateless_event_manager_impl.on_guild_member_remove(shard, payload) event_factory.deserialize_guild_member_remove_event.assert_called_once_with(shard, payload, old_member=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_member_remove_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_member_update_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_member_update_stateful(self, event_manager_impl, shard, event_factory): payload = {"user": {"id": 123}, "guild_id": 456} old_member = object() event = mock.Mock(member=mock.Mock()) event_factory.deserialize_guild_member_update_event.return_value = event - event_manager._cache.get_member.return_value = old_member + event_manager_impl._cache.get_member.return_value = old_member - await event_manager.on_guild_member_update(shard, payload) + await event_manager_impl.on_guild_member_update(shard, payload) - event_manager._cache.get_member.assert_called_once_with(456, 123) - event_manager._cache.update_member.assert_called_once_with(event.member) + event_manager_impl._cache.get_member.assert_called_once_with(456, 123) + event_manager_impl._cache.update_member.assert_called_once_with(event.member) event_factory.deserialize_guild_member_update_event.assert_called_once_with( shard, payload, old_member=old_member ) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_member_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_member_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"user": {"id": 123}, "guild_id": 456} - await stateless_event_manager.on_guild_member_update(shard, payload) + await stateless_event_manager_impl.on_guild_member_update(shard, payload) event_factory.deserialize_guild_member_update_event.assert_called_once_with(shard, payload, old_member=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_member_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_members_chunk_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_members_chunk_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(members={"TestMember": 123}, presences={"TestPresences": 456}) event_factory.deserialize_guild_member_chunk_event.return_value = event - await event_manager.on_guild_members_chunk(shard, payload) + await event_manager_impl.on_guild_members_chunk(shard, payload) - event_manager._cache.set_member.assert_called_once_with(123) - event_manager._cache.set_presence.assert_called_once_with(456) + event_manager_impl._cache.set_member.assert_called_once_with(123) + event_manager_impl._cache.set_presence.assert_called_once_with(456) event_factory.deserialize_guild_member_chunk_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_members_chunk_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_members_chunk_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_guild_members_chunk(shard, payload) + await stateless_event_manager_impl.on_guild_members_chunk(shard, payload) event_factory.deserialize_guild_member_chunk_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_member_chunk_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_role_create_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_role_create_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(role=object()) event_factory.deserialize_guild_role_create_event.return_value = event - await event_manager.on_guild_role_create(shard, payload) + await event_manager_impl.on_guild_role_create(shard, payload) - event_manager._cache.set_role.assert_called_once_with(event.role) + event_manager_impl._cache.set_role.assert_called_once_with(event.role) event_factory.deserialize_guild_role_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_role_create_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_role_create_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_guild_role_create(shard, payload) + await stateless_event_manager_impl.on_guild_role_create(shard, payload) event_factory.deserialize_guild_role_create_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_role_create_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_role_update_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_role_update_stateful(self, event_manager_impl, shard, event_factory): payload = {"role": {"id": 123}} old_role = object() event = mock.Mock(role=mock.Mock()) event_factory.deserialize_guild_role_update_event.return_value = event - event_manager._cache.get_role.return_value = old_role + event_manager_impl._cache.get_role.return_value = old_role - await event_manager.on_guild_role_update(shard, payload) + await event_manager_impl.on_guild_role_update(shard, payload) - event_manager._cache.get_role.assert_called_once_with(123) - event_manager._cache.update_role.assert_called_once_with(event.role) + event_manager_impl._cache.get_role.assert_called_once_with(123) + event_manager_impl._cache.update_role.assert_called_once_with(event.role) event_factory.deserialize_guild_role_update_event.assert_called_once_with(shard, payload, old_role=old_role) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_guild_role_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_role_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"role": {"id": 123}} - await stateless_event_manager.on_guild_role_update(shard, payload) + await stateless_event_manager_impl.on_guild_role_update(shard, payload) event_factory.deserialize_guild_role_update_event.assert_called_once_with(shard, payload, old_role=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_role_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_guild_role_delete_stateful(self, event_manager, shard, event_factory): + async def test_on_guild_role_delete_stateful(self, event_manager_impl, shard, event_factory): payload = {"role_id": "123"} - await event_manager.on_guild_role_delete(shard, payload) + await event_manager_impl.on_guild_role_delete(shard, payload) - event_manager._cache.delete_role.assert_called_once_with(123) + event_manager_impl._cache.delete_role.assert_called_once_with(123) event_factory.deserialize_guild_role_delete_event.assert_called_once_with( - shard, payload, old_role=event_manager._cache.delete_role.return_value + shard, payload, old_role=event_manager_impl._cache.delete_role.return_value + ) + event_manager_impl.dispatch.assert_awaited_once_with( + event_factory.deserialize_guild_role_delete_event.return_value ) - event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_guild_role_delete_event.return_value) @pytest.mark.asyncio() - async def test_on_guild_role_delete_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_guild_role_delete_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_guild_role_delete(shard, payload) + await stateless_event_manager_impl.on_guild_role_delete(shard, payload) event_factory.deserialize_guild_role_delete_event.assert_called_once_with(shard, payload, old_role=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_role_delete_event.return_value ) @pytest.mark.asyncio() - async def test_on_invite_create_stateful(self, event_manager, shard, event_factory): + async def test_on_invite_create_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(invite="qwerty") event_factory.deserialize_invite_create_event.return_value = event - await event_manager.on_invite_create(shard, payload) + await event_manager_impl.on_invite_create(shard, payload) - event_manager._cache.set_invite.assert_called_once_with("qwerty") + event_manager_impl._cache.set_invite.assert_called_once_with("qwerty") event_factory.deserialize_invite_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_invite_create_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_invite_create_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_invite_create(shard, payload) + await stateless_event_manager_impl.on_invite_create(shard, payload) event_factory.deserialize_invite_create_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_invite_create_event.return_value ) @pytest.mark.asyncio() - async def test_on_invite_delete_stateful(self, event_manager, shard, event_factory): + async def test_on_invite_delete_stateful(self, event_manager_impl, shard, event_factory): payload = {"code": "qwerty"} - await event_manager.on_invite_delete(shard, payload) + await event_manager_impl.on_invite_delete(shard, payload) - event_manager._cache.delete_invite.assert_called_once_with("qwerty") + event_manager_impl._cache.delete_invite.assert_called_once_with("qwerty") event_factory.deserialize_invite_delete_event.assert_called_once_with( - shard, payload, old_invite=event_manager._cache.delete_invite.return_value + shard, payload, old_invite=event_manager_impl._cache.delete_invite.return_value ) - event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_invite_delete_event.return_value) + event_manager_impl.dispatch.assert_awaited_once_with(event_factory.deserialize_invite_delete_event.return_value) @pytest.mark.asyncio() - async def test_on_invite_delete_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_invite_delete_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_invite_delete(shard, payload) + await stateless_event_manager_impl.on_invite_delete(shard, payload) event_factory.deserialize_invite_delete_event.assert_called_once_with(shard, payload, old_invite=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_invite_delete_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_create_stateful(self, event_manager, shard, event_factory): + async def test_on_message_create_stateful(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock(message=object()) event_factory.deserialize_message_create_event.return_value = event - await event_manager.on_message_create(shard, payload) + await event_manager_impl.on_message_create(shard, payload) - event_manager._cache.set_message.assert_called_once_with(event.message) + event_manager_impl._cache.set_message.assert_called_once_with(event.message) event_factory.deserialize_message_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_message_create_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_message_create_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_message_create(shard, payload) + await stateless_event_manager_impl.on_message_create(shard, payload) event_factory.deserialize_message_create_event.assert_called_once_with(shard, payload) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_message_create_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_update_stateful(self, event_manager, shard, event_factory): + async def test_on_message_update_stateful(self, event_manager_impl, shard, event_factory): payload = {"id": 123} old_message = object() event = mock.Mock(message=mock.Mock()) event_factory.deserialize_message_update_event.return_value = event - event_manager._cache.get_message.return_value = old_message + event_manager_impl._cache.get_message.return_value = old_message - await event_manager.on_message_update(shard, payload) + await event_manager_impl.on_message_update(shard, payload) - event_manager._cache.get_message.assert_called_once_with(123) - event_manager._cache.update_message.assert_called_once_with(event.message) + event_manager_impl._cache.get_message.assert_called_once_with(123) + event_manager_impl._cache.update_message.assert_called_once_with(event.message) event_factory.deserialize_message_update_event.assert_called_once_with(shard, payload, old_message=old_message) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_message_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_message_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"id": 123} - await stateless_event_manager.on_message_update(shard, payload) + await stateless_event_manager_impl.on_message_update(shard, payload) event_factory.deserialize_message_update_event.assert_called_once_with(shard, payload, old_message=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_message_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_delete_stateful(self, event_manager, shard, event_factory): + async def test_on_message_delete_stateful(self, event_manager_impl, shard, event_factory): payload = {"id": 123} - await event_manager.on_message_delete(shard, payload) + await event_manager_impl.on_message_delete(shard, payload) - event_manager._cache.delete_message.assert_called_once_with(123) + event_manager_impl._cache.delete_message.assert_called_once_with(123) event_factory.deserialize_message_delete_event.assert_called_once_with( - shard, payload, old_message=event_manager._cache.delete_message.return_value + shard, payload, old_message=event_manager_impl._cache.delete_message.return_value + ) + event_manager_impl.dispatch.assert_awaited_once_with( + event_factory.deserialize_message_delete_event.return_value ) - event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_message_delete_event.return_value) @pytest.mark.asyncio() - async def test_on_message_delete_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_message_delete_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_message_delete(shard, payload) + await stateless_event_manager_impl.on_message_delete(shard, payload) event_factory.deserialize_message_delete_event.assert_called_once_with(shard, payload, old_message=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_message_delete_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_delete_bulk_stateful(self, event_manager, shard, event_factory): + async def test_on_message_delete_bulk_stateful(self, event_manager_impl, shard, event_factory): payload = {"ids": [123, 456, 789]} message1 = object() message2 = object() message3 = object() - event_manager._cache.delete_message.side_effect = [message1, message2, message3] + event_manager_impl._cache.delete_message.side_effect = [message1, message2, message3] - await event_manager.on_message_delete_bulk(shard, payload) + await event_manager_impl.on_message_delete_bulk(shard, payload) - event_manager._cache.delete_message.assert_has_calls([mock.call(123), mock.call(456), mock.call(789)]) + event_manager_impl._cache.delete_message.assert_has_calls([mock.call(123), mock.call(456), mock.call(789)]) event_factory.deserialize_guild_message_delete_bulk_event.assert_called_once_with( shard, payload, old_messages={123: message1, 456: message2, 789: message3} ) - event_manager.dispatch.assert_awaited_once_with( + event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_message_delete_bulk_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_delete_bulk_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_message_delete_bulk_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_message_delete_bulk(shard, payload) + await stateless_event_manager_impl.on_message_delete_bulk(shard, payload) event_factory.deserialize_guild_message_delete_bulk_event.assert_called_once_with( shard, payload, old_messages={} ) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_guild_message_delete_bulk_event.return_value ) @pytest.mark.asyncio() - async def test_on_message_reaction_add(self, event_manager, shard, event_factory): + async def test_on_message_reaction_add(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_message_reaction_add_event.return_value = event - await event_manager.on_message_reaction_add(shard, payload) + await event_manager_impl.on_message_reaction_add(shard, payload) event_factory.deserialize_message_reaction_add_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_message_reaction_remove(self, event_manager, shard, event_factory): + async def test_on_message_reaction_remove(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_message_reaction_remove_event.return_value = event - await event_manager.on_message_reaction_remove(shard, payload) + await event_manager_impl.on_message_reaction_remove(shard, payload) event_factory.deserialize_message_reaction_remove_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_message_reaction_remove_all(self, event_manager, shard, event_factory): + async def test_on_message_reaction_remove_all(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_message_reaction_remove_all_event.return_value = event - await event_manager.on_message_reaction_remove_all(shard, payload) + await event_manager_impl.on_message_reaction_remove_all(shard, payload) event_factory.deserialize_message_reaction_remove_all_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_message_reaction_remove_emoji(self, event_manager, shard, event_factory): + async def test_on_message_reaction_remove_emoji(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_message_reaction_remove_emoji_event.return_value = event - await event_manager.on_message_reaction_remove_emoji(shard, payload) + await event_manager_impl.on_message_reaction_remove_emoji(shard, payload) event_factory.deserialize_message_reaction_remove_emoji_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_presence_update_stateful_update(self, event_manager, shard, event_factory): + async def test_on_presence_update_stateful_update(self, event_manager_impl, shard, event_factory): payload = {"user": {"id": 123}, "guild_id": 456} old_presence = object() event = mock.Mock(presence=mock.Mock(visible_status=presences.Status.ONLINE)) event_factory.deserialize_presence_update_event.return_value = event - event_manager._cache.get_presence.return_value = old_presence + event_manager_impl._cache.get_presence.return_value = old_presence - await event_manager.on_presence_update(shard, payload) + await event_manager_impl.on_presence_update(shard, payload) - event_manager._cache.get_presence.assert_called_once_with(456, 123) - event_manager._cache.update_presence.assert_called_once_with(event.presence) + event_manager_impl._cache.get_presence.assert_called_once_with(456, 123) + event_manager_impl._cache.update_presence.assert_called_once_with(event.presence) event_factory.deserialize_presence_update_event.assert_called_once_with( shard, payload, old_presence=old_presence ) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_presence_update_stateful_delete(self, event_manager, shard, event_factory): + async def test_on_presence_update_stateful_delete(self, event_manager_impl, shard, event_factory): payload = {"user": {"id": 123}, "guild_id": 456} old_presence = object() event = mock.Mock(presence=mock.Mock(visible_status=presences.Status.OFFLINE)) event_factory.deserialize_presence_update_event.return_value = event - event_manager._cache.get_presence.return_value = old_presence + event_manager_impl._cache.get_presence.return_value = old_presence - await event_manager.on_presence_update(shard, payload) + await event_manager_impl.on_presence_update(shard, payload) - event_manager._cache.get_presence.assert_called_once_with(456, 123) - event_manager._cache.delete_presence.assert_called_once_with(event.presence.guild_id, event.presence.user_id) + event_manager_impl._cache.get_presence.assert_called_once_with(456, 123) + event_manager_impl._cache.delete_presence.assert_called_once_with( + event.presence.guild_id, event.presence.user_id + ) event_factory.deserialize_presence_update_event.assert_called_once_with( shard, payload, old_presence=old_presence ) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_presence_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_presence_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"user": {"id": 123}, "guild_id": 456} - await stateless_event_manager.on_presence_update(shard, payload) + await stateless_event_manager_impl.on_presence_update(shard, payload) event_factory.deserialize_presence_update_event.assert_called_once_with(shard, payload, old_presence=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_presence_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_typing_start(self, event_manager, shard, event_factory): + async def test_on_typing_start(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_typing_start_event.return_value = event - await event_manager.on_typing_start(shard, payload) + await event_manager_impl.on_typing_start(shard, payload) event_factory.deserialize_typing_start_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_user_update_stateful(self, event_manager, shard, event_factory): + async def test_on_user_update_stateful(self, event_manager_impl, shard, event_factory): payload = {} old_user = object() event = mock.Mock(user=mock.Mock()) event_factory.deserialize_own_user_update_event.return_value = event - event_manager._cache.get_me.return_value = old_user + event_manager_impl._cache.get_me.return_value = old_user - await event_manager.on_user_update(shard, payload) + await event_manager_impl.on_user_update(shard, payload) - event_manager._cache.update_me.assert_called_once_with(event.user) + event_manager_impl._cache.update_me.assert_called_once_with(event.user) event_factory.deserialize_own_user_update_event.assert_called_once_with(shard, payload, old_user=old_user) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_user_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_user_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {} - await stateless_event_manager.on_user_update(shard, payload) + await stateless_event_manager_impl.on_user_update(shard, payload) event_factory.deserialize_own_user_update_event.assert_called_once_with(shard, payload, old_user=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_own_user_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_voice_state_update_stateful_update(self, event_manager, shard, event_factory): + async def test_on_voice_state_update_stateful_update(self, event_manager_impl, shard, event_factory): payload = {"user_id": 123, "guild_id": 456} old_state = object() event = mock.Mock(state=mock.Mock(channel_id=123)) event_factory.deserialize_voice_state_update_event.return_value = event - event_manager._cache.get_voice_state.return_value = old_state + event_manager_impl._cache.get_voice_state.return_value = old_state - await event_manager.on_voice_state_update(shard, payload) + await event_manager_impl.on_voice_state_update(shard, payload) - event_manager._cache.get_voice_state.assert_called_once_with(456, 123) - event_manager._cache.update_voice_state.assert_called_once_with(event.state) + event_manager_impl._cache.get_voice_state.assert_called_once_with(456, 123) + event_manager_impl._cache.update_voice_state.assert_called_once_with(event.state) event_factory.deserialize_voice_state_update_event.assert_called_once_with(shard, payload, old_state=old_state) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_voice_state_update_stateful_delete(self, event_manager, shard, event_factory): + async def test_on_voice_state_update_stateful_delete(self, event_manager_impl, shard, event_factory): payload = {"user_id": 123, "guild_id": 456} old_state = object() event = mock.Mock(state=mock.Mock(channel_id=None)) event_factory.deserialize_voice_state_update_event.return_value = event - event_manager._cache.get_voice_state.return_value = old_state + event_manager_impl._cache.get_voice_state.return_value = old_state - await event_manager.on_voice_state_update(shard, payload) + await event_manager_impl.on_voice_state_update(shard, payload) - event_manager._cache.get_voice_state.assert_called_once_with(456, 123) - event_manager._cache.delete_voice_state.assert_called_once_with(event.state.guild_id, event.state.user_id) + event_manager_impl._cache.get_voice_state.assert_called_once_with(456, 123) + event_manager_impl._cache.delete_voice_state.assert_called_once_with(event.state.guild_id, event.state.user_id) event_factory.deserialize_voice_state_update_event.assert_called_once_with(shard, payload, old_state=old_state) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_voice_state_update_stateless(self, stateless_event_manager, shard, event_factory): + async def test_on_voice_state_update_stateless(self, stateless_event_manager_impl, shard, event_factory): payload = {"user_id": 123, "guild_id": 456} - await stateless_event_manager.on_voice_state_update(shard, payload) + await stateless_event_manager_impl.on_voice_state_update(shard, payload) event_factory.deserialize_voice_state_update_event.assert_called_once_with(shard, payload, old_state=None) - stateless_event_manager.dispatch.assert_awaited_once_with( + stateless_event_manager_impl.dispatch.assert_awaited_once_with( event_factory.deserialize_voice_state_update_event.return_value ) @pytest.mark.asyncio() - async def test_on_voice_server_update(self, event_manager, shard, event_factory): + async def test_on_voice_server_update(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_voice_server_update_event.return_value = event - await event_manager.on_voice_server_update(shard, payload) + await event_manager_impl.on_voice_server_update(shard, payload) event_factory.deserialize_voice_server_update_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_webhooks_update(self, event_manager, shard, event_factory): + async def test_on_webhooks_update(self, event_manager_impl, shard, event_factory): payload = {} event = mock.Mock() event_factory.deserialize_webhook_update_event.return_value = event - await event_manager.on_webhooks_update(shard, payload) + await event_manager_impl.on_webhooks_update(shard, payload) event_factory.deserialize_webhook_update_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event) + event_manager_impl.dispatch.assert_awaited_once_with(event) @pytest.mark.asyncio() - async def test_on_interaction_create(self, event_manager, shard, event_factory): + async def test_on_interaction_create(self, event_manager_impl, shard, event_factory): payload = {"id": "123"} - await event_manager.on_interaction_create(shard, payload) + await event_manager_impl.on_interaction_create(shard, payload) event_factory.deserialize_interaction_create_event.assert_called_once_with(shard, payload) - event_manager.dispatch.assert_awaited_once_with(event_factory.deserialize_interaction_create_event.return_value) + event_manager_impl.dispatch.assert_awaited_once_with( + event_factory.deserialize_interaction_create_event.return_value + ) diff --git a/tests/hikari/impl/test_event_manager_base.py b/tests/hikari/impl/test_event_manager_base.py index 361b8aef99..6c5b6b62da 100644 --- a/tests/hikari/impl/test_event_manager_base.py +++ b/tests/hikari/impl/test_event_manager_base.py @@ -33,7 +33,6 @@ from hikari import errors from hikari import intents from hikari import iterators -from hikari import undefined from hikari.events import base_events from hikari.events import member_events from hikari.events import shard_events @@ -387,8 +386,22 @@ def test_open_for_active_stream(self): stream._active = False -def test__default_predicate_returns_True(): - assert event_manager_base._default_predicate(None) is True +class TestConsumer: + @pytest.mark.parametrize( + ("is_caching", "listener_group_count", "waiter_group_count", "expected_result"), + [ + (True, -10000, -10000, True), + (False, 0, 1, True), + (False, 1, 0, True), + (False, 0, 0, False), + ], + ) + def test_is_enabled(self, is_caching, listener_group_count, waiter_group_count, expected_result): + consumer = event_manager_base._Consumer(object(), 123, is_caching) + consumer.listener_group_count = listener_group_count + consumer.waiter_group_count = waiter_group_count + + assert consumer.is_enabled is expected_result class TestEventManagerBase: @@ -419,23 +432,14 @@ async def on_not_decorated(self, event): async def not_a_listener(self): raise NotImplementedError - expected_bar_events = ( - shard_events.ShardStateEvent, - shard_events.ShardEvent, - base_events.Event, - shard_events.ShardPayloadEvent, - ) - expected_bat_events = (shard_events.MemberChunkEvent, shard_events.ShardEvent, base_events.Event) manager = StubManager( - mock.Mock(), - 0, - cache_components=config.CacheComponents.MEMBERS | config.CacheComponents.GUILD_CHANNELS, + mock.Mock(), 0, cache_components=config.CacheComponents.MEMBERS | config.CacheComponents.GUILD_CHANNELS ) assert manager._consumers == { - "foo": event_manager_base._Consumer(manager.on_foo, (shard_events.ShardEvent, base_events.Event), True), - "bar": event_manager_base._Consumer(manager.on_bar, expected_bar_events, False), - "bat": event_manager_base._Consumer(manager.on_bat, expected_bat_events, False), - "not_decorated": event_manager_base._Consumer(manager.on_not_decorated, undefined.UNDEFINED, True), + "foo": event_manager_base._Consumer(manager.on_foo, 9, True), + "bar": event_manager_base._Consumer(manager.on_bar, 105, False), + "bat": event_manager_base._Consumer(manager.on_bat, 65545, False), + "not_decorated": event_manager_base._Consumer(manager.on_not_decorated, -1, True), } def test___init___loads_consumers_when_cacheless(self): @@ -458,100 +462,56 @@ async def on_not_decorated(self, event): async def not_a_listener(self): raise NotImplementedError - expected_bar_events = ( - shard_events.ShardStateEvent, - shard_events.ShardEvent, - base_events.Event, - shard_events.ShardPayloadEvent, - ) - expected_bat_events = (shard_events.MemberChunkEvent, shard_events.ShardEvent, base_events.Event) manager = StubManager(mock.Mock(), 0, cache_components=config.CacheComponents.NONE) assert manager._consumers == { - "foo": event_manager_base._Consumer(manager.on_foo, (shard_events.ShardEvent, base_events.Event), False), - "bar": event_manager_base._Consumer(manager.on_bar, expected_bar_events, False), - "bat": event_manager_base._Consumer(manager.on_bat, expected_bat_events, False), - "not_decorated": event_manager_base._Consumer(manager.on_not_decorated, undefined.UNDEFINED, False), + "foo": event_manager_base._Consumer(manager.on_foo, 9, False), + "bar": event_manager_base._Consumer(manager.on_bar, 105, False), + "bat": event_manager_base._Consumer(manager.on_bat, 65545, False), + "not_decorated": event_manager_base._Consumer(manager.on_not_decorated, -1, False), } - def test__clear_enabled_cache(self): - event_manager = hikari_test_helpers.mock_class_namespace(event_manager_base.EventManagerBase, init_=False)() - event_manager._enabled_consumers_cache = {object: object(), "ok": object()} + def test__increment_listener_group_count(self, event_manager): + on_foo_consumer = event_manager_base._Consumer(None, 9, False) + on_bar_consumer = event_manager_base._Consumer(None, 105, False) + on_bat_consumer = event_manager_base._Consumer(None, 1, False) + event_manager._consumers = {"foo": on_foo_consumer, "bar": on_bar_consumer, "bat": on_bat_consumer} + + event_manager._increment_listener_group_count(shard_events.ShardEvent, 1) + + assert on_foo_consumer.listener_group_count == 1 + assert on_bar_consumer.listener_group_count == 1 + assert on_bat_consumer.listener_group_count == 0 - event_manager._clear_enabled_cache() + def test__increment_waiter_group_count(self, event_manager): + on_foo_consumer = event_manager_base._Consumer(None, 9, False) + on_bar_consumer = event_manager_base._Consumer(None, 105, False) + on_bat_consumer = event_manager_base._Consumer(None, 1, False) + event_manager._consumers = {"foo": on_foo_consumer, "bar": on_bar_consumer, "bat": on_bat_consumer} - assert event_manager._enabled_consumers_cache == {} + event_manager._increment_waiter_group_count(shard_events.ShardEvent, 1) + + assert on_foo_consumer.waiter_group_count == 1 + assert on_bar_consumer.waiter_group_count == 1 + assert on_bat_consumer.waiter_group_count == 0 def test__enabled_for_event_when_listener_registered(self, event_manager): - event_manager._listeners = {} + event_manager._listeners = {shard_events.ShardStateEvent: [], shard_events.MemberChunkEvent: []} + event_manager._waiters = {} + + assert event_manager._enabled_for_event(shard_events.ShardStateEvent) is True def test__enabled_for_event_when_waiter_registered(self, event_manager): event_manager._listeners = {} + event_manager._waiters = {shard_events.ShardStateEvent: [], shard_events.MemberChunkEvent: []} + + assert event_manager._enabled_for_event(shard_events.ShardStateEvent) is True def test__enabled_for_event_when_not_registered(self, event_manager): event_manager._listeners = {shard_events.ShardPayloadEvent: [], shard_events.MemberChunkEvent: []} + event_manager._waiters = {shard_events.ShardPayloadEvent: [], shard_events.MemberChunkEvent: []} assert event_manager._enabled_for_event(shard_events.ShardStateEvent) is False - def test__enabled_for_consumer_when_event_types_is_undefined(self, event_manager): - consumer = mock.Mock(event_types=undefined.UNDEFINED) - - assert event_manager._enabled_for_consumer(consumer) is True - - def test__enabled_for_consumer_when_caching(self, event_manager): - consumer = mock.Mock(event_types=(), is_caching=True) - - assert event_manager._enabled_for_consumer(consumer) is True - - @pytest.mark.parametrize("cached_state", [False, True]) - def test__enabled_for_consumer_when_consumer_state_cached(self, event_manager, cached_state): - consumer = mock.Mock(event_types=(), is_caching=False) - event_manager._enabled_consumers_cache[consumer] = cached_state - - assert event_manager._enabled_for_consumer(consumer) is cached_state - - def test__enabled_for_consumer_when_consumer_state_not_cached_and_listeners_present(self, event_manager): - event_manager._listeners[shard_events.MemberChunkEvent] = [] - consumer = mock.Mock( - event_types=(shard_events.ShardEvent, shard_events.ShardPayloadEvent, shard_events.MemberChunkEvent), - is_caching=False, - ) - - result = event_manager._enabled_for_consumer(consumer) - - assert result is True - assert event_manager._enabled_consumers_cache[consumer] is True - - def test__enabled_for_consumer_when_consumer_state_not_cached_and_waiters_present(self, event_manager): - event_manager._waiters[shard_events.MemberChunkEvent] = [] - consumer = mock.Mock( - event_types=(shard_events.ShardEvent, shard_events.ShardPayloadEvent, shard_events.MemberChunkEvent), - is_caching=False, - ) - - result = event_manager._enabled_for_consumer(consumer) - - assert result is True - assert event_manager._enabled_consumers_cache[consumer] is True - - def test__enabled_for_consumer_when_consumer_state_not_cached_and_not_enabled(self, event_manager): - consumer = mock.Mock( - event_types=(shard_events.ShardEvent, shard_events.ShardPayloadEvent, shard_events.MemberChunkEvent), - is_caching=False, - ) - - result = event_manager._enabled_for_consumer(consumer) - - assert result is False - assert event_manager._enabled_consumers_cache[consumer] is False - - def test__enabled_for_consumer_when_consumer_state_not_cached_and_no_event_types(self, event_manager): - consumer = mock.Mock(event_types=(), is_caching=False) - - result = event_manager._enabled_for_consumer(consumer) - - assert result is False - assert event_manager._enabled_consumers_cache[consumer] is False - @pytest.mark.asyncio() async def test_consume_raw_event_when_KeyError(self, event_manager): event_manager._enabled_for_event = mock.Mock(return_value=True) @@ -671,8 +631,7 @@ async def test_handle_dispatch_handles_exceptions(self, event_manager, event_loo @pytest.mark.asyncio() async def test_handle_dispatch_invokes_when_consumer_not_enabled(self, event_manager, event_loop): - event_manager._enabled_for_consumer = mock.Mock(return_value=False) - consumer = mock.Mock(callback=mock.AsyncMock(__name__="ok")) + consumer = mock.Mock(callback=mock.AsyncMock(__name__="ok"), is_enabled=False) error_handler = mock.MagicMock() event_loop.set_exception_handler(error_handler) shard = object() @@ -687,18 +646,19 @@ def test_subscribe_when_callback_is_not_coroutine(self, event_manager): def test(): ... - with pytest.raises(TypeError): + with pytest.raises(TypeError, match=r"Cannot subscribe a non-coroutine function callback"): event_manager.subscribe(member_events.MemberCreateEvent, test) - def test_subscribe_when_event_type_does_not_subclass_Event(self, event_manager): + @pytest.mark.parametrize("obj", ["test", event_manager_base.EventManagerBase]) + def test_subscribe_when_event_type_does_not_subclass_Event(self, event_manager, obj): async def test(): ... - with pytest.raises(TypeError): - event_manager.subscribe("test", test) + with pytest.raises(TypeError, match=r"Cannot subscribe to a non-Event type"): + event_manager.subscribe(obj, test) def test_subscribe_when_event_type_not_in_listeners(self, event_manager): - event_manager._clear_enabled_cache = mock.Mock() + event_manager._increment_listener_group_count = mock.Mock() async def test(): ... @@ -708,7 +668,7 @@ async def test(): assert event_manager._listeners == {member_events.MemberCreateEvent: [test]} check.assert_called_once_with(member_events.MemberCreateEvent, 1) - event_manager._clear_enabled_cache.assert_called_once_with() + event_manager._increment_listener_group_count.assert_called_once_with(member_events.MemberCreateEvent, 1) def test_subscribe_when_event_type_in_listeners(self, event_manager): async def test(): @@ -717,7 +677,7 @@ async def test(): async def test2(): ... - event_manager._clear_enabled_cache = mock.Mock() + event_manager._increment_listener_group_count = mock.Mock() event_manager._listeners[member_events.MemberCreateEvent] = [test2] with mock.patch.object(event_manager_base.EventManagerBase, "_check_intents") as check: @@ -725,7 +685,7 @@ async def test2(): assert event_manager._listeners == {member_events.MemberCreateEvent: [test2, test]} check.assert_called_once_with(member_events.MemberCreateEvent, 2) - event_manager._clear_enabled_cache.assert_not_called() + event_manager._increment_listener_group_count.assert_not_called() def test__check_intents_when_no_intents_required(self, event_manager): event_manager._intents = intents.Intents.ALL @@ -767,25 +727,20 @@ def test__check_intents_when_intents_incorrect(self, event_manager): ) def test_get_listeners_when_not_event(self, event_manager): - assert len(event_manager.get_listeners("test")) == 0 + event_manager._listeners = {} + + assert event_manager.get_listeners(base_events.Event) == [] def test_get_listeners_polymorphic(self, event_manager): event_manager._listeners = { - base_events.Event: ["this will never appear"], - member_events.MemberEvent: ["coroutine0"], - member_events.MemberCreateEvent: ["coroutine1", "coroutine2"], - member_events.MemberUpdateEvent: ["coroutine3"], - member_events.MemberDeleteEvent: ["coroutine4", "coroutine5"], + base_events.Event: ["coroutine0"], + member_events.MemberEvent: ["coroutine1"], + member_events.MemberCreateEvent: ["hi", "i am"], + member_events.MemberUpdateEvent: ["hidden"], + base_events.ExceptionEvent: ["so you won't see me"], } - assert event_manager.get_listeners(member_events.MemberEvent) == [ - "coroutine0", - "coroutine1", - "coroutine2", - "coroutine3", - "coroutine4", - "coroutine5", - ] + assert event_manager.get_listeners(member_events.MemberEvent) == ["coroutine1", "coroutine0"] def test_get_listeners_monomorphic_and_no_results(self, event_manager): event_manager._listeners = { @@ -810,11 +765,13 @@ def test_unsubscribe_when_event_type_not_in_listeners(self, event_manager): async def test(): ... + event_manager._increment_listener_group_count = mock.Mock() event_manager._listeners = {} event_manager.unsubscribe(member_events.MemberCreateEvent, test) assert event_manager._listeners == {} + event_manager._increment_listener_group_count.assert_not_called() def test_unsubscribe_when_event_type_when_list_not_empty_after_delete(self, event_manager): async def test(): @@ -823,7 +780,7 @@ async def test(): async def test2(): ... - event_manager._clear_enabled_cache = mock.Mock() + event_manager._increment_listener_group_count = mock.Mock() event_manager._listeners = { member_events.MemberCreateEvent: [test, test2], member_events.MemberDeleteEvent: [test], @@ -835,19 +792,19 @@ async def test2(): member_events.MemberCreateEvent: [test2], member_events.MemberDeleteEvent: [test], } - event_manager._clear_enabled_cache.assert_not_called() + event_manager._increment_listener_group_count.assert_not_called() def test_unsubscribe_when_event_type_when_list_empty_after_delete(self, event_manager): async def test(): ... - event_manager._clear_enabled_cache = mock.Mock() + event_manager._increment_listener_group_count = mock.Mock() event_manager._listeners = {member_events.MemberCreateEvent: [test], member_events.MemberDeleteEvent: [test]} event_manager.unsubscribe(member_events.MemberCreateEvent, test) assert event_manager._listeners == {member_events.MemberDeleteEvent: [test]} - event_manager._clear_enabled_cache.assert_called_once_with() + event_manager._increment_listener_group_count.assert_called_once_with(member_events.MemberCreateEvent, -1) def test_listen_when_no_params(self, event_manager): with pytest.raises(TypeError):