# retoor from datetime import datetime, timedelta, timezone import pytest from devplacepy.db_client import get_table, init_db from devplacepy.db_client import ( can_give_award, can_receive_award, has_giver_cooldown, has_receiver_cooldown, recompute_user_award_stats, revoke_award, ) from devplacepy.utils import generate_uid, make_combined_slug @pytest.fixture(autouse=True) def _db(local_db): init_db() yield def _user(prefix="award"): uid = generate_uid() get_table("users").insert( { "uid": uid, "username": f"{prefix}_{uid[:8]}", "email": f"{uid[:8]}@t.dev", "created_at": datetime.now(timezone.utc).isoformat(), "award_count": 0, "last_award_at": None, "last_award_slug": None, "last_award_uid": None, } ) return uid def _award(giver_uid, receiver_uid, description="Great work", generated_at=None): uid = generate_uid() slug = make_combined_slug(description, uid) now = datetime.now(timezone.utc).isoformat() get_table("awards").insert( { "uid": uid, "slug": slug, "description": description, "giver_uid": giver_uid, "receiver_uid": receiver_uid, "attachment_uid_512": "", "attachment_uid_256": "", "attachment_uid_64": "", "generated_at": generated_at, "created_at": now, "job_uid": "", "deleted_at": None, "deleted_by": None, } ) return uid, slug def _attachment(user_uid): uid = generate_uid() get_table("attachments").insert( { "uid": uid, "target_type": "award", "target_uid": "", "user_uid": user_uid, "original_filename": "award.png", "stored_name": f"{uid}.png", "directory": "aa/bb", "file_size": 10, "mime_type": "image/png", "created_at": datetime.now(timezone.utc).isoformat(), "deleted_at": None, "deleted_by": None, } ) return uid def test_slug_matches_make_combined_slug(): uid = generate_uid() slug = make_combined_slug("Ship it", uid) assert slug.endswith("-ship-it") or slug == uid.replace("-", "")[-12:] def test_can_give_award_respects_giver_cooldown(monkeypatch): giver = _user("giver") receiver = _user("recv") _award(giver, receiver) monkeypatch.setattr( "devplacepy.database.awards.get_int_setting", lambda key, default: 24 if "cooldown" in key else default, ) assert has_giver_cooldown(giver) is True assert can_give_award(giver, receiver) is False def test_can_receive_award_respects_receiver_cooldown(monkeypatch): giver = _user("giver2") receiver = _user("recv2") _award(giver, receiver) monkeypatch.setattr( "devplacepy.database.awards.get_int_setting", lambda key, default: 24 if "receive" in key else default, ) assert has_receiver_cooldown(receiver) is True assert can_receive_award(receiver) is False def test_cooldown_ignores_soft_deleted_rows(): giver = _user("giver3") receiver = _user("recv3") uid, _ = _award(giver, receiver) get_table("awards").update( {"uid": uid, "deleted_at": datetime.now(timezone.utc).isoformat(), "deleted_by": giver}, ["uid"], ) assert has_giver_cooldown(giver) is False assert has_receiver_cooldown(receiver) is False def test_recompute_user_award_stats_sets_latest_generated(): receiver = _user("recv4") giver = _user("giver4") older = datetime.now(timezone.utc) - timedelta(hours=2) newer = datetime.now(timezone.utc) - timedelta(hours=1) uid_old, slug_old = _award(giver, receiver, "older", older.isoformat()) uid_new, slug_new = _award(giver, receiver, "newer", newer.isoformat()) recompute_user_award_stats(receiver) row = get_table("users").find_one(uid=receiver) assert row["award_count"] == 2 assert row["last_award_uid"] == uid_new assert row["last_award_slug"] == slug_new assert uid_old != uid_new assert slug_old != slug_new def test_revoke_latest_recomputes_to_previous(): receiver = _user("recv5") giver = _user("giver5") older = datetime.now(timezone.utc) - timedelta(hours=2) newer = datetime.now(timezone.utc) - timedelta(hours=1) uid_old, slug_old = _award(giver, receiver, "keep", older.isoformat()) uid_new, _ = _award(giver, receiver, "gone", newer.isoformat()) recompute_user_award_stats(receiver) revoke_award(uid_new, giver) row = get_table("users").find_one(uid=receiver) assert row["award_count"] == 1 assert row["last_award_uid"] == uid_old assert row["last_award_slug"] == slug_old def test_revoke_clears_stats_when_count_zero(): receiver = _user("recv6") giver = _user("giver6") now = datetime.now(timezone.utc).isoformat() uid, _ = _award(giver, receiver, "only", now) recompute_user_award_stats(receiver) revoke_award(uid, giver) row = get_table("users").find_one(uid=receiver) assert row["award_count"] == 0 assert not row.get("last_award_uid") def test_revoke_soft_deletes_attachments_under_one_stamp(): receiver = _user("recv7") giver = _user("giver7") admin = _user("admin7") now = datetime.now(timezone.utc).isoformat() uid, _ = _award(giver, receiver, "with files", now) a512 = _attachment(receiver) a256 = _attachment(giver) a64 = _attachment(giver) get_table("awards").update( { "uid": uid, "attachment_uid_512": a512, "attachment_uid_256": a256, "attachment_uid_64": a64, }, ["uid"], ) revoke_award(uid, admin) award = get_table("awards").find_one(uid=uid) assert award.get("deleted_at") stamps = { get_table("attachments").find_one(uid=a512).get("deleted_at"), get_table("attachments").find_one(uid=a256).get("deleted_at"), get_table("attachments").find_one(uid=a64).get("deleted_at"), } assert len(stamps) == 1 assert award.get("deleted_at") in stamps