def test_complete_deserialize_fails_if_invalid_schedule(self):
        schema = JobSchema()
        data = {
            'taskDefinition':
            'test-task',
            'schedule':
            'bad-schedule',
            'scheduleStart':
            '2015-10-07T13:44:53.123456+00:00',
            'scheduleEnd':
            '2015-10-10T04:10:03.654321+00:00',
            'taskCount':
            12,
            'suspended':
            True,
            'trigger': {
                'type': 'test-type'
            },
            'overrides': [{
                'containerName': 'test-container',
                'environment': {
                    'foo': 'foo_value',
                    'bar': 'bar_value',
                    'baz': 'baz_value'
                }
            }]
        }

        job, errors = schema.load(data)

        self.assertEqual({'parsedSchedule'}, errors.keys())
    def test_deserialize_fails_if_invalid_timezone(self):
        schema = JobSchema()
        data = {'timezone': 'FOO'}

        job, errors = schema.load(data)

        self.assertEqual({'timezone'}, errors.keys())
    def test_deserialize_fails_if_task_definition_includes_revision(self):
        schema = JobSchema()
        data = {'taskDefinition': 'foo:3', 'schedule': '*'}

        job, errors = schema.load(data)

        self.assertEqual({'taskDefinition'}, errors.keys())
    def test_deserialize_fails_if_above_max_task_count(self):
        schema = JobSchema()
        data = {'taskCount': 51, 'maxCount': 51}

        job, errors = schema.load(data)

        self.assertEqual({'taskCount', 'maxCount'}, errors.keys())
    def test_deserialize_does_not_support_wildcards_for_other_fields(self):
        schema = JobSchema()
        data = {'schedule': '0 0 0 ? ? ? ? ?'}

        job, errors = schema.load(data)

        self.assertEqual({'parsedSchedule'}, errors.keys())
    def test_deserialize_fails_if_bad_trigger(self):
        schema = JobSchema()
        data = {'schedule': '*', 'trigger': {}}

        job, errors = schema.load(data)

        self.assertEqual({'trigger'}, errors.keys())
    def test_simple_deserialize(self):
        schema = JobSchema()
        data = {}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertEqual({}, job)
    def test_deserialize_fails_if_task_definition_includes_invalid_characters(
            self):
        schema = JobSchema()
        data = {'taskDefinition': 'foo/bar.baz', 'schedule': '*'}

        job, errors = schema.load(data)

        self.assertEqual({'taskDefinition'}, errors.keys())
    def test_deserialize_succeeds_with_empty_overrides(self):
        schema = JobSchema()
        data = {'overrides': []}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertEqual({'overrides': []}, job)
    def test_deserialize_ignores_parsed_schedule_on_input(self):
        schema = JobSchema()
        data = {'schedule': '*', 'parsedSchedule': 'foobar'}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertEqual({'second': '*'}, job['parsedSchedule'])
    def test_deserialize_succeeds_if_max_less_than_task_count(self):
        schema = JobSchema()
        data = {'taskCount': 21, 'maxCount': 20}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertEqual(21, job['taskCount'])
        self.assertEqual(20, job['maxCount'])
    def test_deserialize_succeeds_if_at_max_task_count(self):
        schema = JobSchema()
        data = {'taskCount': 50, 'maxCount': 50}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertEqual(50, job['taskCount'])
        self.assertEqual(50, job['maxCount'])
    def test_deserialize_uses_underscore_to_denote_spaces(self):
        schema = JobSchema()
        data = {'schedule': '0 0 0 * * 2nd_wed'}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        expected_data = {
            'second': '0',
            'minute': '0',
            'hour': '0',
            'day_of_week': '*',
            'week': '*',
            'day': '2nd wed'
        }
        self.assertEqual(expected_data, job['parsedSchedule'])
    def test_deserialize_supports_timezone_offsets(self):
        schema = JobSchema()
        data = {
            'scheduleStart': '2015-10-07T13:44:53.123456+10:00',
            'scheduleEnd': '2015-10-10T04:10:03.654321-06:00',
            'timezone': 'US/Pacific'
        }

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))

        expected_data = data.copy()
        _parse_datetime_fields(expected_data, 'scheduleStart', 'scheduleEnd')

        self.assertEqual(expected_data, job)
    def test_deserialize_supports_wildcards_combined_with_other_fields(self):
        schema = JobSchema()
        data = {'schedule': '? ? ? sun 34 last 2 2012-2015'}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertTrue(0 <= int(job['parsedSchedule']['second']) < 60,
                        'wildcard second not in expected range')
        self.assertTrue(0 <= int(job['parsedSchedule']['minute']) < 60,
                        'wildcard minute not in expected range')
        self.assertTrue(0 <= int(job['parsedSchedule']['hour']) < 24,
                        'wildcard hour not in expected range')
        expected_expression = ' '.join(
            (job['parsedSchedule']['second'], job['parsedSchedule']['minute'],
             job['parsedSchedule']['hour'], 'sun 34 last 2 2012-2015'))
        self.assertEqual(expected_expression, job['schedule'])
    def test_deserialize_supports_wildcards_for_secminhour(self):
        schema = JobSchema()
        data = {'schedule': '? ? ?'}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        self.assertTrue(0 <= int(job['parsedSchedule']['second']) < 60,
                        'wildcard second not in expected range')
        self.assertTrue(0 <= int(job['parsedSchedule']['minute']) < 60,
                        'wildcard minute not in expected range')
        self.assertTrue(0 <= int(job['parsedSchedule']['hour']) < 24,
                        'wildcard hour not in expected range')
        expected_expression = ' '.join(
            (job['parsedSchedule']['second'], job['parsedSchedule']['minute'],
             job['parsedSchedule']['hour']))
        self.assertEqual(expected_expression, job['schedule'])
    def test_deserialize_parses_full_schedule(self):
        schema = JobSchema()
        data = {'schedule': '10 12 22-23 sun 34 last 2 2012-2015'}

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))
        expected_data = {
            'second': '10',
            'minute': '12',
            'hour': '22-23',
            'day_of_week': 'sun',
            'week': '34',
            'day': 'last',
            'month': '2',
            'year': '2012-2015'
        }
        self.assertEqual(expected_data, job['parsedSchedule'])
    def test_complete_deserialize(self):
        schema = JobSchema()
        data = {
            'taskDefinition':
            'test-task',
            'schedule':
            '*',
            'scheduleStart':
            '2015-10-07T13:44:53.123456+00:00',
            'scheduleEnd':
            '2015-10-10T04:10:03.654321+00:00',
            'timezone':
            'UTC',
            'taskCount':
            12,
            'maxCount':
            20,
            'suspended':
            True,
            'trigger': {
                'type': 'test-type'
            },
            'overrides': [{
                'containerName': 'test-container',
                'environment': {
                    'foo': 'foo_value',
                    'bar': 'bar_value',
                    'baz': 'baz_value'
                }
            }]
        }

        job, errors = schema.load(data)

        self.assertEqual(0, len(errors))

        expected_data = data.copy()
        _parse_datetime_fields(expected_data, 'scheduleStart', 'scheduleEnd')

        self.assertEqual(expected_data, job)