Ejemplo n.º 1
0
def pomodoro_in_progress_with_breaks_and_pauses(task_instance):
    pomodoro_date_frame = factory.create(klass=DateFrameFactory,
                                         task=task_instance,
                                         end=None,
                                         frame_type=0)
    factory.create(klass=InnerDateFrameFactory,
                   task=task_instance,
                   frame_type=1,
                   start=get_time_delta({'minutes': 2}),
                   end=get_time_delta({'minutes': 4}))
    factory.create(klass=InnerDateFrameFactory,
                   task=task_instance,
                   frame_type=1,
                   start=get_time_delta({'minutes': 5}),
                   end=get_time_delta({'minutes': 7}))
    factory.create(klass=InnerDateFrameFactory,
                   task=task_instance,
                   frame_type=2,
                   start=get_time_delta({'minutes': 10}),
                   end=get_time_delta({'minutes': 12}))
    factory.create(klass=InnerDateFrameFactory,
                   task=task_instance,
                   frame_type=2,
                   start=get_time_delta({'minutes': 15}),
                   end=get_time_delta({'minutes': 19}))
    return pomodoro_date_frame
Ejemplo n.º 2
0
    def test_finish_date_frame_with_start_greater_than_end(self, mock_datetime_now, tested_date_frame, task_instance):
        mock_datetime_now.now.return_value = get_time_delta({'minutes': 20}, ahead=False)

        with pytest.raises(ValidationError) as exc:
            finish_date_frame(date_frame_id=tested_date_frame.id)

        assert exc.value.messages[0] == DateFrameException.messages[DateFrameException.start_greater_than_end]
Ejemplo n.º 3
0
def date_frame_in_progress_for_yesterday(task_instance):
    date_frame_instance = factory.create(klass=DateFrameFactory,
                                         task=task_instance,
                                         start=get_time_delta({'days': 1},
                                                              ahead=False),
                                         end=None)
    return date_frame_instance
class TestTodayValidator:
    @pytest.mark.parametrize('tested_date', [
        get_time_delta({'days': 0}),
        get_time_delta({'days': 1}),
        get_time_delta({'days': 5}),
    ])
    def test_validate_today_with_valid_dates(self, tested_date):
        today_validator(value=tested_date)

    @pytest.mark.parametrize('tested_date', [
        get_time_delta({'days': 1}, ahead=False),
        get_time_delta({'days': 5}, ahead=False),
    ])
    def test_validate_today_with_invalid_dates(self, tested_date):
        with pytest.raises(ValidationError):
            today_validator(value=tested_date)
Ejemplo n.º 5
0
    def test_finish_date_frame_with_breaks_and_pauses_saves_proper_duration(self, mock_timezone,
                                                                            pomodoro_in_progress_with_breaks_and_pauses):
        mock_timezone.now.return_value = get_time_delta({'minutes': 25}, ahead=True)

        finish_date_frame(date_frame_id=pomodoro_in_progress_with_breaks_and_pauses.id)

        pomodoro_in_progress_with_breaks_and_pauses.refresh_from_db()

        break_frames = get_breaks_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks_and_pauses,
            end=pomodoro_in_progress_with_breaks_and_pauses.end).values('start', 'end')

        breaks_duration = reduce(operator.add,
                                 (break_frame['end'] - break_frame['start'] for break_frame in break_frames),
                                 timedelta(0))

        pause_frames = get_pauses_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks_and_pauses,
            end=pomodoro_in_progress_with_breaks_and_pauses.end).values('start', 'end')

        pauses_duration = reduce(operator.add,
                                 (pause_frame['end'] - pause_frame['start'] for pause_frame in pause_frames),
                                 timedelta(0))

        breaks_and_pauses_duration = breaks_duration + pauses_duration
        expected_duration = math.trunc(
            (pomodoro_in_progress_with_breaks_and_pauses.end - pomodoro_in_progress_with_breaks_and_pauses.start -
             breaks_and_pauses_duration).seconds / 60)

        assert round(breaks_and_pauses_duration.seconds) // 60 == 10
        assert pomodoro_in_progress_with_breaks_and_pauses.end is not None
        assert pomodoro_in_progress_with_breaks_and_pauses.duration == timedelta(minutes=expected_duration)
Ejemplo n.º 6
0
def test_update_form_blocked_until_invalid_datetime(user_model, user_data,
                                                    active_user):
    user_data["blocked_until"] = get_time_delta({"days": 1}, ahead=False)
    user_data["date_joined"] = active_user.date_joined
    form = AdminSiteUserUpdateForm(instance=active_user, data=user_data)

    assert not form.is_valid()

    assert "blocked_until" in form.errors
    assert form.errors["blocked_until"][
        0] == "This datetime value cannot be lower than the actual date and time."
Ejemplo n.º 7
0
def test_create_form_blocked_until_invalid_datetime(user_model,
                                                    user_registration_data):
    user_registration_data["blocked_until"] = get_time_delta({"days": 1},
                                                             ahead=False)
    form = AdminSiteUserCreationForm(user_registration_data)

    assert not form.is_valid()

    assert "blocked_until" in form.errors
    assert form.errors["blocked_until"][
        0] == "This datetime value cannot be lower than the actual date and time."
Ejemplo n.º 8
0
def obsolete_date_frames(task_instance):
    obsolete_pomodoro = factory.create(klass=DateFrameFactory,
                                       task=task_instance,
                                       start=get_time_delta({'days': 8},
                                                            ahead=False),
                                       frame_type=0,
                                       end=None)
    obsolete_break = factory.create(klass=DateFrameFactory,
                                    task=task_instance,
                                    start=get_time_delta({'days': 8},
                                                         ahead=False),
                                    frame_type=1,
                                    end=None)
    obsolete_pause = factory.create(klass=DateFrameFactory,
                                    task=task_instance,
                                    start=get_time_delta({'days': 8},
                                                         ahead=False),
                                    frame_type=2,
                                    end=None)
    return obsolete_pomodoro, obsolete_break, obsolete_pause
def test_jwt_verify_serializer_with_blocked_user(json_web_token, active_user):
    active_user.blocked_until = get_time_delta({"days": 1})
    active_user.save()

    serializer = CustomVerificationBaseSerializer(data={
        'token': json_web_token
    })

    assert serializer.is_valid() is False
    assert serializer.errors['non_field_errors'][0] == \
           'User account is currently blocked.'
Ejemplo n.º 10
0
def test_authentication_fails_for_blocked_user(json_web_token, active_user):
    active_user.blocked_until = get_time_delta({"days": 1})
    active_user.save()
    payload = jwt_decode_handler(json_web_token)

    jwt_authentication_class = CustomJWTWebTokenAuthentication()

    with pytest.raises(AuthenticationFailed) as exc:
        jwt_authentication_class.authenticate_credentials(payload=payload)

    assert exc.value.args[0] == 'User account is currently blocked.'
Ejemplo n.º 11
0
def test_create_form_blocked_until_valid_datetime(user_model,
                                                  user_registration_data):
    user_registration_data["blocked_until"] = get_time_delta({"days": 1})
    form = AdminSiteUserCreationForm(user_registration_data)

    assert form.is_valid()

    user_instance = form.save()

    assert user_instance.blocked_until is not None
    assert user_instance in user_model.objects.blocked_standard_users()
Ejemplo n.º 12
0
    def test_custom_refresh_and_verify_jwt_token_with_blocked_user(
            self, view_url, json_web_token, request_factory, active_user):
        active_user.blocked_until = get_time_delta({"days": 1})
        active_user.save()

        request = request_factory.post(view_url, {'token': json_web_token})
        response = custom_verify_jwt_token(request)

        assert response.status_code == 400
        assert response.data['non_field_errors'][0] == \
               "User account is currently blocked."
Ejemplo n.º 13
0
def test_update_form_blocked_until_valid_datetime(user_model, user_data,
                                                  active_user):
    user_data["blocked_until"] = get_time_delta({"days": 1})
    user_data["date_joined"] = active_user.date_joined
    form = AdminSiteUserUpdateForm(instance=active_user, data=user_data)

    assert form.is_valid()

    user_instance = form.save()

    assert user_instance.blocked_until is not None
    assert user_instance in user_model.objects.blocked_standard_users()
Ejemplo n.º 14
0
    def test_get_breaks_inside_date_frame(self,
                                          pomodoro_in_progress_with_breaks):
        finish_date = get_time_delta({'minutes': 25})
        selector_method_result = get_breaks_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks,
            end=finish_date)

        assert selector_method_result.count() == 2
        assert all(date_frame.start > pomodoro_in_progress_with_breaks.start
                   for date_frame in selector_method_result)
        assert all(date_frame.end < finish_date
                   for date_frame in selector_method_result)
Ejemplo n.º 15
0
class TestStartDateFrame:

    @pytest.mark.parametrize(
        'date_frame_type',
        [0, 1, 2]
    )
    def test_start_date_frame_with_valid_start_datetime(self, date_frame_type, task_instance):
        assert task_instance.frames.count() == 0
        start_date_frame(task_id=task_instance.id, frame_type=date_frame_type)
        assert task_instance.frames.count() == 1

    @pytest.mark.parametrize(
        'date_frame_type, started_date_frame, nearest_possible_date',
        [
            (0, lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 26})),
            (0, lazy_fixture('break_in_progress'), get_time_delta({'minutes': 16})),
            (0, lazy_fixture('pause_in_progress'), get_time_delta({'minutes': 1})),
            (1, lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 26})),
            (1, lazy_fixture('break_in_progress'), get_time_delta({'minutes': 16})),
            (1, lazy_fixture('pause_in_progress'), get_time_delta({'minutes': 1}))
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_start_date_frame_finishes_current_date_frame(self, mock_timezone, date_frame_type, started_date_frame,
                                                          nearest_possible_date, task_instance):
        mock_timezone.now.return_value = nearest_possible_date

        assert started_date_frame.end is None
        assert task_instance.frames.count() == 1
        start_date_frame(task_id=task_instance.id, frame_type=date_frame_type)

        started_date_frame.refresh_from_db()
        assert started_date_frame.end is not None
        assert task_instance.frames.count() == 2

    @pytest.mark.parametrize(
        'date_frame_type',
        [0, 1, 2]
    )
    def test_start_date_frame_on_completed_task(self, date_frame_type, completed_task_instance):
        with pytest.raises(ValidationError) as exc:
            start_date_frame(task_id=completed_task_instance.id, frame_type=date_frame_type)

        assert exc.value.messages[0] == DateFrameException.messages[DateFrameException.task_already_completed]
def get_obsolete_date_frames():
    #  Returns date frames that have been created at least one week ago
    return models.DateFrame.objects.filter(is_finished=False, start__date__lt=get_time_delta({'days': 7}, ahead=False))
Ejemplo n.º 17
0
class TestFinishDateFrame:
    @pytest.mark.parametrize(
        'tested_date_frame',
        [
            lazy_fixture('pomodoro_in_progress'),
            lazy_fixture('break_in_progress'),
            lazy_fixture('pause_in_progress')
        ]
    )
    def test_finish_date_frame_with_valid_end_datetime(self, tested_date_frame):
        finish_date_frame(date_frame_id=tested_date_frame.id)

        tested_date_frame.refresh_from_db()
        assert tested_date_frame.end is not None and tested_date_frame.duration is not None

    @pytest.mark.parametrize(
        'tested_date_frame',
        [
            lazy_fixture('pomodoro_in_progress'),
            lazy_fixture('break_in_progress'),
            lazy_fixture('pause_in_progress')
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_with_start_greater_than_end(self, mock_datetime_now, tested_date_frame, task_instance):
        mock_datetime_now.now.return_value = get_time_delta({'minutes': 20}, ahead=False)

        with pytest.raises(ValidationError) as exc:
            finish_date_frame(date_frame_id=tested_date_frame.id)

        assert exc.value.messages[0] == DateFrameException.messages[DateFrameException.start_greater_than_end]

    @pytest.mark.parametrize(
        'tested_date_frame',
        [
            lazy_fixture('pomodoro_in_progress'),
            lazy_fixture('break_in_progress'),
            lazy_fixture('pause_in_progress')
        ]
    )
    def test_finish_date_frame_on_completed_task(self, tested_date_frame, task_instance):
        task_instance.status = 1
        task_instance.save()

        with pytest.raises(ValidationError) as exc:
            finish_date_frame(date_frame_id=tested_date_frame.id)
        assert exc.value.messages[0] == DateFrameException.messages[DateFrameException.task_already_completed]

    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_with_breaks_saves_proper_duration(self, mock_timezone, pomodoro_in_progress_with_breaks):
        mock_timezone.now.return_value = get_time_delta({'minutes': 25})
        finish_date_frame(date_frame_id=pomodoro_in_progress_with_breaks.id)
        pomodoro_in_progress_with_breaks.refresh_from_db()

        break_frames = get_breaks_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks,
            end=pomodoro_in_progress_with_breaks.end).values('start', 'end')

        breaks_duration = reduce(operator.add,
                                 (break_frame['end'] - break_frame['start'] for break_frame in break_frames),
                                 timedelta(0))

        expected_duration = math.trunc(
            (pomodoro_in_progress_with_breaks.end - pomodoro_in_progress_with_breaks.start -
             breaks_duration).seconds / 60)

        assert pomodoro_in_progress_with_breaks.end is not None
        assert pomodoro_in_progress_with_breaks.duration == timedelta(minutes=expected_duration)

    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_with_pauses_saves_proper_duration(self, mock_timezone, pomodoro_in_progress_with_pauses,
                                                                 task_instance):
        mock_timezone.now.return_value = get_time_delta({'minutes': 25}, ahead=True)
        finish_date_frame(date_frame_id=pomodoro_in_progress_with_pauses.id)
        pomodoro_in_progress_with_pauses.refresh_from_db()

        pause_frames = get_pauses_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_pauses,
            end=pomodoro_in_progress_with_pauses.end).values('start', 'end')

        pauses_duration = reduce(operator.add,
                                 (break_frame['end'] - break_frame['start'] for break_frame in pause_frames),
                                 timedelta(0))

        expected_duration = math.trunc(
            (pomodoro_in_progress_with_pauses.end - pomodoro_in_progress_with_pauses.start -
             pauses_duration).seconds / 60)

        assert pomodoro_in_progress_with_pauses.end is not None
        assert pomodoro_in_progress_with_pauses.duration == timedelta(minutes=expected_duration)

    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_with_breaks_and_pauses_saves_proper_duration(self, mock_timezone,
                                                                            pomodoro_in_progress_with_breaks_and_pauses):
        mock_timezone.now.return_value = get_time_delta({'minutes': 25}, ahead=True)

        finish_date_frame(date_frame_id=pomodoro_in_progress_with_breaks_and_pauses.id)

        pomodoro_in_progress_with_breaks_and_pauses.refresh_from_db()

        break_frames = get_breaks_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks_and_pauses,
            end=pomodoro_in_progress_with_breaks_and_pauses.end).values('start', 'end')

        breaks_duration = reduce(operator.add,
                                 (break_frame['end'] - break_frame['start'] for break_frame in break_frames),
                                 timedelta(0))

        pause_frames = get_pauses_inside_date_frame(
            date_frame_object=pomodoro_in_progress_with_breaks_and_pauses,
            end=pomodoro_in_progress_with_breaks_and_pauses.end).values('start', 'end')

        pauses_duration = reduce(operator.add,
                                 (pause_frame['end'] - pause_frame['start'] for pause_frame in pause_frames),
                                 timedelta(0))

        breaks_and_pauses_duration = breaks_duration + pauses_duration
        expected_duration = math.trunc(
            (pomodoro_in_progress_with_breaks_and_pauses.end - pomodoro_in_progress_with_breaks_and_pauses.start -
             breaks_and_pauses_duration).seconds / 60)

        assert round(breaks_and_pauses_duration.seconds) // 60 == 10
        assert pomodoro_in_progress_with_breaks_and_pauses.end is not None
        assert pomodoro_in_progress_with_breaks_and_pauses.duration == timedelta(minutes=expected_duration)

    @pytest.mark.parametrize(
        'tested_date_frame, tested_date_frame_length, expected_error_code',
        [
            (lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 55}),
             DateFrameException.invalid_pomodoro_length),
            (lazy_fixture('break_in_progress'), get_time_delta({'minutes': 35}),
             DateFrameException.invalid_break_length)
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_with_longer_than_specified_duration(self, mock_timezone, tested_date_frame,
                                                                   expected_error_code, tested_date_frame_length):
        mock_timezone.now.return_value = tested_date_frame_length

        with pytest.raises(ValidationError) as exc:
            finish_date_frame(date_frame_id=tested_date_frame.id)
        assert exc.value.messages[0] == DateFrameException.messages[expected_error_code]

    @pytest.mark.parametrize(
        'tested_date_frame, tested_date_frame_length, normalized_date_frame_duration',
        [
            (lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 25, 'seconds': 40}),
             timedelta(minutes=25)),
            (lazy_fixture('break_in_progress'), get_time_delta({'minutes': 15, 'seconds': 40}),
             timedelta(minutes=15))
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_finish_date_frame_fits_within_length_error_margin(self, mock_timezone, tested_date_frame,
                                                               normalized_date_frame_duration,
                                                               tested_date_frame_length):
        mock_timezone.now.return_value = tested_date_frame_length
        finish_date_frame(date_frame_id=tested_date_frame.id)
        tested_date_frame.refresh_from_db()

        assert tested_date_frame.end is not None
        assert tested_date_frame.duration == normalized_date_frame_duration

    @pytest.mark.parametrize(
        'tested_date_frame, end_date',
        [
            (lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 23})),
            (lazy_fixture('break_in_progress'), get_time_delta({'minutes': 12}))
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_force_finish_date_frame_with_proper_end_date(self, mock_timezone, tested_date_frame, end_date):
        mock_timezone.now.return_value = end_date

        finished_date_frame = force_finish_date_frame(date_frame=tested_date_frame)

        assert finished_date_frame.end == end_date

    @pytest.mark.parametrize(
        'tested_date_frame, end_date',
        [
            (lazy_fixture('pomodoro_in_progress'), get_time_delta({'minutes': 51})),
            (lazy_fixture('break_in_progress'), get_time_delta({'minutes': 31}))
        ]
    )
    @patch('pomodorr.frames.services.date_frame_service.timezone')
    def test_force_finish_date_frame_with_end_date_exceeding_estimated_date(self, mock_timezone, tested_date_frame,
                                                                            end_date):
        mock_timezone.now.return_value = end_date

        finished_date_frame = force_finish_date_frame(date_frame=tested_date_frame)

        assert finished_date_frame.end < end_date

    def test_force_finish_pause_finishes_related_pomodoro(self, pause_in_progress_with_ongoing_pomodoro):
        pause, pomodoro = pause_in_progress_with_ongoing_pomodoro

        force_finish_date_frame(date_frame=pause)

        pause.refresh_from_db()
        pomodoro.refresh_from_db()

        assert pause.end is not None
        assert pomodoro.end is not None
        assert pause.end < pomodoro.end
Ejemplo n.º 18
0
def test_get_negative_time_delta(mock_timezone):
    mock_timezone.now.return_value = datetime(2020, 1, 15, tzinfo=timezone.utc)

    utc_time_tomorrow = get_time_delta({'days': 1}, ahead=False)

    assert utc_time_tomorrow == datetime(2020, 1, 14, tzinfo=timezone.utc)
Ejemplo n.º 19
0
def ready_to_unblock_user():
    return UserFactory.create(is_active=True,
                              blocked_until=get_time_delta({"days": 1},
                                                           ahead=False))
Ejemplo n.º 20
0
def blocked_user(blocked_user_data):
    return UserFactory.create(is_active=True,
                              blocked_until=get_time_delta({"days": 1}),
                              **blocked_user_data)
class TestTaskSerializer:
    serializer_class = TaskSerializer

    def test_serialize_many_tasks(self, task_model, task_instance,
                                  completed_task_instance):
        serializer = self.serializer_class(instance=task_model.objects.all(),
                                           many=True)

        assert serializer.data is not None
        assert len(serializer.data) == 2

    def test_serializer_single_task(self, task_instance):
        serializer = self.serializer_class(instance=task_instance)

        assert serializer.data is not None
        assert serializer.data['id'] == str(task_instance.id)

    def test_save_task_with_valid_data(self, task_data, request_mock,
                                       project_instance, priority_instance):
        task_data['project'] = project_instance.id
        task_data['priority'] = priority_instance.id

        serializer = self.serializer_class(data=task_data)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        assert serializer.validated_data is not None

    @pytest.mark.parametrize(
        'invalid_field_key, invalid_field_value, get_field',
        [
            ('name', factory.Faker('pystr', max_chars=129).generate(), None),
            ('name', lazy_fixture('repeatable_task_instance'), 'name'),
            ('name', '', None),
            ('user_defined_ordering', random.randint(-999, -1), None),
            ('user_defined_ordering', '', None),
            ('pomodoro_number', 'xyz', None),
            ('pomodoro_number', '', None),
            ('repeat_duration', timedelta(minutes=5), None),
            ('repeat_duration', timedelta(days=5, minutes=5), None),
            ('due_date', get_time_delta({'days': 1}, ahead=False), None),
            ('due_date', get_time_delta({
                'days': 1
            }, ahead=False).strftime('%Y-%m-%d'), None),
            ('priority', lazy_fixture('priority_instance_for_random_user'),
             'id'),
            ('project', lazy_fixture('project_instance_for_random_user'),
             'id'),
            ('project', '', None),
            ('status', '', None),
            ('status', 3, None),
            ('status', 1, None)  # you can create only active tasks
        ])
    def test_save_task_with_invalid_data(self, invalid_field_key,
                                         invalid_field_value, get_field,
                                         task_data, request_mock,
                                         project_instance, priority_instance):
        task_data['project'] = project_instance.id
        task_data['priority'] = priority_instance.id

        if get_field is not None:
            task_data[invalid_field_key] = getattr(invalid_field_value,
                                                   get_field)
        else:
            task_data[invalid_field_key] = invalid_field_value

        serializer = self.serializer_class(data=task_data)
        serializer.context['request'] = request_mock

        assert serializer.is_valid() is False
        assert invalid_field_key in serializer.errors

    def test_update_task_with_valid_data(self, task_data, task_instance,
                                         priority_instance, project_instance,
                                         request_mock):
        task_data['project'] = project_instance.id
        task_data['priority'] = priority_instance.id

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        assert serializer.validated_data is not None

    @pytest.mark.parametrize(
        'invalid_field_key, invalid_field_value, get_field',
        [('name', factory.Faker('pystr', max_chars=129).generate(), None),
         ('name', lazy_fixture('repeatable_task_instance'), 'name'),
         ('name', '', None),
         ('user_defined_ordering', random.randint(-999, -1), None),
         ('user_defined_ordering', '', None), ('pomodoro_number', 'xyz', None),
         ('pomodoro_number', '', None),
         ('repeat_duration', timedelta(minutes=5), None),
         ('repeat_duration', timedelta(days=5, minutes=5), None),
         ('due_date', get_time_delta({'days': 1}, ahead=False), None),
         ('due_date', get_time_delta({
             'days': 1
         }, ahead=False).strftime('%Y-%m-%d'), None),
         ('priority', lazy_fixture('priority_instance_for_random_user'), 'id'),
         ('project', lazy_fixture('project_instance_for_random_user'), 'id'),
         ('project', '', None), ('status', '', None), ('status', 3, None)])
    def test_update_task_with_invalid_data(self, invalid_field_key,
                                           invalid_field_value, get_field,
                                           task_data, task_instance,
                                           priority_instance, project_instance,
                                           request_mock):
        task_data['project'] = project_instance.id
        task_data['priority'] = priority_instance.id

        if get_field is not None:
            task_data[invalid_field_key] = getattr(invalid_field_value,
                                                   get_field)
        else:
            task_data[invalid_field_key] = invalid_field_value

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data)
        serializer.context['request'] = request_mock

        assert serializer.is_valid() is False
        assert invalid_field_key in serializer.errors

    @pytest.mark.parametrize('changed_field, field_value', [
        ('name', 'xyz'),
        ('status', 1),
        ('priority', None),
        ('user_defined_ordering', 5),
        ('pomodoro_number', 14),
        ('pomodoro_length', timedelta(minutes=30)),
        ('due_date', get_time_delta({'days': 1})),
        ('due_date', None),
        ('reminder_date', None),
        ('repeat_duration', None),
    ])
    def test_partial_update_task(self, changed_field, field_value,
                                 task_instance, request_mock):
        task_data = {changed_field: field_value}

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        assert all(value in serializer.validated_data for value in task_data)

    def test_pin_task_to_new_project_with_unique_name_for_new_project(
            self, task_instance, project_create_batch, request_mock):
        new_project = project_create_batch[0]

        task_data = {'project': new_project.id}

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock
        assert serializer.is_valid()

        pinned_task = serializer.save()
        assert pinned_task.project == new_project

    def test_pin_task_to_new_project_with_colliding_name_for_new_project(
            self, task_instance, request_mock,
            duplicate_task_instance_in_second_project):
        new_project = duplicate_task_instance_in_second_project.project

        task_data = {'project': new_project.id}

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        with pytest.raises(ValidationError) as exc:
            serializer.save()

        assert exc.value.messages[0] == TaskException.messages[
            TaskException.task_duplicated]

    def test_complete_one_time_task(self, task_model, task_instance,
                                    request_mock):
        task_data = {'status': task_model.status_completed}

        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        completed_task = serializer.save()
        assert completed_task.status == task_model.status_completed

    def test_complete_repeatable_task_create_new_task_for_next_due_date(
            self, task_model, request_mock,
            repeatable_task_instance_without_due_date):
        task_data = {'status': task_model.status_completed}

        serializer = self.serializer_class(
            instance=repeatable_task_instance_without_due_date,
            data=task_data,
            partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        completed_task = serializer.save()
        assert completed_task.status == task_model.status_completed

        next_task = get_active_tasks(project=completed_task.project,
                                     name=completed_task.name)[0]
        assert next_task.due_date is not None and next_task.due_date.date(
        ) == timezone.now().date()

    def test_complete_task_force_finishes_current_pomodoros(
            self, task_model, task_instance, date_frame_in_progress,
            request_mock):
        task_data = {'status': task_model.status_completed}
        serializer = self.serializer_class(instance=task_instance,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert date_frame_in_progress.end is None
        assert serializer.is_valid()
        completed_task = serializer.save()

        date_frame_in_progress.refresh_from_db()
        assert completed_task.status == task_model.status_completed
        assert date_frame_in_progress.end is not None

    @pytest.mark.parametrize('completed_task', [
        lazy_fixture('completed_task_instance'),
        lazy_fixture('completed_repeatable_task_instance')
    ])
    def test_reactivate_task(self, completed_task, task_model, request_mock):
        task_data = {'status': task_model.status_active}
        serializer = self.serializer_class(instance=completed_task,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        reactivated_task = serializer.save()

        assert reactivated_task.status == task_model.status_active

    @pytest.mark.parametrize('completed_task', [
        lazy_fixture('completed_task_instance'),
        lazy_fixture('completed_repeatable_task_instance')
    ])
    def test_reactivate_task_with_existing_active_duplicate(
            self, completed_task, task_model, task_instance, request_mock):
        task_instance.name = completed_task.name
        task_instance.save()

        task_data = {'status': task_model.status_active}
        serializer = self.serializer_class(instance=completed_task,
                                           data=task_data,
                                           partial=True)
        serializer.context['request'] = request_mock

        assert serializer.is_valid()
        with pytest.raises(ValidationError) as exc:
            serializer.save()

        assert exc.value.messages[0] == TaskException.messages[
            TaskException.task_duplicated]