# retoor <retoor@molodetz.nl>
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