From e07cf7342184b769d8c0655bc9b84be02809319a Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Wed, 4 Dec 2019 23:38:46 +0700 Subject: Added `unittest` for `bot.utils.time` --- tests/bot/utils/test_time.py | 87 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/bot/utils/test_time.py diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py new file mode 100644 index 000000000..0ef59292e --- /dev/null +++ b/tests/bot/utils/test_time.py @@ -0,0 +1,87 @@ +import asyncio +import unittest +from datetime import datetime, timezone +from unittest.mock import patch + +from dateutil.relativedelta import relativedelta + +from bot.utils import time +from tests.helpers import AsyncMock + + +class TimeTests(unittest.TestCase): + """Test helper functions in bot.utils.time.""" + + def setUp(self): + pass + + def test_humanize_delta(self): + """Testing humanize delta.""" + test_cases = ( + (relativedelta(days=2), 'seconds', 1, '2 days'), + (relativedelta(days=2, hours=2), 'seconds', 2, '2 days and 2 hours'), + (relativedelta(days=2, hours=2), 'seconds', 1, '2 days'), + (relativedelta(days=2, hours=2), 'days', 2, '2 days'), + + # Does not abort for unknown units, as the unit name is checked + # against the attribute of the relativedelta instance. + (relativedelta(days=2, hours=2), 'elephants', 2, '2 days and 2 hours'), + + # Very high maximum units, but it only ever iterates over + # each value the relativedelta might have. + (relativedelta(days=2, hours=2), 'hours', 20, '2 days and 2 hours'), + ) + + for delta, precision, max_units, expected in test_cases: + self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + + def test_humanize_delta_raises_for_invalid_max_units(self): + test_cases = (-1, 0) + + for max_units in test_cases: + with self.assertRaises(ValueError) as error: + time.humanize_delta(relativedelta(days=2, hours=2), 'hours', max_units) + self.assertEqual(str(error), 'max_units must be positive') + + def test_parse_rfc1123(self): + """Testing parse_rfc1123.""" + test_cases = ( + ('Sun, 15 Sep 2019 12:00:00 GMT', datetime(2019, 9, 15, 12, 0, 0, tzinfo=timezone.utc)), + ) + + for stamp, expected in test_cases: + self.assertEqual(time.parse_rfc1123(stamp), expected) + + @patch('asyncio.sleep', new_callable=AsyncMock) + def test_wait_until(self, mock): + """Testing wait_until.""" + start = datetime(2019, 1, 1, 0, 0) + then = datetime(2019, 1, 1, 0, 10) + + # No return value + assert asyncio.run(time.wait_until(then, start)) is None + + mock.assert_called_once_with(10 * 60) + + def test_format_infraction_with_duration(self): + """Testing format_infraction_with_duration.""" + test_cases = ( + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 2, '2019-12-12 00:01 (12 hours and 55 seconds)'), + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 1, '2019-12-12 00:01 (12 hours)'), + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6, + '2019-12-12 00:01 (11 hours, 55 minutes and 55 seconds)'), + ('2019-12-12T00:00:00Z', datetime(2019, 12, 11, 23, 59), 2, '2019-12-12 00:00 (1 minute)'), + ('2019-11-23T20:09:00Z', datetime(2019, 11, 15, 20, 15), 2, '2019-11-23 20:09 (7 days and 23 hours)'), + ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 2, '2019-11-23 20:09 (6 months and 28 days)'), + ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 6, + '2019-11-23 20:09 (6 months, 28 days, 23 hours and 54 minutes)'), + ('2019-11-23T20:58:00Z', datetime(2019, 11, 23, 20, 53), 2, '2019-11-23 20:58 (5 minutes)'), + ('2019-11-24T00:00:00Z', datetime(2019, 11, 23, 23, 59, 0), 2, '2019-11-24 00:00 (1 minute)'), + ('2019-11-23T23:59:00Z', datetime(2017, 7, 21, 23, 0), 2, '2019-11-23 23:59 (2 years and 4 months)'), + ('2019-11-23T23:59:00Z', datetime(2019, 11, 23, 23, 49, 5), 2, + '2019-11-23 23:59 (9 minutes and 55 seconds)'), + (None, datetime(2019, 11, 23, 23, 49, 5), 2, None), + ) + + for expiry, date_from, max_units, expected in test_cases: + self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) -- cgit v1.2.3 From b17dbe5e3e0dfa6ae44d660924455f709abefd0d Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 00:34:58 +0700 Subject: Splitting test cases for `humanize_delta` into proper, independent tests. --- tests/bot/utils/test_time.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 0ef59292e..5e5f2bf2f 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -15,18 +15,20 @@ class TimeTests(unittest.TestCase): def setUp(self): pass - def test_humanize_delta(self): - """Testing humanize delta.""" + def test_humanize_delta_handle_unknown_units(self): + """humanize_delta should be able to handle unknown units, and will not abort.""" test_cases = ( - (relativedelta(days=2), 'seconds', 1, '2 days'), - (relativedelta(days=2, hours=2), 'seconds', 2, '2 days and 2 hours'), - (relativedelta(days=2, hours=2), 'seconds', 1, '2 days'), - (relativedelta(days=2, hours=2), 'days', 2, '2 days'), - # Does not abort for unknown units, as the unit name is checked # against the attribute of the relativedelta instance. (relativedelta(days=2, hours=2), 'elephants', 2, '2 days and 2 hours'), + ) + for delta, precision, max_units, expected in test_cases: + self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + + def test_humanize_delta_handle_high_units(self): + """humanize_delta should be able to handle very high units.""" + test_cases = ( # Very high maximum units, but it only ever iterates over # each value the relativedelta might have. (relativedelta(days=2, hours=2), 'hours', 20, '2 days and 2 hours'), @@ -35,6 +37,18 @@ class TimeTests(unittest.TestCase): for delta, precision, max_units, expected in test_cases: self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + def test_humanize_delta_should_work_normally(self): + """Testing humanize delta.""" + test_cases = ( + (relativedelta(days=2), 'seconds', 1, '2 days'), + (relativedelta(days=2, hours=2), 'seconds', 2, '2 days and 2 hours'), + (relativedelta(days=2, hours=2), 'seconds', 1, '2 days'), + (relativedelta(days=2, hours=2), 'days', 2, '2 days'), + ) + + for delta, precision, max_units, expected in test_cases: + self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + def test_humanize_delta_raises_for_invalid_max_units(self): test_cases = (-1, 0) -- cgit v1.2.3 From 0aee728d6d23ef24f51834f39016f938f3f1b8a9 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 00:36:29 +0700 Subject: Added missing docstring for `test_humanize_delta_raises_for_invalid_max_units` --- tests/bot/utils/test_time.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 5e5f2bf2f..a929bee89 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -50,6 +50,7 @@ class TimeTests(unittest.TestCase): self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) def test_humanize_delta_raises_for_invalid_max_units(self): + """humanize_delta should raises ValueError('max_units must be positive') for invalid max units.""" test_cases = (-1, 0) for max_units in test_cases: -- cgit v1.2.3 From beed21355e7f0e25b69637768843c53d510b8969 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 00:40:38 +0700 Subject: Changed `assert` to `self.assertIs` for `test_wait_until` --- tests/bot/utils/test_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index a929bee89..0afabe400 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -74,7 +74,7 @@ class TimeTests(unittest.TestCase): then = datetime(2019, 1, 1, 0, 10) # No return value - assert asyncio.run(time.wait_until(then, start)) is None + self.assertIs(asyncio.run(time.wait_until(then, start)), None) mock.assert_called_once_with(10 * 60) -- cgit v1.2.3 From ccdd8363d75846f0841791ba54763dae28243c62 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 00:45:36 +0700 Subject: Splitting test cases for `format_infraction_with_duration` into proper, independent tests. --- tests/bot/utils/test_time.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 0afabe400..2a2a707d8 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -37,7 +37,7 @@ class TimeTests(unittest.TestCase): for delta, precision, max_units, expected in test_cases: self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) - def test_humanize_delta_should_work_normally(self): + def test_humanize_delta_should_normal_usage(self): """Testing humanize delta.""" test_cases = ( (relativedelta(days=2), 'seconds', 1, '2 days'), @@ -78,18 +78,38 @@ class TimeTests(unittest.TestCase): mock.assert_called_once_with(10 * 60) - def test_format_infraction_with_duration(self): - """Testing format_infraction_with_duration.""" + def test_format_infraction_with_duration_none_expiry(self): + """format_infraction_with_duration should work for None expiry.""" + self.assertEqual(time.format_infraction_with_duration(None), None) + + # To make sure that date_from and max_units are not touched + self.assertEqual(time.format_infraction_with_duration(None, date_from='Why hello there!'), None) + self.assertEqual(time.format_infraction_with_duration(None, max_units=float('inf')), None) + self.assertEqual( + time.format_infraction_with_duration(None, date_from='Why hello there!', max_units=float('inf')), + None + ) + + def test_format_infraction_with_duration_custom_units(self): + """format_infraction_with_duration should work for custom max_units.""" + self.assertEqual( + time.format_infraction_with_duration('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6), + '2019-12-12 00:01 (11 hours, 55 minutes and 55 seconds)' + ) + + self.assertEqual( + time.format_infraction_with_duration('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 20), + '2019-11-23 20:09 (6 months, 28 days, 23 hours and 54 minutes)' + ) + + def test_format_infraction_with_duration_normal_usage(self): + """format_infraction_with_duration should work for normal usage, across various durations.""" test_cases = ( ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 2, '2019-12-12 00:01 (12 hours and 55 seconds)'), ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 1, '2019-12-12 00:01 (12 hours)'), - ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6, - '2019-12-12 00:01 (11 hours, 55 minutes and 55 seconds)'), ('2019-12-12T00:00:00Z', datetime(2019, 12, 11, 23, 59), 2, '2019-12-12 00:00 (1 minute)'), ('2019-11-23T20:09:00Z', datetime(2019, 11, 15, 20, 15), 2, '2019-11-23 20:09 (7 days and 23 hours)'), ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 2, '2019-11-23 20:09 (6 months and 28 days)'), - ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 6, - '2019-11-23 20:09 (6 months, 28 days, 23 hours and 54 minutes)'), ('2019-11-23T20:58:00Z', datetime(2019, 11, 23, 20, 53), 2, '2019-11-23 20:58 (5 minutes)'), ('2019-11-24T00:00:00Z', datetime(2019, 11, 23, 23, 59, 0), 2, '2019-11-24 00:00 (1 minute)'), ('2019-11-23T23:59:00Z', datetime(2017, 7, 21, 23, 0), 2, '2019-11-23 23:59 (2 years and 4 months)'), -- cgit v1.2.3 From fa66195dbb6f79bb7174084835499a61e8cb03a3 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 00:52:15 +0700 Subject: Introduced test for `test_format_infraction`, refactored `test_parse_rfc1123`, fixed typo. --- tests/bot/utils/test_time.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 2a2a707d8..09fb824e4 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -50,7 +50,7 @@ class TimeTests(unittest.TestCase): self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) def test_humanize_delta_raises_for_invalid_max_units(self): - """humanize_delta should raises ValueError('max_units must be positive') for invalid max units.""" + """humanize_delta should raises ValueError('max_units must be positive') for invalid max_units.""" test_cases = (-1, 0) for max_units in test_cases: @@ -60,12 +60,14 @@ class TimeTests(unittest.TestCase): def test_parse_rfc1123(self): """Testing parse_rfc1123.""" - test_cases = ( - ('Sun, 15 Sep 2019 12:00:00 GMT', datetime(2019, 9, 15, 12, 0, 0, tzinfo=timezone.utc)), + self.assertEqual( + time.parse_rfc1123('Sun, 15 Sep 2019 12:00:00 GMT'), + datetime(2019, 9, 15, 12, 0, 0, tzinfo=timezone.utc) ) - for stamp, expected in test_cases: - self.assertEqual(time.parse_rfc1123(stamp), expected) + def test_format_infraction(self): + """Testing format_infraction.""" + self.assertEqual(time.format_infraction('2019-12-12T00:01:00Z'), '2019-12-12 00:01') @patch('asyncio.sleep', new_callable=AsyncMock) def test_wait_until(self, mock): -- cgit v1.2.3 From 5e0b19ae841f3f355931ad331f7aa861fbafc4d9 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 01:05:41 +0700 Subject: Added `self.subTest` for tests with multiple test cases & simplified single test case tests. --- tests/bot/utils/test_time.py | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 09fb824e4..c47a306f0 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -17,25 +17,15 @@ class TimeTests(unittest.TestCase): def test_humanize_delta_handle_unknown_units(self): """humanize_delta should be able to handle unknown units, and will not abort.""" - test_cases = ( - # Does not abort for unknown units, as the unit name is checked - # against the attribute of the relativedelta instance. - (relativedelta(days=2, hours=2), 'elephants', 2, '2 days and 2 hours'), - ) - - for delta, precision, max_units, expected in test_cases: - self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + # Does not abort for unknown units, as the unit name is checked + # against the attribute of the relativedelta instance. + self.assertEqual(time.humanize_delta(relativedelta(days=2, hours=2), 'elephants', 2), '2 days and 2 hours') def test_humanize_delta_handle_high_units(self): """humanize_delta should be able to handle very high units.""" - test_cases = ( - # Very high maximum units, but it only ever iterates over - # each value the relativedelta might have. - (relativedelta(days=2, hours=2), 'hours', 20, '2 days and 2 hours'), - ) - - for delta, precision, max_units, expected in test_cases: - self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + # Very high maximum units, but it only ever iterates over + # each value the relativedelta might have. + self.assertEqual(time.humanize_delta(relativedelta(days=2, hours=2), 'hours', 20), '2 days and 2 hours') def test_humanize_delta_should_normal_usage(self): """Testing humanize delta.""" @@ -47,14 +37,15 @@ class TimeTests(unittest.TestCase): ) for delta, precision, max_units, expected in test_cases: - self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) + with self.subTest(delta=delta, precision=precision, max_units=max_units, expected=expected): + self.assertEqual(time.humanize_delta(delta, precision, max_units), expected) def test_humanize_delta_raises_for_invalid_max_units(self): """humanize_delta should raises ValueError('max_units must be positive') for invalid max_units.""" test_cases = (-1, 0) for max_units in test_cases: - with self.assertRaises(ValueError) as error: + with self.subTest(max_units=max_units), self.assertRaises(ValueError) as error: time.humanize_delta(relativedelta(days=2, hours=2), 'hours', max_units) self.assertEqual(str(error), 'max_units must be positive') @@ -121,4 +112,5 @@ class TimeTests(unittest.TestCase): ) for expiry, date_from, max_units, expected in test_cases: - self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) + with self.subTest(expiry=expiry, date_from=date_from, max_units=max_units, expected=expected): + self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) -- cgit v1.2.3 From db341d927aab42c2e874cb499ab1c2e6c0e7647b Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 01:17:56 +0700 Subject: Moved all individual test cases into iterables and test with `self.subTest` context manager. --- tests/bot/utils/test_time.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index c47a306f0..25cd3f69f 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -73,27 +73,31 @@ class TimeTests(unittest.TestCase): def test_format_infraction_with_duration_none_expiry(self): """format_infraction_with_duration should work for None expiry.""" - self.assertEqual(time.format_infraction_with_duration(None), None) + test_cases = ( + (None, None, None, None), - # To make sure that date_from and max_units are not touched - self.assertEqual(time.format_infraction_with_duration(None, date_from='Why hello there!'), None) - self.assertEqual(time.format_infraction_with_duration(None, max_units=float('inf')), None) - self.assertEqual( - time.format_infraction_with_duration(None, date_from='Why hello there!', max_units=float('inf')), - None + # To make sure that date_from and max_units are not touched + (None, 'Why hello there!', None, None), + (None, None, float('inf'), None), + (None, 'Why hello there!', float('inf'), None), ) + for expiry, date_from, max_units, expected in test_cases: + with self.subTest(expiry=expiry, date_from=date_from, max_units=max_units, expected=expected): + self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) + def test_format_infraction_with_duration_custom_units(self): """format_infraction_with_duration should work for custom max_units.""" - self.assertEqual( - time.format_infraction_with_duration('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6), - '2019-12-12 00:01 (11 hours, 55 minutes and 55 seconds)' + test_cases = ( + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6, + '2019-12-12 00:01 (11 hours, 55 minutes and 55 seconds)'), + ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 20, + '2019-11-23 20:09 (6 months, 28 days, 23 hours and 54 minutes)') ) - self.assertEqual( - time.format_infraction_with_duration('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 20), - '2019-11-23 20:09 (6 months, 28 days, 23 hours and 54 minutes)' - ) + for expiry, date_from, max_units, expected in test_cases: + with self.subTest(expiry=expiry, date_from=date_from, max_units=max_units, expected=expected): + self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) def test_format_infraction_with_duration_normal_usage(self): """format_infraction_with_duration should work for normal usage, across various durations.""" -- cgit v1.2.3 From 323306776b0312e2a32ada213a35159311a93a7f Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Thu, 5 Dec 2019 01:42:23 +0700 Subject: Removed `setUp()` from `TimeTests` since it is not being used for anything. --- tests/bot/utils/test_time.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 25cd3f69f..7f55dc3ec 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -12,9 +12,6 @@ from tests.helpers import AsyncMock class TimeTests(unittest.TestCase): """Test helper functions in bot.utils.time.""" - def setUp(self): - pass - def test_humanize_delta_handle_unknown_units(self): """humanize_delta should be able to handle unknown units, and will not abort.""" # Does not abort for unknown units, as the unit name is checked -- cgit v1.2.3 From c5109844e45c37bc1cc38eb1c3da31d52ab2aa6d Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Fri, 13 Dec 2019 09:17:21 +0700 Subject: Adding an optional argument for `until_expiration`, update typehints for `format_infraction_with_duration` - `until_expiration` was being a pain to unittests without a `now` ( default to `datetime.utcnow()` ). Adding an optional argument for this will not only make writing tests easier, but also allow more control over the helper function should we need to calculate the remaining time between two dates in the past. - Changed typehint for `date_from` in `format_infraction_with_duration` to `Optional[datetime.datetime]` to better reflect what it is. --- bot/utils/time.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/bot/utils/time.py b/bot/utils/time.py index ac64865d6..7416f36e0 100644 --- a/bot/utils/time.py +++ b/bot/utils/time.py @@ -115,7 +115,7 @@ def format_infraction(timestamp: str) -> str: def format_infraction_with_duration( expiry: Optional[str], - date_from: datetime.datetime = None, + date_from: Optional[datetime.datetime] = None, max_units: int = 2 ) -> Optional[str]: """ @@ -140,10 +140,15 @@ def format_infraction_with_duration( return f"{expiry_formatted}{duration_formatted}" -def until_expiration(expiry: Optional[str], max_units: int = 2) -> Optional[str]: +def until_expiration( + expiry: Optional[str], + now: Optional[datetime.datetime] = None, + max_units: int = 2 +) -> Optional[str]: """ Get the remaining time until infraction's expiration, in a human-readable version of the relativedelta. + Returns a human-readable version of the remaining duration between datetime.utcnow() and an expiry. Unlike `humanize_delta`, this function will force the `precision` to be `seconds` by not passing it. `max_units` specifies the maximum number of units of time to include (e.g. 1 may include days but not hours). By default, max_units is 2. @@ -151,7 +156,7 @@ def until_expiration(expiry: Optional[str], max_units: int = 2) -> Optional[str] if not expiry: return None - now = datetime.datetime.utcnow() + now = now or datetime.datetime.utcnow() since = dateutil.parser.isoparse(expiry).replace(tzinfo=None, microsecond=0) if since < now: -- cgit v1.2.3 From 520346d0b472e5cb6c9091a8323b871d2e3821cc Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Fri, 13 Dec 2019 09:18:49 +0700 Subject: Added tests for `until_expiration` Similar to `format_infraction_with_duration` ( if not outright copying it ), added 3 tests for `until_expiration`: - None `expiry`. - Custom `max_units`. - Normal use cases. --- tests/bot/utils/test_time.py | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index 7f55dc3ec..bd04de28b 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -115,3 +115,48 @@ class TimeTests(unittest.TestCase): for expiry, date_from, max_units, expected in test_cases: with self.subTest(expiry=expiry, date_from=date_from, max_units=max_units, expected=expected): self.assertEqual(time.format_infraction_with_duration(expiry, date_from, max_units), expected) + + def test_until_expiration_with_duration_none_expiry(self): + """until_expiration should work for None expiry.""" + test_cases = ( + (None, None, None, None), + + # To make sure that date_from and max_units are not touched + (None, 'Why hello there!', None, None), + (None, None, float('inf'), None), + (None, 'Why hello there!', float('inf'), None), + ) + + for expiry, now, max_units, expected in test_cases: + with self.subTest(expiry=expiry, now=now, max_units=max_units, expected=expected): + self.assertEqual(time.until_expiration(expiry, now, max_units), expected) + + def test_until_expiration_with_duration_custom_units(self): + """until_expiration should work for custom max_units.""" + test_cases = ( + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 5, 5), 6, '11 hours, 55 minutes and 55 seconds'), + ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 20, '6 months, 28 days, 23 hours and 54 minutes') + ) + + for expiry, now, max_units, expected in test_cases: + with self.subTest(expiry=expiry, now=now, max_units=max_units, expected=expected): + self.assertEqual(time.until_expiration(expiry, now, max_units), expected) + + def test_until_expiration_normal_usage(self): + """until_expiration should work for normal usage, across various durations.""" + test_cases = ( + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 2, '12 hours and 55 seconds'), + ('2019-12-12T00:01:00Z', datetime(2019, 12, 11, 12, 0, 5), 1, '12 hours'), + ('2019-12-12T00:00:00Z', datetime(2019, 12, 11, 23, 59), 2, '1 minute'), + ('2019-11-23T20:09:00Z', datetime(2019, 11, 15, 20, 15), 2, '7 days and 23 hours'), + ('2019-11-23T20:09:00Z', datetime(2019, 4, 25, 20, 15), 2, '6 months and 28 days'), + ('2019-11-23T20:58:00Z', datetime(2019, 11, 23, 20, 53), 2, '5 minutes'), + ('2019-11-24T00:00:00Z', datetime(2019, 11, 23, 23, 59, 0), 2, '1 minute'), + ('2019-11-23T23:59:00Z', datetime(2017, 7, 21, 23, 0), 2, '2 years and 4 months'), + ('2019-11-23T23:59:00Z', datetime(2019, 11, 23, 23, 49, 5), 2, '9 minutes and 55 seconds'), + (None, datetime(2019, 11, 23, 23, 49, 5), 2, None), + ) + + for expiry, now, max_units, expected in test_cases: + with self.subTest(expiry=expiry, now=now, max_units=max_units, expected=expected): + self.assertEqual(time.until_expiration(expiry, now, max_units), expected) -- cgit v1.2.3 From 66d4b93593b95bfa6999b70aca53328d83710c44 Mon Sep 17 00:00:00 2001 From: Shirayuki Nekomata Date: Fri, 13 Dec 2019 09:29:06 +0700 Subject: Fixed a typo ( due to poor copy pasta and eyeballing skills ) --- tests/bot/utils/test_time.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/bot/utils/test_time.py b/tests/bot/utils/test_time.py index bd04de28b..69f35f2f5 100644 --- a/tests/bot/utils/test_time.py +++ b/tests/bot/utils/test_time.py @@ -121,7 +121,7 @@ class TimeTests(unittest.TestCase): test_cases = ( (None, None, None, None), - # To make sure that date_from and max_units are not touched + # To make sure that now and max_units are not touched (None, 'Why hello there!', None, None), (None, None, float('inf'), None), (None, 'Why hello there!', float('inf'), None), -- cgit v1.2.3