# This program is free software; you can redistribute it and/or modify it under # the terms of the (LGPL) GNU Lesser General Public License as published by the # Free Software Foundation; either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Library Lesser General Public License # for more details at ( http://www.gnu.org/licenses/lgpl.html ). # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # written by: Jeff Ortel ( jortel@redhat.com ) """ Date & time related suds Python library unit tests. Implemented using the 'pytest' testing framework. """ if __name__ == "__main__": import __init__ __init__.runUsingPyTest(globals()) from suds.sax.date import (FixedOffsetTimezone, Date, DateTime, Time, UtcTimezone) from suds.xsd.sxbuiltin import XDate, XDateTime, XTime import tests import pytest import datetime class _Dummy: """Class for testing unknown object class handling.""" pass """Invalid date strings reused for both date & datetime testing.""" _invalid_date_strings = ( "", "abla", "12", "12-01", "-12-01", "1900-01", "+1900-10-01", # Plus sign not allowed. "1900-13-01", # Invalid month. "1900-02-30", # Invalid day. "2001-02-29", # Not a leap year. "2100-02-29", # Not a leap year. " 1900-01-01", "1900- 01-01", "1900-01 -01", "1900-01-01 ", "1900-13-011", "1900-01-01X", "1900-01-01T", # 'T' is a date/time separator for DateTime. # Invalid time zone indicators. "1900-01-01 +17:00", "1900-01-01+ 17:00", "1900-01-01*17:00", "1900-01-01 17:00", "1900-01-01+17:", "1900-01-01+170", "1900-01-01+1730", "1900-01-01+170:00", "1900-01-01+17:00:00", "1900-01-01-:4", "1900-01-01-2a:00", "1900-01-01-222:00", "1900-01-01-12:000" "1900-01-01+00:60", "1900-01-01-00:99") """Invalid date strings reused for both time & datetime testing.""" _invalid_time_strings = ( "", "bunga", "12", "::", "12:", "12:01", "12:01:", "12:01: 00", "12:01: 00", "23: 01:00", " 23:01:00", "23 :01:00", "23::00", "23:000:00", "023:00:00", "23:00:000", "25:01:00", "-1:01:00", "24:01:00", "23:-1:00", "23:61:00", "23:60:00", "23:59:-1", "23:59:61", "23:59:60", "7.59.13", "7-59-13", "-0:01:00", "23:-0:00", "23:59:-0", "23:59:6.a", "23:59:6.", "23:59:6:0", "23:59:6.12x", "23:59:6.12x45", "23:59:6.999999 ", "23:59:6.999999x", "T23:59:6", # Invalid time zone indicators. "13:27:04 -10:00", "13:27:04- 10:00", "13:27:04*17:00", "13:27:04 17:00", "13:27:04-003", "13:27:04-003:00", "13:27:04+00:002", "13:27:04-13:60", "13:27:04-121", "13:27:04-1210", "13:27:04-121:00", "13:27:04+12:", "13:27:04+12:00:00", "13:27:04-:13" "13:27:04-24:00" "13:27:04+99:00") class TestDate: """Tests for the suds.sax.date.Date class.""" def testConstructFromDate(self): date = datetime.date(2001, 12, 10) assert Date(date).value is date def testConstructFromDateTime_naive(self): date = datetime.datetime(2001, 12, 10, 10, 50, 21, 32132) assert Date(date).value == datetime.date(2001, 12, 10) @pytest.mark.parametrize("hours", (5, 20)) def testConstructFromDateTime_tzAware(self, hours): tz = FixedOffsetTimezone(10) date = datetime.datetime(2001, 12, 10, hours, 50, 21, 32132, tzinfo=tz) assert Date(date).value == datetime.date(2001, 12, 10) @pytest.mark.parametrize(("string", "y", "m", "d"), ( ("1900-01-01", 1900, 1, 1), ("1900-1-1", 1900, 1, 1), ("1900-01-01z", 1900, 1, 1), ("1900-01-01Z", 1900, 1, 1), ("1900-01-01-02", 1900, 1, 1), ("1900-01-01+2", 1900, 1, 1), ("1900-01-01+02:00", 1900, 1, 1), ("1900-01-01+99:59", 1900, 1, 1), ("1900-01-01-21:13", 1900, 1, 1), ("2000-02-29", 2000, 2, 29))) # Leap year. def testConstructFromString(self, string, y, m, d): assert Date(string).value == datetime.date(y, m, d) @pytest.mark.parametrize("string", _invalid_date_strings) def testConstructFromString_failure(self, string): pytest.raises(ValueError, Date, string) @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.time(10, 10))) def testConstructFromUnknown(self, source): pytest.raises(ValueError, Date, source) @pytest.mark.parametrize(("input", "output"), ( ("1900-01-01", "1900-01-01"), ("2000-02-29", "2000-02-29"), ("1900-1-1", "1900-01-01"), ("1900-01-01z", "1900-01-01"), ("1900-01-01Z", "1900-01-01"), ("1900-01-01-02", "1900-01-01"), ("1900-01-01+2", "1900-01-01"), ("1900-01-01+02:00", "1900-01-01"), ("1900-01-01+99:59", "1900-01-01"), ("1900-01-01-21:13", "1900-01-01"))) def testConvertToString(self, input, output): assert str(Date(input)) == output class TestDateTime: """Tests for the suds.sax.date.DateTime class.""" def testConstructFromDateTime(self): dt = datetime.datetime(2001, 12, 10, 1, 1) assert DateTime(dt).value is dt dt.replace(tzinfo=UtcTimezone()) assert DateTime(dt).value is dt @pytest.mark.parametrize( ("string", "y", "M", "d", "h", "m", "s", "micros"), ( ("2013-11-19T14:05:23.428068", 2013, 11, 19, 14, 5, 23, 428068), ("2013-11-19 14:05:23.4280", 2013, 11, 19, 14, 5, 23, 428000))) def testConstructFromString(self, string, y, M, d, h, m, s, micros): assert DateTime(string).value == datetime.datetime(y, M, d, h, m, s, micros) @pytest.mark.parametrize("string", [x + "T00:00:00" for x in _invalid_date_strings] + ["2000-12-31T" + x for x in _invalid_time_strings] + [ # Invalid date/time separator characters. "2013-11-1914:05:23.428068", "2013-11-19X14:05:23.428068"]) def testConstructFromString_failure(self, string): pytest.raises(ValueError, DateTime, string) @pytest.mark.parametrize( ("string", "y", "M", "d", "h", "m", "s", "micros"), ( ("2000-2-28T23:59:59.9999995", 2000, 2, 29, 0, 0, 0, 0), ("2000-2-29T23:59:59.9999995", 2000, 3, 1, 0, 0, 0, 0), ("2013-12-31T23:59:59.9999994", 2013, 12, 31, 23, 59, 59, 999999), ("2013-12-31T23:59:59.99999949", 2013, 12, 31, 23, 59, 59, 999999), ("2013-12-31T23:59:59.9999995", 2014, 1, 1, 0, 0, 0, 0))) def testConstructFromString_subsecondRounding(self, string, y, M, d, h, m, s, micros): ref = datetime.datetime(y, M, d, h, m, s, micros) assert DateTime(string).value == ref @pytest.mark.parametrize( ("string", "y", "M", "d", "h", "m", "s", "micros", "tz_h", "tz_m"), ( ("2013-11-19T14:05:23.428068-3", 2013, 11, 19, 14, 5, 23, 428068, -3, 0), ("2013-11-19T14:05:23.068+03", 2013, 11, 19, 14, 5, 23, 68000, 3, 0), ("2013-11-19T14:05:23.428068-02:00", 2013, 11, 19, 14, 5, 23, 428068, -2, 0), ("2013-11-19T14:05:23.428068+02:00", 2013, 11, 19, 14, 5, 23, 428068, 2, 0), ("2013-11-19T14:05:23.428068-23:59", 2013, 11, 19, 14, 5, 23, 428068, -23, -59))) def testConstructFromString_timezone(self, string, y, M, d, h, m, s, micros, tz_h, tz_m): tzdelta = datetime.timedelta(hours=tz_h, minutes=tz_m) tzinfo = FixedOffsetTimezone(tzdelta) ref = datetime.datetime(y, M, d, h, m, s, micros, tzinfo=tzinfo) assert DateTime(string).value == ref @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.date(2010, 10, 27), datetime.time(10, 10))) def testConstructFromUnknown(self, source): pytest.raises(ValueError, DateTime, source) @pytest.mark.parametrize(("input", "output"), ( ("2013-11-19T14:05:23.428068", "2013-11-19T14:05:23.428068"), ("2013-11-19 14:05:23.4280", "2013-11-19T14:05:23.428000"), ("2013-12-31T23:59:59.9999995", "2014-01-01T00:00:00"), ("2013-11-19T14:05:23.428068-3", "2013-11-19T14:05:23.428068-03:00"), ("2013-11-19T14:05:23.068+03", "2013-11-19T14:05:23.068000+03:00"), ("2013-11-19T14:05:23.4-02:00", "2013-11-19T14:05:23.400000-02:00"), ("2013-11-19T14:05:23.410+02:00", "2013-11-19T14:05:23.410000+02:00"), ("2013-11-19T14:05:23.428-23:59", "2013-11-19T14:05:23.428000-23:59"))) def testConvertToString(self, input, output): assert str(DateTime(input)) == output class TestTime: """Tests for the suds.sax.date.Time class.""" def testConstructFromTime(self): time = datetime.time(1, 1) assert Time(time).value is time time.replace(tzinfo=UtcTimezone()) assert Time(time).value is time @pytest.mark.parametrize(("string", "h", "m", "s", "micros"), ( ("10:59:47", 10, 59, 47, 0), ("9:9:13", 9, 9, 13, 0), ("18:0:09.2139", 18, 0, 9, 213900), ("18:0:09.02139", 18, 0, 9, 21390), ("18:0:09.002139", 18, 0, 9, 2139), ("0:00:00.00013", 0, 0, 0, 130), ("0:00:00.000001", 0, 0, 0, 1), ("0:00:00.000000", 0, 0, 0, 0), ("23:59:6.999999", 23, 59, 6, 999999), ("1:13:50.0", 1, 13, 50, 0))) def testConstructFromString(self, string, h, m, s, micros): assert Time(string).value == datetime.time(h, m, s, micros) @pytest.mark.parametrize("string", _invalid_time_strings) def testConstructFromString_failure(self, string): pytest.raises(ValueError, Time, string) @pytest.mark.parametrize(("string", "h", "m", "s", "micros"), ( ("0:0:0.0000000", 0, 0, 0, 0), ("0:0:0.0000001", 0, 0, 0, 0), ("0:0:0.0000004", 0, 0, 0, 0), ("0:0:0.0000005", 0, 0, 0, 1), ("0:0:0.0000006", 0, 0, 0, 1), ("0:0:0.0000009", 0, 0, 0, 1), ("0:0:0.5", 0, 0, 0, 500000), ("0:0:0.5000004", 0, 0, 0, 500000), ("0:0:0.5000005", 0, 0, 0, 500001), ("0:0:0.50000050", 0, 0, 0, 500001), ("0:0:0.50000051", 0, 0, 0, 500001), ("0:0:0.50000055", 0, 0, 0, 500001), ("0:0:0.50000059", 0, 0, 0, 500001), ("0:0:0.5000006", 0, 0, 0, 500001), ("0:0:0.9999990", 0, 0, 0, 999999), ("0:0:0.9999991", 0, 0, 0, 999999), ("0:0:0.9999994", 0, 0, 0, 999999), ("0:0:0.99999949", 0, 0, 0, 999999), ("0:0:0.9999995", 0, 0, 1, 0), ("0:0:0.9999996", 0, 0, 1, 0), ("0:0:0.9999999", 0, 0, 1, 0))) def testConstructFromString_subsecondRounding(self, string, h, m, s, micros): assert Time(string).value == datetime.time(h, m, s, micros) @pytest.mark.parametrize( ("string", "h", "m", "s", "micros", "tz_h", "tz_m"), ( ("18:0:09.2139z", 18, 0, 9, 213900, 0, 0), ("18:0:09.2139Z", 18, 0, 9, 213900, 0, 0), ("18:0:09.2139+3", 18, 0, 9, 213900, 3, 0), ("18:0:09.2139-3", 18, 0, 9, 213900, -3, 0), ("18:0:09.2139-03", 18, 0, 9, 213900, -3, 0), ("18:0:09.2139+9:3", 18, 0, 9, 213900, 9, 3), ("18:0:09.2139+10:31", 18, 0, 9, 213900, 10, 31), ("18:0:09.2139-10:31", 18, 0, 9, 213900, -10, -31))) def testConstructFromString_timezone(self, string, h, m, s, micros, tz_h, tz_m): tzdelta = datetime.timedelta(hours=tz_h, minutes=tz_m) tzinfo = FixedOffsetTimezone(tzdelta) ref = datetime.time(h, m, s, micros, tzinfo=tzinfo) assert Time(string).value == ref @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.date(2010, 10, 27), datetime.datetime(2010, 10, 27, 10, 10))) def testConstructFromUnknown(self, source): pytest.raises(ValueError, Time, source) @pytest.mark.parametrize(("input", "output"), ( ("14:05:23.428068", "14:05:23.428068"), ("14:05:23.4280", "14:05:23.428000"), ("23:59:59.9999995", "00:00:00"), ("14:05:23.428068-3", "14:05:23.428068-03:00"), ("14:05:23.068+03", "14:05:23.068000+03:00"), ("14:05:23.4-02:00", "14:05:23.400000-02:00"), ("14:05:23.410+02:00", "14:05:23.410000+02:00"), ("14:05:23.428-23:59", "14:05:23.428000-23:59"))) def testConvertToString(self, input, output): assert str(Time(input)) == output class TestXDate: """ Tests for the suds.xsd.sxbuiltin.XDate class. Python object <--> string conversion details already tested in TestDate. """ def testTranslateEmptyStringToPythonObject(self): assert XDate.translate("") == None def testTranslateStringToPythonObject(self): assert XDate.translate("1941-12-7") == datetime.date(1941, 12, 7) def testTranslatePythonObjectToString(self): date = datetime.date(2013, 7, 24) translated = XDate.translate(date, topython=False) assert isinstance(translated, str) assert translated == "2013-07-24" def testTranslatePythonObjectToString_datetime(self): dt = datetime.datetime(2013, 7, 24, 11, 59, 4) translated = XDate.translate(dt, topython=False) assert isinstance(translated, str) assert translated == "2013-07-24" @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.time())) def testTranslatePythonObjectToString_failed(self, source): assert XDate.translate(source, topython=False) is source class TestXDateTime: """ Tests for the suds.xsd.sxbuiltin.XDateTime class. Python object <--> string conversion details already tested in TestDateTime. """ def testTranslateEmptyStringToPythonObject(self): assert XDateTime.translate("") == None def testTranslateStringToPythonObject(self): dt = datetime.datetime(1941, 12, 7, 10, 30, 22, 454000) assert XDateTime.translate("1941-12-7T10:30:22.454") == dt def testTranslatePythonObjectToString(self): dt = datetime.datetime(2021, 12, 31, 11, 25, tzinfo=UtcTimezone()) translated = XDateTime.translate(dt, topython=False) assert isinstance(translated, str) assert translated == "2021-12-31T11:25:00+00:00" @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.time(22, 47, 9, 981), datetime.date(2101, 1, 1))) def testTranslatePythonObjectToString_failed(self, source): assert XDateTime.translate(source, topython=False) is source class TestXTime: """ Tests for the suds.xsd.sxbuiltin.XTime class. Python object <--> string conversion details already tested in TestDateTime. """ def testTranslateEmptyStringToPythonObject(self): assert XTime.translate("") == None def testTranslateStringToPythonObject(self): assert XTime.translate("10:30:22") == datetime.time(10, 30, 22) def testTranslatePythonObjectToString(self): time = datetime.time(16, 53, 12, tzinfo=FixedOffsetTimezone(4)) translated = XTime.translate(time, topython=False) assert isinstance(translated, str) assert translated == "16:53:12+04:00" @pytest.mark.parametrize("source", ( None, object(), _Dummy(), datetime.date(2101, 1, 1), datetime.datetime(2101, 1, 1, 22, 47, 9, 981))) def testTranslatePythonObjectToString_failed(self, source): assert XTime.translate(source, topython=False) is source