Repository: hikari-py/hikari
Pull request #402 (2020-12-19 09:56:01)

Add more testing
  - Tidyups

### Checklist
<!-- Make sure to tick all the following boxes by putting an `x` in between (like this `[x]`) -->
- [x] I have run `nox` and all the pipelines have passed.
- [x] I have made unittests according to the code I have added/modified/deleted.

### Related issues
#47 



Patch:
diff --git a/hikari/api/voice.py b/hikari/api/voice.py
index 765a3f5e..403b3cbb 100644
--- a/hikari/api/voice.py
+++ b/hikari/api/voice.py
@@ -67,32 +67,32 @@ async def disconnect(self) -> None:
     @abc.abstractmethod
     async def connect_to(
         self,
-        channel: snowflakes.SnowflakeishOr[channels.GuildVoiceChannel],
         guild: snowflakes.SnowflakeishOr[guilds.Guild],
+        channel: snowflakes.SnowflakeishOr[channels.GuildVoiceChannel],
+        voice_connection_type: typing.Type[_VoiceConnectionT],
         *,
         deaf: bool = False,
         mute: bool = False,
-        voice_connection_type: typing.Type[_VoiceConnectionT],
         **kwargs: typing.Any,
     ) -> _VoiceConnectionT:
         """Connect to a given voice channel.
 
         Parameters
         ----------
-        channel : hikari.snowflakes.SnowflakeishOr[hikari.channels.GuildVoiceChannel]
-            The channel or channel ID to connect to.
         guild : hikari.snowflakes.SnowflakeishOr[hikari.guilds.Guild]
             The guild to connect to.
+        channel : hikari.snowflakes.SnowflakeishOr[hikari.channels.GuildVoiceChannel]
+            The channel or channel ID to connect to.
+        voice_connection_type : typing.Type[VoiceConnection]
+            The type of voice connection to use. This should be initialized
+            internally using the `IVoiceConnection.initialize`
+            `builtins.classmethod`.
         deaf : builtins.bool
             Defaulting to `builtins.False`, if `builtins.True`, the client will
             enter the voice channel deafened (thus unable to hear other users).
         mute : builtins.bool
             Defaulting to `builtins.False`, if `builtins.True`, the client will
             enter the voice channel muted (thus unable to send audio).
-        voice_connection_type : typing.Type[VoiceConnection]
-            The type of voice connection to use. This should be initialized
-            internally using the `IVoiceConnection.initialize`
-            `builtins.classmethod`.
         **kwargs : typing.Any
             Any arguments to provide to the `IVoiceConnection.initialize`
             method.
diff --git a/hikari/impl/entity_factory.py b/hikari/impl/entity_factory.py
index 68a55348..9abf38db 100644
--- a/hikari/impl/entity_factory.py
+++ b/hikari/impl/entity_factory.py
@@ -1934,7 +1934,7 @@ def deserialize_template(self, payload: data_binding.JSONObject) -> template_mod
             created_at=time.iso8601_datetime_string_to_datetime(payload["created_at"]),
             updated_at=time.iso8601_datetime_string_to_datetime(payload["updated_at"]),
             source_guild=source_guild,
-            is_dirty=payload["is_dirty"],
+            is_unsynced=bool(payload["is_dirty"]),
         )
 
     ###############
diff --git a/hikari/impl/voice.py b/hikari/impl/voice.py
index d40977eb..ffac3fbb 100644
--- a/hikari/impl/voice.py
+++ b/hikari/impl/voice.py
@@ -40,12 +40,10 @@
 from hikari.internal import ux
 
 if typing.TYPE_CHECKING:
-    _VoiceEventCallbackT = typing.Callable[[voice_events.VoiceEvent], typing.Coroutine[None, typing.Any, None]]
+    _VoiceConnectionT = typing.TypeVar("_VoiceConnectionT", bound="voice.VoiceConnection")
 
 _LOGGER: typing.Final[logging.Logger] = logging.getLogger("hikari.voice.management")
 
-_VoiceConnectionT = typing.TypeVar("_VoiceConnectionT", bound="voice.VoiceConnection")
-
 
 class VoiceComponentImpl(voice.VoiceComponent):
     """A standard voice component management implementation.
@@ -81,12 +79,12 @@ async def close(self) -> None:
 
     async def connect_to(
         self,
-        channel: snowflakes.SnowflakeishOr[channels.GuildVoiceChannel],
         guild: snowflakes.SnowflakeishOr[guilds.PartialGuild],
+        channel: snowflakes.SnowflakeishOr[channels.GuildVoiceChannel],
+        voice_connection_type: typing.Type[_VoiceConnectionT],
         *,
         deaf: bool = False,
         mute: bool = False,
-        voice_connection_type: typing.Type[_VoiceConnectionT],
         **kwargs: typing.Any,
     ) -> _VoiceConnectionT:
         guild_id = snowflakes.Snowflake(guild)
diff --git a/hikari/templates.py b/hikari/templates.py
index 78efa3b6..72acdec3 100644
--- a/hikari/templates.py
+++ b/hikari/templates.py
@@ -139,11 +139,6 @@ class TemplateGuild(guilds.PartialGuild):
     """Return flags for the guild system channel.
 
     These are used to describe which notifications are suppressed.
-
-    Returns
-    -------
-    GuildSystemChannelFlag
-        The system channel flags for this channel.
     """
 
 
@@ -176,7 +171,7 @@ class Template:
     source_guild: TemplateGuild = attr.ib(eq=False, hash=False, repr=True)
     """The partial object of the guild this template is based on."""
 
-    is_dirty: typing.Optional[bool] = attr.ib(eq=False, hash=False, repr=False)
+    is_unsynced: bool = attr.ib(eq=False, hash=False, repr=False)
     """Whether this template is missing changes from it's source guild."""
 
     def __str__(self) -> str:
diff --git a/hikari/users.py b/hikari/users.py
index ea8b48f5..5e1368bf 100644
--- a/hikari/users.py
+++ b/hikari/users.py
@@ -652,7 +652,7 @@ async def fetch_self(self) -> OwnUser:
         return await self.app.rest.fetch_my_user()
 
     async def fetch_dm_channel(self) -> typing.NoReturn:
-        raise TypeError("Unable to send a DM to yourself")
+        raise TypeError("Unable to fetch your own DM channel")
 
     async def send(self) -> typing.NoReturn:  # type: ignore[override]
         raise TypeError("Unable to send a DM to yourself")


Test patch:
diff --git a/tests/hikari/impl/test_entity_factory.py b/tests/hikari/impl/test_entity_factory.py
index 77fb65f0..80e5d29c 100644
--- a/tests/hikari/impl/test_entity_factory.py
+++ b/tests/hikari/impl/test_entity_factory.py
@@ -1552,6 +1552,13 @@ def test_deserialize_role(self, entity_factory_impl, mock_app, guild_role_payloa
         assert isinstance(guild_role, guild_models.Role)
 
     def test_deserialize_role_with_missing_or_unset_fields(self, entity_factory_impl, guild_role_payload):
+        guild_role_payload["tags"] = {}
+        guild_role = entity_factory_impl.deserialize_role(guild_role_payload, guild_id=snowflakes.Snowflake(76534453))
+        assert guild_role.bot_id is None
+        assert guild_role.integration_id is None
+        assert guild_role.is_premium_subscriber_role is False
+
+    def test_deserialize_role_with_no_tags(self, entity_factory_impl, guild_role_payload):
         del guild_role_payload["tags"]
         guild_role = entity_factory_impl.deserialize_role(guild_role_payload, guild_id=snowflakes.Snowflake(76534453))
         assert guild_role.bot_id is None
@@ -3050,7 +3057,7 @@ def test_deserialize_template(
         assert template.source_guild.system_channel_id == 8
         assert template.source_guild.system_channel_flags == guild_models.GuildSystemChannelFlag.NONE
 
-        assert template.is_dirty is True
+        assert template.is_unsynced is True
 
     def test_deserialize_template_with_null_fields(self, entity_factory_impl, template_payload, user_payload):
         template = entity_factory_impl.deserialize_template(
@@ -3095,7 +3102,7 @@ def test_deserialize_template_with_null_fields(self, entity_factory_impl, templa
         assert template.description is None
         assert template.source_guild.afk_channel_id is None
         assert template.source_guild.system_channel_id is None
-        assert template.is_dirty is None
+        assert template.is_unsynced is False
 
     ###############
     # USER MODELS #
diff --git a/tests/hikari/test_errors.py b/tests/hikari/test_errors.py
index 137fbb41..c63bc570 100644
--- a/tests/hikari/test_errors.py
+++ b/tests/hikari/test_errors.py
@@ -103,6 +103,9 @@ def error(self):
     def test_percentage_completion_property(self, error):
         assert error.percentage_completion == 50
 
+    def test_str(self, error):
+        assert str(error) == "Error encountered when bulk deleting messages (10/20 messages deleted)"
+
 
 class TestMissingIntentError:
     @pytest.fixture()
diff --git a/tests/hikari/test_templates.py b/tests/hikari/test_templates.py
new file mode 100644
index 00000000..240b629d
--- /dev/null
+++ b/tests/hikari/test_templates.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020 Nekokatt
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+import pytest
+
+from hikari import templates
+
+
+class TestTemplate:
+    @pytest.fixture()
+    def obj(self):
+        return templates.Template(
+            code="abc123",
+            name="Test Template",
+            description="Template used for testing",
+            usage_count=101,
+            creator=object(),
+            created_at=object(),
+            updated_at=object(),
+            source_guild=object(),
+            is_unsynced=True,
+        )
+
+    def test_str(self, obj):
+        assert str(obj) == "https://discord.new/abc123"
diff --git a/tests/hikari/test_users.py b/tests/hikari/test_users.py
index 3cc7c6e9..bae66694 100644
--- a/tests/hikari/test_users.py
+++ b/tests/hikari/test_users.py
@@ -270,3 +270,11 @@ async def test_fetch_self(self, obj):
         obj.app.rest.fetch_my_user = mock.AsyncMock(return_value=user)
         assert await obj.fetch_self() is user
         obj.app.rest.fetch_my_user.assert_awaited_once_with()
+
+    async def test_fetch_dm_channel(self, obj):
+        with pytest.raises(TypeError, match=r"Unable to fetch your own DM channel"):
+            await obj.fetch_dm_channel()
+
+    async def test_send(self, obj):
+        with pytest.raises(TypeError, match=r"Unable to send a DM to yourself"):
+            await obj.send()