|
@@ -201,9 +201,15 @@ async def test_paused_preselection_draw_can_be_canceled(app_modules):
|
|
|
assert current["status"] == database.STATUS_CANCELED
|
|
assert current["status"] == database.STATUS_CANCELED
|
|
|
|
|
|
|
|
|
|
|
|
|
-async def test_month_end_schedule_and_materialization_is_idempotent(app_modules):
|
|
|
|
|
|
|
+async def test_month_end_schedule_and_materialization_is_idempotent(app_modules, monkeypatch):
|
|
|
templates = app_modules.load("wbb.services.giveaway_templates")
|
|
templates = app_modules.load("wbb.services.giveaway_templates")
|
|
|
database = app_modules.load("wbb.utils.dbgiveaway")
|
|
database = app_modules.load("wbb.utils.dbgiveaway")
|
|
|
|
|
+
|
|
|
|
|
+ async def unsupported_history(*_args, **_kwargs):
|
|
|
|
|
+ raise AssertionError("Bot accounts must not request chat history")
|
|
|
|
|
+ yield
|
|
|
|
|
+
|
|
|
|
|
+ monkeypatch.setattr(app_modules.app, "get_chat_history", unsupported_history)
|
|
|
body = {
|
|
body = {
|
|
|
"chat_id": -100, "title": "Monthly", "description": "",
|
|
"chat_id": -100, "title": "Monthly", "description": "",
|
|
|
"prizes": [{"name": "First", "count": 1}],
|
|
"prizes": [{"name": "First", "count": 1}],
|
|
@@ -228,12 +234,80 @@ async def test_month_end_schedule_and_materialization_is_idempotent(app_modules)
|
|
|
items = [item async for item in database.giveawaysdb.find({"template_id": template["template_id"]})]
|
|
items = [item async for item in database.giveawaysdb.find({"template_id": template["template_id"]})]
|
|
|
assert len(items) == 1
|
|
assert len(items) == 1
|
|
|
assert items[0]["period_key"] == local.strftime("%Y-%m")
|
|
assert items[0]["period_key"] == local.strftime("%Y-%m")
|
|
|
|
|
+ assert items[0]["message_id"] == 1
|
|
|
|
|
+ assert len(app_modules.app.sent_messages) == 1
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+async def test_announcement_send_failure_retries_same_period(app_modules, monkeypatch):
|
|
|
|
|
+ templates = app_modules.load("wbb.services.giveaway_templates")
|
|
|
|
|
+ database = app_modules.load("wbb.utils.dbgiveaway")
|
|
|
|
|
+ now = datetime.now(UTC)
|
|
|
|
|
+ local = now.astimezone(templates.TIMEZONE)
|
|
|
|
|
+ template = await templates.create_template({
|
|
|
|
|
+ "chat_id": -100, "title": "Retry", "description": "",
|
|
|
|
|
+ "prizes": [{"name": "First", "count": 1}],
|
|
|
|
|
+ "day_of_month": local.day, "start_time": "00:00",
|
|
|
|
|
+ "draw_day_offset": 1, "draw_time": "20:00",
|
|
|
|
|
+ }, "Admin")
|
|
|
|
|
+ original_send = app_modules.app.send_message
|
|
|
|
|
+ failed_once = False
|
|
|
|
|
+
|
|
|
|
|
+ async def flaky_send(*args, **kwargs):
|
|
|
|
|
+ nonlocal failed_once
|
|
|
|
|
+ if not failed_once:
|
|
|
|
|
+ failed_once = True
|
|
|
|
|
+ raise RuntimeError("temporary Telegram failure")
|
|
|
|
|
+ return await original_send(*args, **kwargs)
|
|
|
|
|
+
|
|
|
|
|
+ monkeypatch.setattr(app_modules.app, "send_message", flaky_send)
|
|
|
|
|
+ with pytest.raises(RuntimeError, match="temporary Telegram failure"):
|
|
|
|
|
+ await templates.materialize_template(template, now=now)
|
|
|
|
|
+ await templates.materialize_template(template, now=now)
|
|
|
|
|
+ items = [item async for item in database.giveawaysdb.find({
|
|
|
|
|
+ "template_id": template["template_id"]
|
|
|
|
|
+ })]
|
|
|
|
|
+ assert len(items) == 1
|
|
|
|
|
+ assert items[0]["message_id"] == 1
|
|
|
assert len(app_modules.app.sent_messages) == 1
|
|
assert len(app_modules.app.sent_messages) == 1
|
|
|
|
|
|
|
|
|
|
|
|
|
-async def test_due_period_recovers_draw_and_pins_result_once(app_modules):
|
|
|
|
|
|
|
+async def test_due_unannounced_period_cancels_instead_of_drawing(app_modules):
|
|
|
database = app_modules.load("wbb.utils.dbgiveaway")
|
|
database = app_modules.load("wbb.utils.dbgiveaway")
|
|
|
service = app_modules.load("wbb.services.giveaways")
|
|
service = app_modules.load("wbb.services.giveaways")
|
|
|
|
|
+ giveaway = await database.create_giveaway(
|
|
|
|
|
+ chat_id=-100, creator_id=1, creator_name="Admin", title="Unannounced",
|
|
|
|
|
+ description="", prizes=[{"name": "First", "count": 1}],
|
|
|
|
|
+ ends_at=datetime.now(UTC) + timedelta(hours=1),
|
|
|
|
|
+ template_id="monthly-template", period_key="2026-09",
|
|
|
|
|
+ )
|
|
|
|
|
+ accepted, reason, _ = await service.finish_and_publish_giveaway(
|
|
|
|
|
+ giveaway["giveaway_id"], publish=False
|
|
|
|
|
+ )
|
|
|
|
|
+ assert accepted is False
|
|
|
|
|
+ assert "报名公告未发布" in reason
|
|
|
|
|
+ await database.giveawaysdb.update_one(
|
|
|
|
|
+ {"giveaway_id": giveaway["giveaway_id"]},
|
|
|
|
|
+ {"$set": {"ends_at": datetime.now(UTC) - timedelta(seconds=1)}},
|
|
|
|
|
+ )
|
|
|
|
|
+ due = await database.list_due_giveaways()
|
|
|
|
|
+ await service.resume_pending_giveaway(due[0])
|
|
|
|
|
+ canceled = await database.get_giveaway(giveaway["giveaway_id"])
|
|
|
|
|
+ assert canceled["status"] == database.STATUS_CANCELED
|
|
|
|
|
+ assert canceled["cancellation_reason"] == "announcement_not_published"
|
|
|
|
|
+ assert canceled["winners"] == []
|
|
|
|
|
+ assert not app_modules.app.sent_messages
|
|
|
|
|
+ assert not app_modules.app.pinned_messages
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+async def test_due_period_recovers_draw_and_pins_result_once(app_modules, monkeypatch):
|
|
|
|
|
+ database = app_modules.load("wbb.utils.dbgiveaway")
|
|
|
|
|
+ service = app_modules.load("wbb.services.giveaways")
|
|
|
|
|
+
|
|
|
|
|
+ async def unsupported_history(*_args, **_kwargs):
|
|
|
|
|
+ raise AssertionError("Bot accounts must not request chat history")
|
|
|
|
|
+ yield
|
|
|
|
|
+
|
|
|
|
|
+ monkeypatch.setattr(app_modules.app, "get_chat_history", unsupported_history)
|
|
|
giveaway = await database.create_giveaway(
|
|
giveaway = await database.create_giveaway(
|
|
|
chat_id=-100, creator_id=1, creator_name="Admin", title="Catch-up",
|
|
chat_id=-100, creator_id=1, creator_name="Admin", title="Catch-up",
|
|
|
description="", prizes=[{"name": "First", "count": 1}],
|
|
description="", prizes=[{"name": "First", "count": 1}],
|