diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/cogs/test_information.py | 48 | ||||
-rw-r--r-- | tests/test_converters.py | 201 | ||||
-rw-r--r-- | tests/test_resources.py | 11 |
3 files changed, 237 insertions, 23 deletions
diff --git a/tests/cogs/test_information.py b/tests/cogs/test_information.py index 85b2d092e..184bd2595 100644 --- a/tests/cogs/test_information.py +++ b/tests/cogs/test_information.py @@ -8,6 +8,8 @@ import pytest from discord import ( CategoryChannel, Colour, + Permissions, + Role, TextChannel, VoiceChannel, ) @@ -66,6 +68,52 @@ def test_roles_info_command(cog, ctx): assert embed.footer.text == "Total roles: 1" +def test_role_info_command(cog, ctx): + dummy_role = MagicMock(spec=Role) + dummy_role.name = "Dummy" + dummy_role.colour = Colour.blurple() + dummy_role.id = 112233445566778899 + dummy_role.position = 10 + dummy_role.permissions = Permissions(0) + dummy_role.members = [ctx.author] + + admin_role = MagicMock(spec=Role) + admin_role.name = "Admin" + admin_role.colour = Colour.red() + admin_role.id = 998877665544332211 + admin_role.position = 3 + admin_role.permissions = Permissions(0) + admin_role.members = [ctx.author] + + ctx.guild.roles = [dummy_role, admin_role] + + cog.role_info.can_run = AsyncMock() + cog.role_info.can_run.return_value = True + + coroutine = cog.role_info.callback(cog, ctx, dummy_role, admin_role) + + assert asyncio.run(coroutine) is None + + assert ctx.send.call_count == 2 + + (_, dummy_kwargs), (_, admin_kwargs) = ctx.send.call_args_list + + dummy_embed = dummy_kwargs["embed"] + admin_embed = admin_kwargs["embed"] + + assert dummy_embed.title == "Dummy info" + assert dummy_embed.colour == Colour.blurple() + + assert dummy_embed.fields[0].value == str(dummy_role.id) + assert dummy_embed.fields[1].value == f"#{dummy_role.colour.value:0>6x}" + assert dummy_embed.fields[2].value == "0.63 0.48 218" + assert dummy_embed.fields[3].value == "1" + assert dummy_embed.fields[4].value == "10" + assert dummy_embed.fields[5].value == "0" + + assert admin_embed.title == "Admin info" + assert admin_embed.colour == Colour.red() + # There is no argument passed in here that we can use to test, # so the return value would change constantly. @patch('bot.cogs.information.time_since') diff --git a/tests/test_converters.py b/tests/test_converters.py index 3cf774c80..f69995ec6 100644 --- a/tests/test_converters.py +++ b/tests/test_converters.py @@ -1,12 +1,14 @@ import asyncio -from datetime import datetime -from unittest.mock import MagicMock +import datetime +from unittest.mock import MagicMock, patch import pytest +from dateutil.relativedelta import relativedelta from discord.ext.commands import BadArgument from bot.converters import ( - ExpirationDate, + Duration, + ISODateTime, TagContentConverter, TagNameConverter, ValidPythonIdentifier, @@ -16,18 +18,6 @@ from bot.converters import ( @pytest.mark.parametrize( ('value', 'expected'), ( - # sorry aliens - ('2199-01-01T00:00:00', datetime(2199, 1, 1)), - ) -) -def test_expiration_date_converter_for_valid(value: str, expected: datetime): - converter = ExpirationDate() - assert asyncio.run(converter.convert(None, value)) == expected - - - ('value', 'expected'), - ( ('hello', 'hello'), (' h ello ', 'h ello') ) @@ -91,3 +81,184 @@ def test_valid_python_identifier_for_valid(value: str): def test_valid_python_identifier_for_invalid(value: str): with pytest.raises(BadArgument, match=f'`{value}` is not a valid Python identifier'): asyncio.run(ValidPythonIdentifier.convert(None, value)) + + +FIXED_UTC_NOW = datetime.datetime.fromisoformat('2019-01-01T00:00:00') + + + params=( + # Simple duration strings + ('1Y', {"years": 1}), + ('1y', {"years": 1}), + ('1year', {"years": 1}), + ('1years', {"years": 1}), + ('1m', {"months": 1}), + ('1month', {"months": 1}), + ('1months', {"months": 1}), + ('1w', {"weeks": 1}), + ('1W', {"weeks": 1}), + ('1week', {"weeks": 1}), + ('1weeks', {"weeks": 1}), + ('1d', {"days": 1}), + ('1D', {"days": 1}), + ('1day', {"days": 1}), + ('1days', {"days": 1}), + ('1h', {"hours": 1}), + ('1H', {"hours": 1}), + ('1hour', {"hours": 1}), + ('1hours', {"hours": 1}), + ('1M', {"minutes": 1}), + ('1minute', {"minutes": 1}), + ('1minutes', {"minutes": 1}), + ('1s', {"seconds": 1}), + ('1S', {"seconds": 1}), + ('1second', {"seconds": 1}), + ('1seconds', {"seconds": 1}), + + # Complex duration strings + ( + '1y1m1w1d1H1M1S', + { + "years": 1, + "months": 1, + "weeks": 1, + "days": 1, + "hours": 1, + "minutes": 1, + "seconds": 1 + } + ), + ('5y100S', {"years": 5, "seconds": 100}), + ('2w28H', {"weeks": 2, "hours": 28}), + + # Duration strings with spaces + ('1 year 2 months', {"years": 1, "months": 2}), + ('1d 2H', {"days": 1, "hours": 2}), + ('1 week2 days', {"weeks": 1, "days": 2}), + ) +) +def create_future_datetime(request): + """Yields duration string and target datetime.datetime object.""" + duration, duration_dict = request.param + future_datetime = FIXED_UTC_NOW + relativedelta(**duration_dict) + yield duration, future_datetime + + +def test_duration_converter_for_valid(create_future_datetime: tuple): + converter = Duration() + duration, expected = create_future_datetime + with patch('bot.converters.datetime') as mock_datetime: + mock_datetime.utcnow.return_value = FIXED_UTC_NOW + assert asyncio.run(converter.convert(None, duration)) == expected + + + ('duration'), + ( + # Units in wrong order + ('1d1w'), + ('1s1y'), + + # Duplicated units + ('1 year 2 years'), + ('1 M 10 minutes'), + + # Unknown substrings + ('1MVes'), + ('1y3breads'), + + # Missing amount + ('ym'), + + # Incorrect whitespace + (" 1y"), + ("1S "), + ("1y 1m"), + + # Garbage + ('Guido van Rossum'), + ('lemon lemon lemon lemon lemon lemon lemon'), + ) +) +def test_duration_converter_for_invalid(duration: str): + converter = Duration() + with pytest.raises(BadArgument, match=f'`{duration}` is not a valid duration string.'): + asyncio.run(converter.convert(None, duration)) + + + ("datetime_string", "expected_dt"), + ( + + # `YYYY-mm-ddTHH:MM:SSZ` | `YYYY-mm-dd HH:MM:SSZ` + ('2019-09-02T02:03:05Z', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 02:03:05Z', datetime.datetime(2019, 9, 2, 2, 3, 5)), + + # `YYYY-mm-ddTHH:MM:SS±HH:MM` | `YYYY-mm-dd HH:MM:SS±HH:MM` + ('2019-09-02T03:18:05+01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 03:18:05+01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02T00:48:05-01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 00:48:05-01:15', datetime.datetime(2019, 9, 2, 2, 3, 5)), + + # `YYYY-mm-ddTHH:MM:SS±HHMM` | `YYYY-mm-dd HH:MM:SS±HHMM` + ('2019-09-02T03:18:05+0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 03:18:05+0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02T00:48:05-0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 00:48:05-0115', datetime.datetime(2019, 9, 2, 2, 3, 5)), + + # `YYYY-mm-ddTHH:MM:SS±HH` | `YYYY-mm-dd HH:MM:SS±HH` + ('2019-09-02 03:03:05+01', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02T01:03:05-01', datetime.datetime(2019, 9, 2, 2, 3, 5)), + + # `YYYY-mm-ddTHH:MM:SS` | `YYYY-mm-dd HH:MM:SS` + ('2019-09-02T02:03:05', datetime.datetime(2019, 9, 2, 2, 3, 5)), + ('2019-09-02 02:03:05', datetime.datetime(2019, 9, 2, 2, 3, 5)), + + # `YYYY-mm-ddTHH:MM` | `YYYY-mm-dd HH:MM` + ('2019-11-12T09:15', datetime.datetime(2019, 11, 12, 9, 15)), + ('2019-11-12 09:15', datetime.datetime(2019, 11, 12, 9, 15)), + + # `YYYY-mm-dd` + ('2019-04-01', datetime.datetime(2019, 4, 1)), + + # `YYYY-mm` + ('2019-02-01', datetime.datetime(2019, 2, 1)), + + # `YYYY` + ('2025', datetime.datetime(2025, 1, 1)), + ), +) +def test_isodatetime_converter_for_valid(datetime_string: str, expected_dt: datetime.datetime): + converter = ISODateTime() + converted_dt = asyncio.run(converter.convert(None, datetime_string)) + assert converted_dt.tzinfo is None + assert converted_dt == expected_dt + + + ("datetime_string"), + ( + # Make sure it doesn't interfere with the Duration converter + ('1Y'), + ('1d'), + ('1H'), + + # Check if it fails when only providing the optional time part + ('10:10:10'), + ('10:00'), + + # Invalid date format + ('19-01-01'), + + # Other non-valid strings + ('fisk the tag master'), + ), +) +def test_isodatetime_converter_for_invalid(datetime_string: str): + converter = ISODateTime() + with pytest.raises( + BadArgument, + match=f"`{datetime_string}` is not a valid ISO-8601 datetime string", + ): + asyncio.run(converter.convert(None, datetime_string)) diff --git a/tests/test_resources.py b/tests/test_resources.py index 2b17aea64..bcf124f05 100644 --- a/tests/test_resources.py +++ b/tests/test_resources.py @@ -1,18 +1,13 @@ import json -import mimetypes from pathlib import Path -from urllib.parse import urlparse def test_stars_valid(): - """Validates that `bot/resources/stars.json` contains valid images.""" + """Validates that `bot/resources/stars.json` contains a list of strings.""" path = Path('bot', 'resources', 'stars.json') content = path.read_text() data = json.loads(content) - for url in data.values(): - assert urlparse(url).scheme == 'https' - - mimetype, _ = mimetypes.guess_type(url) - assert mimetype in ('image/jpeg', 'image/png') + for name in data: + assert type(name) is str |