fix: make cooldown_action decorator properly await async finish() (#75)

* fix: make cooldown_action decorator properly await async finish()

The decorator was incorrectly wrapping the async finish() method with a sync
wrapper, causing cooldown to be recorded BEFORE the action actually executed.

This fix:
- Changes wrapper to async def finish()
- Awaits original_finish() before recording cooldown
- Ensures cooldown is only recorded on successful execution

Fixes #74

* test: add test to verify cooldown not recorded on failure

This test actually reveals the async/await bug:
- Creates a FailingAction that raises in finish()
- Verifies cooldown is NOT recorded when action fails
- Fails with buggy sync wrapper, passes with async fix
This commit is contained in:
Zihao Xu
2026-01-19 23:32:14 -08:00
committed by GitHub
parent 5f236361dc
commit a1241a9156
2 changed files with 142 additions and 2 deletions

View File

@@ -34,8 +34,10 @@ def cooldown_action(cls: type) -> type:
if hasattr(cls, "finish"):
original_finish = cls.finish
def finish(self, **params): # type: ignore[no-redef]
events = original_finish(self, **params)
async def finish(self, **params): # type: ignore[no-redef]
# Must await original_finish first, then record cooldown.
# This ensures cooldown is only recorded after finish() succeeds.
events = await original_finish(self, **params)
last_map = getattr(self.avatar, "_action_cd_last_months", None)
if last_map is not None:
last_map[self.__class__.__name__] = self.world.month_stamp