import re import pytz import pytest from datetime import date, datetime from flask_restx import inputs class Iso8601DateTest(object): @pytest.mark.parametrize( "value,expected", [ ("2011-01-01", date(2011, 1, 1)), ("2011-01-01T00:00:00+00:00", date(2011, 1, 1)), ("2011-01-01T23:59:59+00:00", date(2011, 1, 1)), ("2011-01-01T23:59:59.001000+00:00", date(2011, 1, 1)), ("2011-01-01T23:59:59+02:00", date(2011, 1, 1)), ], ) def test_valid_values(self, value, expected): assert inputs.date_from_iso8601(value) == expected def test_error(self): with pytest.raises(ValueError): inputs.date_from_iso8601("2008-13-13") def test_schema(self): assert inputs.date_from_iso8601.__schema__ == { "type": "string", "format": "date", } class Iso8601DatetimeTest(object): @pytest.mark.parametrize( "value,expected", [ ("2011-01-01", datetime(2011, 1, 1)), ("2011-01-01T00:00:00+00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), ( "2011-01-01T23:59:59+00:00", datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), ), ( "2011-01-01T23:59:59.001000+00:00", datetime(2011, 1, 1, 23, 59, 59, 1000, tzinfo=pytz.utc), ), ( "2011-01-01T23:59:59+02:00", datetime(2011, 1, 1, 21, 59, 59, tzinfo=pytz.utc), ), ], ) def test_valid_values(self, value, expected): assert inputs.datetime_from_iso8601(value) == expected def test_error(self): with pytest.raises(ValueError): inputs.datetime_from_iso8601("2008-13-13") def test_schema(self): assert inputs.datetime_from_iso8601.__schema__ == { "type": "string", "format": "date-time", } class Rfc822DatetimeTest(object): @pytest.mark.parametrize( "value,expected", [ ("Sat, 01 Jan 2011", datetime(2011, 1, 1, tzinfo=pytz.utc)), ("Sat, 01 Jan 2011 00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), ("Sat, 01 Jan 2011 00:00:00", datetime(2011, 1, 1, tzinfo=pytz.utc)), ("Sat, 01 Jan 2011 00:00:00 +0000", datetime(2011, 1, 1, tzinfo=pytz.utc)), ("Sat, 01 Jan 2011 00:00:00 -0000", datetime(2011, 1, 1, tzinfo=pytz.utc)), ( "Sat, 01 Jan 2011 23:59:59 -0000", datetime(2011, 1, 1, 23, 59, 59, tzinfo=pytz.utc), ), ( "Sat, 01 Jan 2011 21:00:00 +0200", datetime(2011, 1, 1, 19, 0, 0, tzinfo=pytz.utc), ), ( "Sat, 01 Jan 2011 21:00:00 -0200", datetime(2011, 1, 1, 23, 0, 0, tzinfo=pytz.utc), ), ], ) def test_valid_values(self, value, expected): assert inputs.datetime_from_rfc822(value) == expected def test_error(self): with pytest.raises(ValueError): inputs.datetime_from_rfc822("Fake, 01 XXX 2011") class NetlocRegexpTest(object): @pytest.mark.parametrize( "netloc,kwargs", [ ("localhost", {"localhost": "localhost"}), ("example.com", {"domain": "example.com"}), ("www.example.com", {"domain": "www.example.com"}), ("www.example.com:8000", {"domain": "www.example.com", "port": "8000"}), ("valid-with-hyphens.com", {"domain": "valid-with-hyphens.com"}), ("subdomain.example.com", {"domain": "subdomain.example.com"}), ("200.8.9.10", {"ipv4": "200.8.9.10"}), ("200.8.9.10:8000", {"ipv4": "200.8.9.10", "port": "8000"}), ("valid-----hyphens.com", {"domain": "valid-----hyphens.com"}), ("foo:bar@example.com", {"auth": "foo:bar", "domain": "example.com"}), ("foo:@example.com", {"auth": "foo:", "domain": "example.com"}), ("foo@example.com", {"auth": "foo", "domain": "example.com"}), ( "foo:@2001:db8:85a3::8a2e:370:7334", {"auth": "foo:", "ipv6": "2001:db8:85a3::8a2e:370:7334"}, ), ( "[1fff:0:a88:85a3::ac1f]:8001", {"ipv6": "1fff:0:a88:85a3::ac1f", "port": "8001"}, ), ("foo2:qd1%r@example.com", {"auth": "foo2:qd1%r", "domain": "example.com"}), ], ) def test_match(self, netloc, kwargs): match = inputs.netloc_regex.match(netloc) assert match, "Should match {0}".format(netloc) expected = { "auth": None, "domain": None, "ipv4": None, "ipv6": None, "localhost": None, "port": None, } expected.update(kwargs) assert match.groupdict() == expected class URLTest(object): def assert_bad_url(self, validator, value, details=None): msg = "{0} is not a valid URL" with pytest.raises(ValueError) as cm: validator(value) if details: assert str(cm.value) == ". ".join((msg, details)).format(value) else: assert str(cm.value).startswith(msg.format(value)) @pytest.mark.parametrize( "url", [ "http://www.djangoproject.com/", "http://example.com/", "http://www.example.com/", "http://www.example.com/test", "http://valid-with-hyphens.com/", "http://subdomain.example.com/", "http://valid-----hyphens.com/", "http://example.com?something=value", "http://example.com/index.php?something=value&another=value2", ], ) def test_valid_values_default(self, url): validator = inputs.URL() assert validator(url) == url @pytest.mark.parametrize( "url", [ "foo", "http://", "http://example", "http://example.", "http://.com", "http://invalid-.com", "http://-invalid.com", "http://inv-.alid-.com", "http://inv-.-alid.com", "foo bar baz", "foo \u2713", "http://@foo:bar@example.com", "http://:bar@example.com", "http://bar:bar:bar@example.com", "http://300:300:300:300", "http://example.com:70000", "http://example.com:0000", ], ) def test_bad_urls(self, url): # Test with everything enabled to ensure bad URL are really detected validator = inputs.URL(ip=True, auth=True, port=True) self.assert_bad_url(validator, url) # msg = '{0} is not a valid URL'.format(url) # with pytest.raises(ValueError) as cm: # validator(url) # assert str(cm.exception).startswith(msg) @pytest.mark.parametrize( "url", [ "google.com", "domain.google.com", "kevin:pass@google.com/path?query", "google.com/path?\u2713", ], ) def test_bad_urls_with_suggestion(self, url): validator = inputs.URL() self.assert_bad_url(validator, url, "Did you mean: http://{0}") @pytest.mark.parametrize( "url", [ "http://200.8.9.10/", "http://foo:bar@200.8.9.10/", "http://200.8.9.10:8000/test", "http://2001:db8:85a3::8a2e:370:7334", "http://[1fff:0:a88:85a3::ac1f]:8001", ], ) def test_reject_ip(self, url): validator = inputs.URL() self.assert_bad_url(validator, url, "IP is not allowed") @pytest.mark.parametrize( "url", [ "http://200.8.9.10/", "http://200.8.9.10/test", "http://2001:db8:85a3::8a2e:370:7334", "http://[1fff:0:a88:85a3::ac1f]", ], ) def test_allow_ip(self, url): validator = inputs.URL(ip=True) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://foo:bar@200.8.9.10/", "http://foo:@2001:db8:85a3::8a2e:370:7334", "http://foo:bar@[1fff:0:a88:85a3::ac1f]:8001", "http://foo:@2001:db8:85a3::8a2e:370:7334", "http://foo2:qd1%r@example.com", ], ) def test_reject_auth(self, url): # Test with IP and port to ensure only auth is rejected validator = inputs.URL(ip=True, port=True) self.assert_bad_url(validator, url, "Authentication is not allowed") @pytest.mark.parametrize( "url", [ "http://foo:bar@example.com", "http://foo:@example.com", "http://foo@example.com", ], ) def test_allow_auth(self, url): validator = inputs.URL(auth=True) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://localhost", "http://127.0.0.1", "http://127.0.1.1", "http://::1", ], ) def test_reject_local(self, url): # Test with IP and port to ensure only auth is rejected validator = inputs.URL(ip=True) self.assert_bad_url(validator, url, "Localhost is not allowed") @pytest.mark.parametrize( "url", [ "http://localhost", "http://127.0.0.1", "http://127.0.1.1", "http://::1", ], ) def test_allow_local(self, url): validator = inputs.URL(ip=True, local=True) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://200.8.9.10:8080/", "http://foo:bar@200.8.9.10:8080/", "http://foo:bar@[1fff:0:a88:85a3::ac1f]:8001", ], ) def test_reject_port(self, url): # Test with auth and port to ensure only port is rejected validator = inputs.URL(ip=True, auth=True) self.assert_bad_url(validator, url, "Custom port is not allowed") @pytest.mark.parametrize( "url", [ "http://example.com:80", "http://example.com:8080", "http://www.example.com:8000/test", ], ) def test_allow_port(self, url): validator = inputs.URL(port=True) assert validator(url) == url @pytest.mark.parametrize( "url", [ "sip://somewhere.com", "irc://somewhere.com", ], ) def test_valid_restricted_schemes(self, url): validator = inputs.URL(schemes=("sip", "irc")) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://somewhere.com", "https://somewhere.com", ], ) def test_invalid_restricted_schemes(self, url): validator = inputs.URL(schemes=("sip", "irc")) self.assert_bad_url(validator, url, "Protocol is not allowed") @pytest.mark.parametrize( "url", [ "http://example.com", "http://example.com/test/", "http://www.example.com/", "http://www.example.com/test", ], ) def test_valid_restricted_domains(self, url): validator = inputs.URL(domains=["example.com", "www.example.com"]) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://somewhere.com", "https://somewhere.com", ], ) def test_invalid_restricted_domains(self, url): validator = inputs.URL(domains=["example.com", "www.example.com"]) self.assert_bad_url(validator, url, "Domain is not allowed") @pytest.mark.parametrize( "url", [ "http://somewhere.com", "https://somewhere.com", ], ) def test_valid_excluded_domains(self, url): validator = inputs.URL(exclude=["example.com", "www.example.com"]) assert validator(url) == url @pytest.mark.parametrize( "url", [ "http://example.com", "http://example.com/test/", "http://www.example.com/", "http://www.example.com/test", ], ) def test_excluded_domains(self, url): validator = inputs.URL(exclude=["example.com", "www.example.com"]) self.assert_bad_url(validator, url, "Domain is not allowed") def test_check(self): validator = inputs.URL(check=True, ip=True) assert ( validator("http://www.google.com") == "http://www.google.com" ), "Should check domain" # This test will fail on a network where this address is defined self.assert_bad_url( validator, "http://this-domain-should-not-exist.com", "Domain does not exists", ) def test_schema(self): assert inputs.URL().__schema__ == {"type": "string", "format": "url"} class UrlTest(object): @pytest.mark.parametrize( "url", [ "http://www.djangoproject.com/", "http://localhost/", "http://example.com/", "http://www.example.com/", "http://www.example.com:8000/test", "http://valid-with-hyphens.com/", "http://subdomain.example.com/", "http://200.8.9.10/", "http://200.8.9.10:8000/test", "http://valid-----hyphens.com/", "http://example.com?something=value", "http://example.com/index.php?something=value&another=value2", "http://foo:bar@example.com", "http://foo:@example.com", "http://foo@example.com", "http://foo:@2001:db8:85a3::8a2e:370:7334", "http://foo2:qd1%r@example.com", ], ) def test_valid_url(self, url): assert inputs.url(url) == url @pytest.mark.parametrize( "url", [ "foo", "http://", "http://example", "http://example.", "http://.com", "http://invalid-.com", "http://-invalid.com", "http://inv-.alid-.com", "http://inv-.-alid.com", "foo bar baz", "foo \u2713", "http://@foo:bar@example.com", "http://:bar@example.com", "http://bar:bar:bar@example.com", "http://300:300:300:300", "http://example.com:70000", ], ) def test_bad_url(self, url): with pytest.raises(ValueError) as cm: inputs.url(url) assert str(cm.value).startswith("{0} is not a valid URL".format(url)) @pytest.mark.parametrize( "url", [ "google.com", "domain.google.com", "kevin:pass@google.com/path?query", "google.com/path?\u2713", ], ) def test_bad_url_with_suggestion(self, url): with pytest.raises(ValueError) as cm: inputs.url(url) assert str( cm.value ) == "{0} is not a valid URL. Did you mean: http://{0}".format(url) def test_schema(self): assert inputs.url.__schema__ == {"type": "string", "format": "url"} class IPTest(object): @pytest.mark.parametrize( "value", [ "200.8.9.10", "127.0.0.1", "2001:db8:85a3::8a2e:370:7334", "::1", ], ) def test_valid_value(self, value): assert inputs.ip(value) == value @pytest.mark.parametrize( "value", [ "foo", "http://", "http://example", "http://example.", "http://.com", "http://invalid-.com", "http://-invalid.com", "http://inv-.alid-.com", "http://inv-.-alid.com", "foo bar baz", "foo \u2713", "http://@foo:bar@example.com", "http://:bar@example.com", "http://bar:bar:bar@example.com", "127.0", ], ) def test_bad_value(self, value): with pytest.raises(ValueError): inputs.ip(value) def test_schema(self): assert inputs.ip.__schema__ == {"type": "string", "format": "ip"} class IPv4Test(object): @pytest.mark.parametrize( "value", [ "200.8.9.10", "127.0.0.1", ], ) def test_valid_value(self, value): assert inputs.ipv4(value) == value @pytest.mark.parametrize( "value", [ "2001:db8:85a3::8a2e:370:7334", "::1", "foo", "http://", "http://example", "http://example.", "http://.com", "http://invalid-.com", "http://-invalid.com", "http://inv-.alid-.com", "http://inv-.-alid.com", "foo bar baz", "foo \u2713", "http://@foo:bar@example.com", "http://:bar@example.com", "http://bar:bar:bar@example.com", "127.0", ], ) def test_bad_value(self, value): with pytest.raises(ValueError): inputs.ipv4(value) def test_schema(self): assert inputs.ipv4.__schema__ == {"type": "string", "format": "ipv4"} class IPv6Test(object): @pytest.mark.parametrize( "value", [ "2001:db8:85a3::8a2e:370:7334", "::1", ], ) def test_valid_value(self, value): assert inputs.ipv6(value) == value @pytest.mark.parametrize( "value", [ "200.8.9.10", "127.0.0.1", "foo", "http://", "http://example", "http://example.", "http://.com", "http://invalid-.com", "http://-invalid.com", "http://inv-.alid-.com", "http://inv-.-alid.com", "foo bar baz", "foo \u2713", "http://@foo:bar@example.com", "http://:bar@example.com", "http://bar:bar:bar@example.com", "127.0", ], ) def test_bad_value(self, value): with pytest.raises(ValueError): inputs.ipv6(value) def test_schema(self): assert inputs.ipv6.__schema__ == {"type": "string", "format": "ipv6"} class EmailTest(object): def assert_bad_email(self, validator, value, msg=None): msg = msg or "{0} is not a valid email" with pytest.raises(ValueError) as cm: validator(value) assert str(cm.value) == msg.format(value) @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", "me@valid-with-hyphens.com", "me@subdomain.example.com", "me@sub.subdomain.example.com", "Loïc.Accentué@voilà.fr", ], ) def test_valid_value_default(self, value): validator = inputs.email() assert validator(value) == value @pytest.mark.parametrize( "value", [ "me@localhost", "me@127.0.0.1", "me@127.1.2.3", "me@::1", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", "foo@bar.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?" + ".?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?@", ], ) def test_invalid_value_default(self, value): self.assert_bad_email(inputs.email(), value) @pytest.mark.parametrize( "value", [ "test@gmail.com", "test@live.com", ], ) def test_valid_value_check(self, value): email = inputs.email(check=True) assert email(value) == value @pytest.mark.parametrize( "value", [ "coucou@not-found.fr", "me@localhost", "me@127.0.0.1", "me@127.1.2.3", "me@::1", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", ], ) def test_invalid_values_check(self, value): email = inputs.email(check=True) self.assert_bad_email(email, value) @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", "me@valid-with-hyphens.com", "me@subdomain.example.com", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", ], ) def test_valid_value_ip(self, value): email = inputs.email(ip=True) assert email(value) == value @pytest.mark.parametrize( "value", [ "me@localhost", "me@127.0.0.1", "me@127.1.2.3", "me@::1", ], ) def test_invalid_value_ip(self, value): email = inputs.email(ip=True) self.assert_bad_email(email, value) @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", "coucou@localhost", "me@valid-with-hyphens.com", "me@subdomain.example.com", "me@localhost", ], ) def test_valid_value_local(self, value): email = inputs.email(local=True) assert email(value) == value @pytest.mark.parametrize( "value", [ "me@127.0.0.1", "me@127.1.2.3", "me@::1", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", ], ) def test_invalid_value_local(self, value): email = inputs.email(local=True) self.assert_bad_email(email, value) @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", "coucou@localhost", "me@valid-with-hyphens.com", "me@subdomain.example.com", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", "me@localhost", "me@127.0.0.1", "me@127.1.2.3", "me@::1", ], ) def test_valid_value_ip_and_local(self, value): email = inputs.email(ip=True, local=True) assert email(value) == value @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", ], ) def test_valid_value_domains(self, value): email = inputs.email(domains=("gmail.com", "cmoi.fr")) assert email(value) == value @pytest.mark.parametrize( "value", [ "me@valid-with-hyphens.com", "me@subdomain.example.com", "me@localhost", "me@127.0.0.1", "me@127.1.2.3", "me@::1", "me@200.8.9.10", "me@2001:db8:85a3::8a2e:370:7334", ], ) def test_invalid_value_domains(self, value): email = inputs.email(domains=("gmail.com", "cmoi.fr")) self.assert_bad_email( email, value, "{0} does not belong to the authorized domains" ) @pytest.mark.parametrize( "value", [ "test@gmail.com", "coucou@cmoi.fr", "coucou+another@cmoi.fr", "Coucou@cmoi.fr", ], ) def test_valid_value_exclude(self, value): email = inputs.email(exclude=("somewhere.com", "foo.bar")) assert email(value) == value @pytest.mark.parametrize( "value", [ "me@somewhere.com", "me@foo.bar", ], ) def test_invalid_value_exclude(self, value): email = inputs.email(exclude=("somewhere.com", "foo.bar")) self.assert_bad_email(email, value, "{0} belongs to a forbidden domain") @pytest.mark.parametrize( "value", [ "someone@", "@somewhere", "email.somewhere.com", "[invalid!email]", "me.@somewhere", "me..something@somewhere", ], ) def test_bad_email(self, value): email = inputs.email() self.assert_bad_email(email, value) def test_schema(self): assert inputs.email().__schema__ == {"type": "string", "format": "email"} class RegexTest(object): @pytest.mark.parametrize( "value", [ "123", "1234567890", "00000", ], ) def test_valid_input(self, value): num_only = inputs.regex(r"^[0-9]+$") assert num_only(value) == value @pytest.mark.parametrize( "value", [ "abc", "123abc", "abc123", "", ], ) def test_bad_input(self, value): num_only = inputs.regex(r"^[0-9]+$") with pytest.raises(ValueError): num_only(value) def test_bad_pattern(self): with pytest.raises(re.error): inputs.regex("[") def test_schema(self): assert inputs.regex(r"^[0-9]+$").__schema__ == { "type": "string", "pattern": "^[0-9]+$", } class BooleanTest(object): def test_false(self): assert inputs.boolean("False") is False def test_0(self): assert inputs.boolean("0") is False def test_true(self): assert inputs.boolean("true") is True def test_1(self): assert inputs.boolean("1") is True def test_case(self): assert inputs.boolean("FaLSE") is False assert inputs.boolean("FaLSE") is False def test_python_bool(self): assert inputs.boolean(True) is True assert inputs.boolean(False) is False def test_bad_boolean(self): with pytest.raises(ValueError): inputs.boolean("blah") with pytest.raises(ValueError): inputs.boolean(None) def test_checkbox(self): assert inputs.boolean("on") is True def test_non_strings(self): assert inputs.boolean(0) is False assert inputs.boolean(1) is True assert inputs.boolean([]) is False def test_schema(self): assert inputs.boolean.__schema__ == {"type": "boolean"} class DateTest(object): def test_later_than_1900(self): assert inputs.date("1900-01-01") == datetime(1900, 1, 1) def test_error(self): with pytest.raises(ValueError): inputs.date("2008-13-13") def test_default(self): assert inputs.date("2008-08-01") == datetime(2008, 8, 1) def test_schema(self): assert inputs.date.__schema__ == {"type": "string", "format": "date"} class NaturalTest(object): def test_negative(self): with pytest.raises(ValueError): inputs.natural(-1) def test_default(self): assert inputs.natural(3) == 3 def test_string(self): with pytest.raises(ValueError): inputs.natural("foo") def test_schema(self): assert inputs.natural.__schema__ == {"type": "integer", "minimum": 0} class PositiveTest(object): def test_positive(self): assert inputs.positive(1) == 1 assert inputs.positive(10000) == 10000 def test_zero(self): with pytest.raises(ValueError): inputs.positive(0) def test_negative(self): with pytest.raises(ValueError): inputs.positive(-1) def test_schema(self): assert inputs.positive.__schema__ == { "type": "integer", "minimum": 0, "exclusiveMinimum": True, } class IntRangeTest(object): def test_valid_range(self): int_range = inputs.int_range(1, 5) assert int_range(3) == 3 def test_inclusive_range(self): int_range = inputs.int_range(1, 5) assert int_range(5) == 5 def test_lower(self): int_range = inputs.int_range(0, 5) with pytest.raises(ValueError): int_range(-1) def test_higher(self): int_range = inputs.int_range(0, 5) with pytest.raises(ValueError): int_range(6) def test_schema(self): assert inputs.int_range(1, 5).__schema__ == { "type": "integer", "minimum": 1, "maximum": 5, } interval_test_values = [ ( # Full precision with explicit UTC. "2013-01-01T12:30:00Z/P1Y2M3DT4H5M6S", ( datetime(2013, 1, 1, 12, 30, 0, tzinfo=pytz.utc), datetime(2014, 3, 5, 16, 35, 6, tzinfo=pytz.utc), ), ), ( # Full precision with alternate UTC indication "2013-01-01T12:30+00:00/P2D", ( datetime(2013, 1, 1, 12, 30, 0, tzinfo=pytz.utc), datetime(2013, 1, 3, 12, 30, 0, tzinfo=pytz.utc), ), ), ( # Implicit UTC with time "2013-01-01T15:00/P1M", ( datetime(2013, 1, 1, 15, 0, 0, tzinfo=pytz.utc), datetime(2013, 1, 31, 15, 0, 0, tzinfo=pytz.utc), ), ), ( # TZ conversion "2013-01-01T17:00-05:00/P2W", ( datetime(2013, 1, 1, 22, 0, 0, tzinfo=pytz.utc), datetime(2013, 1, 15, 22, 0, 0, tzinfo=pytz.utc), ), ), ( # Date upgrade to midnight-midnight period "2013-01-01/P3D", ( datetime(2013, 1, 1, 0, 0, 0, tzinfo=pytz.utc), datetime(2013, 1, 4, 0, 0, 0, 0, tzinfo=pytz.utc), ), ), ( # Start/end with UTC "2013-01-01T12:00:00Z/2013-02-01T12:00:00Z", ( datetime(2013, 1, 1, 12, 0, 0, tzinfo=pytz.utc), datetime(2013, 2, 1, 12, 0, 0, tzinfo=pytz.utc), ), ), ( # Start/end with time upgrade "2013-01-01/2013-06-30", ( datetime(2013, 1, 1, tzinfo=pytz.utc), datetime(2013, 6, 30, tzinfo=pytz.utc), ), ), ( # Start/end with TZ conversion "2013-02-17T12:00:00-07:00/2013-02-28T15:00:00-07:00", ( datetime(2013, 2, 17, 19, 0, 0, tzinfo=pytz.utc), datetime(2013, 2, 28, 22, 0, 0, tzinfo=pytz.utc), ), ), ( # Resolution expansion for single date(time) # Second with UTC "2013-01-01T12:30:45Z", ( datetime(2013, 1, 1, 12, 30, 45, tzinfo=pytz.utc), datetime(2013, 1, 1, 12, 30, 46, tzinfo=pytz.utc), ), ), ( # Second with tz conversion "2013-01-01T12:30:45+02:00", ( datetime(2013, 1, 1, 10, 30, 45, tzinfo=pytz.utc), datetime(2013, 1, 1, 10, 30, 46, tzinfo=pytz.utc), ), ), ( # Second with implicit UTC "2013-01-01T12:30:45", ( datetime(2013, 1, 1, 12, 30, 45, tzinfo=pytz.utc), datetime(2013, 1, 1, 12, 30, 46, tzinfo=pytz.utc), ), ), ( # Minute with UTC "2013-01-01T12:30+00:00", ( datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), datetime(2013, 1, 1, 12, 31, tzinfo=pytz.utc), ), ), ( # Minute with conversion "2013-01-01T12:30+04:00", ( datetime(2013, 1, 1, 8, 30, tzinfo=pytz.utc), datetime(2013, 1, 1, 8, 31, tzinfo=pytz.utc), ), ), ( # Minute with implicit UTC "2013-01-01T12:30", ( datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), datetime(2013, 1, 1, 12, 31, tzinfo=pytz.utc), ), ), ( # Hour, explicit UTC "2013-01-01T12Z", ( datetime(2013, 1, 1, 12, tzinfo=pytz.utc), datetime(2013, 1, 1, 13, tzinfo=pytz.utc), ), ), ( # Hour with offset "2013-01-01T12-07:00", ( datetime(2013, 1, 1, 19, tzinfo=pytz.utc), datetime(2013, 1, 1, 20, tzinfo=pytz.utc), ), ), ( # Hour with implicit UTC "2013-01-01T12", ( datetime(2013, 1, 1, 12, tzinfo=pytz.utc), datetime(2013, 1, 1, 13, tzinfo=pytz.utc), ), ), ( # Interval with trailing zero fractional seconds should # be accepted. "2013-01-01T12:00:00.0/2013-01-01T12:30:00.000000", ( datetime(2013, 1, 1, 12, tzinfo=pytz.utc), datetime(2013, 1, 1, 12, 30, tzinfo=pytz.utc), ), ), ] class IsoIntervalTest(object): @pytest.mark.parametrize("value,expected", interval_test_values) def test_valid_value(self, value, expected): assert inputs.iso8601interval(value) == expected def test_error_message(self): with pytest.raises(ValueError) as cm: inputs.iso8601interval("2013-01-01/blah") expected = "Invalid argument: 2013-01-01/blah. argument must be a valid ISO8601 date/time interval." assert str(cm.value) == expected @pytest.mark.parametrize( "value", [ "2013-01T14:", "", "asdf", "01/01/2013", ], ) def test_bad_values(self, value): with pytest.raises(ValueError): inputs.iso8601interval(value) def test_schema(self): assert inputs.iso8601interval.__schema__ == { "type": "string", "format": "iso8601-interval", }