fix(conferences): разрешить дату в прошлом для закреплённых конференций

Закреплённая конференция — постоянная комната-ссылка, а не разовое
событие: прошедшая дата в scheduled_at для неё осмысленна (например,
серия «каждый день с такого-то числа», уже наступившего). Запрет
прошлого остаётся в силе только для обычных конференций.
This commit is contained in:
2026-08-11 20:49:50 +03:00
parent e71bf0623f
commit e2254819c7
2 changed files with 28 additions and 1 deletions

View File

@@ -91,7 +91,17 @@ class ConferenceCreateIn(BaseModel):
raise ValueError("closed_conference_requires_password") raise ValueError("closed_conference_requires_password")
if self.recurrence is not None and not self.is_pinned: if self.recurrence is not None and not self.is_pinned:
raise ValueError("recurrence_requires_pinned") raise ValueError("recurrence_requires_pinned")
if self.scheduled_at is not None and self.scheduled_at < datetime.now(UTC) - PAST_TOLERANCE: # Запрет прошлого — только для обычных (не закреплённых) конференций.
# Закреплённая конференция — не разовое событие с конкретной датой, а
# постоянная комната-ссылка (см. комментарий у `is_pinned` в
# `models/conference.py`); `scheduled_at` для неё — не более чем
# подсказка в календаре, и прошедшая дата (например, «встречаемся тут
# каждый день с такого-то числа», уже наступившего) осмысленна.
if (
not self.is_pinned
and self.scheduled_at is not None
and self.scheduled_at < datetime.now(UTC) - PAST_TOLERANCE
):
raise ValueError("scheduled_at_in_the_past") raise ValueError("scheduled_at_in_the_past")
return self return self

View File

@@ -212,6 +212,23 @@ async def test_create_scheduled_in_the_past_returns_422(
assert response.status_code == 422 assert response.status_code == 422
async def test_create_pinned_scheduled_in_the_past_is_allowed(
client: httpx.AsyncClient, db_session: AsyncSession
) -> None:
"""Закреплённая конференция — не разовое событие, прошлая дата для неё осмысленна."""
user = await _make_user(db_session)
await db_session.commit()
past = datetime.now(UTC) - timedelta(hours=1)
response = await client.post(
"/api/v1/conferences",
json={"title": "Pinned past", "scheduled_at": _iso(past), "is_pinned": True},
headers=_auth_headers(user),
)
assert response.status_code == 201
assert response.json()["is_pinned"] is True
# --- «Мои конференции» --------------------------------------------------------- # --- «Мои конференции» ---------------------------------------------------------