diff --git a/tests/api/profile/index.py b/tests/api/profile/index.py index d50535e0..8640f3fb 100644 --- a/tests/api/profile/index.py +++ b/tests/api/profile/index.py @@ -407,3 +407,96 @@ def test_viewing_profile_marks_notification_read(app_server): refresh_snapshot() assert bool(get_table("notifications").find_one(uid=notif_uid)["read"]) is True + + +def test_own_profile_json_exposes_xp_fields(app_server, seeded_db): + """GET /profile (own) with Accept: application/json includes xp_next_level and xp_progress_pct.""" + import requests + + session = requests.Session() + session.post( + f"{BASE_URL}/auth/login", + data={ + "username": seeded_db["alice"]["username"], + "password": seeded_db["alice"]["password"], + }, + allow_redirects=True, + ) + r = session.get(f"{BASE_URL}/profile", headers={"Accept": "application/json"}) + assert r.status_code == 200 + data = r.json() + pu = data["profile_user"] + assert "xp_next_level" in pu, "xp_next_level missing from own profile JSON" + assert "xp_progress_pct" in pu, "xp_progress_pct missing from own profile JSON" + assert isinstance(pu["xp_next_level"], int) + assert isinstance(pu["xp_progress_pct"], int) + + +def test_other_profile_json_exposes_xp_fields(app_server): + """GET /profile/{username} with Accept: application/json includes xp_next_level and xp_progress_pct.""" + from devplacepy.database import get_table, refresh_snapshot + + owner = _seed_owner() + username = get_table("users").find_one(uid=owner)["username"] + refresh_snapshot() + + r = requests.get( + f"{BASE_URL}/profile/{username}", headers={"Accept": "application/json"} + ) + assert r.status_code == 200 + data = r.json() + pu = data["profile_user"] + assert "xp_next_level" in pu, "xp_next_level missing from other profile JSON" + assert "xp_progress_pct" in pu, "xp_progress_pct missing from other profile JSON" + assert isinstance(pu["xp_next_level"], int) + assert isinstance(pu["xp_progress_pct"], int) + + +def test_profile_json_xp_fields_zero_xp(app_server): + """User with 0 XP returns xp_next_level=100 (level 1) and xp_progress_pct=0.""" + from devplacepy.database import get_table, refresh_snapshot + from devplacepy.utils.rewards import LEVEL_XP + + owner = _seed_owner() + username = get_table("users").find_one(uid=owner)["username"] + refresh_snapshot() + + r = requests.get( + f"{BASE_URL}/profile/{username}", headers={"Accept": "application/json"} + ) + assert r.status_code == 200 + pu = r.json()["profile_user"] + assert pu["xp"] == 0 or pu["xp"] is None, f"expected 0 xp, got {pu['xp']}" + assert pu["xp_next_level"] == LEVEL_XP, ( + f"expected xp_next_level={LEVEL_XP} for level 1, got {pu['xp_next_level']}" + ) + assert pu["xp_progress_pct"] == 0, ( + f"expected xp_progress_pct=0 for 0 XP, got {pu['xp_progress_pct']}" + ) + + +def test_profile_json_xp_fields_boundary_xp(app_server): + """User with exactly 100 XP (level 2) returns xp_next_level=200 and xp_progress_pct=0.""" + from devplacepy.database import get_table, refresh_snapshot + from devplacepy.utils.rewards import award_xp + + owner = _seed_owner() + username = get_table("users").find_one(uid=owner)["username"] + award_xp(owner, 100) + refresh_snapshot() + + r = requests.get( + f"{BASE_URL}/profile/{username}", headers={"Accept": "application/json"} + ) + assert r.status_code == 200 + pu = r.json()["profile_user"] + assert pu["xp"] == 100, f"expected 100 xp, got {pu['xp']}" + assert pu["level"] == 2, f"expected level 2, got {pu['level']}" + assert pu["xp_next_level"] == 200, ( + f"expected xp_next_level=200 for level 2, got {pu['xp_next_level']}" + ) + assert pu["xp_progress_pct"] == 0, ( + f"expected xp_progress_pct=0 at boundary, got {pu['xp_progress_pct']}" + ) + +