Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions questions/services/forecasts.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
from django.db import IntegrityError, transaction
from django.db.models import F, Q, QuerySet, Subquery, OuterRef, Count
from django.utils import timezone
from rest_framework.exceptions import ValidationError

from notifications.constants import MailingTags
from posts.models import PostUserSnapshot, PostSubscription
from posts.services.subscriptions import (
Expand Down Expand Up @@ -201,11 +199,12 @@ def withdraw_forecast_bulk(user: User = None, withdrawals: list[dict] = None):
author=user,
).order_by("start_time")

# Skip questions where the user has no active forecast at withdraw_at.
# This allows bulk "withdraw all" requests to succeed even when some
# questions in a group have no forecast from the user (e.g. resolved
# questions the user never forecasted on).
if not user_forecasts.exists():
raise ValidationError(
f"User {user.id} has no forecast at {withdraw_at} to "
f"withdraw for question {question.id}"
)
continue

forecast_to_terminate = user_forecasts.first()
forecast_to_terminate.end_time = withdraw_at
Expand Down
13 changes: 10 additions & 3 deletions tests/unit/test_questions/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,15 +426,22 @@ def test_withdraw_forecast(
)
assert response.status_code == 201

def test_cant_withdraw_forecast_if_no_forecast(
self, question_binary_with_forecast_user_1, user2_client
def test_withdraw_forecast_no_forecast_is_noop(
self, question_binary_with_forecast_user_1, user2, user2_client
):
# Withdrawing for a question the user never forecasted on is a no-op
# so that bulk "withdraw all" works across mixed groups containing
# questions the user did not forecast on (e.g. resolved siblings).
response = user2_client.post(
self.url,
data=json.dumps([{"question": question_binary_with_forecast_user_1.id}]),
content_type="application/json",
)
assert response.status_code == 400
assert response.status_code == 201
assert not Forecast.objects.filter(
question=question_binary_with_forecast_user_1,
author=user2,
).exists()


class TestQuestionResolve:
Expand Down
Loading