diff --git a/devplacepy/routers/profile/index.py b/devplacepy/routers/profile/index.py index 26484688..90c09a0c 100644 --- a/devplacepy/routers/profile/index.py +++ b/devplacepy/routers/profile/index.py @@ -203,7 +203,9 @@ async def profile_page( badges = list(get_table("badges").find(user_uid=profile_user["uid"])) for b in badges: - b["icon"] = get_badge(b["badge_name"]).get("icon") + badge_meta = get_badge(b["badge_name"]) + b["icon"] = badge_meta.get("icon") + b["description"] = badge_meta.get("description") achievements = build_achievements({b["badge_name"] for b in badges}) badge_total = sum(group["total"] for group in achievements) badge_earned = sum(group["earned"] for group in achievements) diff --git a/devplacepy/schemas/content.py b/devplacepy/schemas/content.py index fe370a4e..8f8c79a1 100644 --- a/devplacepy/schemas/content.py +++ b/devplacepy/schemas/content.py @@ -68,6 +68,7 @@ class PollOut(_Out): class BadgeOut(_Out): name: Optional[str] = Field(None, alias="badge_name") icon: Optional[str] = None + description: Optional[str] = None created_at: Optional[str] = None model_config = ConfigDict(populate_by_name=True) diff --git a/tests/api/profile/index.py b/tests/api/profile/index.py index 4c359124..b3b7e3af 100644 --- a/tests/api/profile/index.py +++ b/tests/api/profile/index.py @@ -499,3 +499,76 @@ def test_profile_json_xp_fields_boundary_xp(app_server): ) +def _signup_badge_user(): + import time + + name = f"bdgi{int(time.time() * 1000)}" + session = requests.Session() + session.post( + f"{BASE_URL}/auth/signup", + data={ + "username": name, + "email": f"{name}@t.dev", + "password": "secret123", + "confirm_password": "secret123", + }, + allow_redirects=True, + ) + return session, name + + +def test_profile_badges_json_description_matches_catalog(app_server): + from devplacepy.database import get_table, refresh_snapshot + from devplacepy.utils import BADGE_CATALOG, award_badge + + session, name = _signup_badge_user() + refresh_snapshot() + user = get_table("users").find_one(username=name) + assert user, f"user {name} not found after signup" + award_badge(user["uid"], "First Post") + award_badge(user["uid"], "Member") + refresh_snapshot() + + r = session.get( + f"{BASE_URL}/profile/{name}", headers={"Accept": "application/json"} + ) + assert r.status_code == 200 + body = r.json() + assert "badges" in body, "badges key missing from profile JSON" + assert isinstance(body["badges"], list), "badges is not a list" + assert body["badges"], "expected at least one badge" + for badge in body["badges"]: + assert "description" in badge, f"badge missing description key: {badge}" + assert badge["description"] is not None, f"badge description is null: {badge}" + assert isinstance(badge["description"], str), ( + f"badge description is not a string: {badge}" + ) + assert badge["description"] == BADGE_CATALOG[badge["name"]]["description"], ( + f"badge description mismatch: {badge['name']}" + ) + + +def test_profile_badge_description_exact_string(app_server): + from devplacepy.database import get_table, refresh_snapshot + from devplacepy.utils import award_badge + + session, name = _signup_badge_user() + refresh_snapshot() + user = get_table("users").find_one(username=name) + assert user, f"user {name} not found after signup" + award_badge(user["uid"], "Cheerleader") + refresh_snapshot() + + r = session.get( + f"{BASE_URL}/profile/{name}", headers={"Accept": "application/json"} + ) + assert r.status_code == 200 + badges = r.json()["badges"] + cheerleader = [b for b in badges if b["name"] == "Cheerleader"] + assert cheerleader, f"Cheerleader badge missing from profile JSON: {badges}" + assert cheerleader[0]["description"] == "Reacted 50 times", ( + f"expected 'Reacted 50 times', got {cheerleader[0]['description']!r}" + ) + + + diff --git a/tests/api/profile/search.py b/tests/api/profile/search.py index 01333631..c833bcf7 100644 --- a/tests/api/profile/search.py +++ b/tests/api/profile/search.py @@ -276,7 +276,7 @@ def test_profile_renders_heatmap_and_streak(app_server): def test_profile_badges_json_has_non_null_names(app_server): import time from devplacepy.database import get_table, refresh_snapshot - from devplacepy.utils import award_badge + from devplacepy.utils import BADGE_CATALOG, award_badge name = f"bdg{int(time.time() * 1000)}" session = requests.Session() @@ -309,5 +309,14 @@ def test_profile_badges_json_has_non_null_names(app_server): assert badge["name"] is not None, f"badge name is null: {badge}" assert isinstance(badge["name"], str), f"badge name is not a string: {badge}" assert len(badge["name"]) > 0, f"badge name is empty: {badge}" + assert "description" in badge, f"badge missing description key: {badge}" + assert badge["description"] is not None, f"badge description is null: {badge}" + assert isinstance(badge["description"], str), f"badge description is not a string: {badge}" + assert len(badge["description"]) > 0, f"badge description is empty: {badge}" + assert badge["description"] == BADGE_CATALOG[badge["name"]]["description"], ( + f"badge description mismatch: {badge['name']}" + ) + +