332 lines
11 KiB
Python
Raw Normal View History

# retoor <retoor@molodetz.nl>
import base64
import time
import requests
from tests.conftest import BASE_URL
from devplacepy.database import get_table
_counter_api_auth = [0]
def _signup_api_auth(password="secret123"):
_counter_api_auth[0] += 1
name = f"apiauth{int(time.time() * 1000)}{_counter_api_auth[0]}"
email = f"{name}@t.dev"
session = requests.Session()
session.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": email,
"password": password,
"confirm_password": password,
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
"birth_date": "1990-01-01",
"accept_terms": "1",
},
allow_redirects=True,
)
return session, name, email, password
def _user_api_auth(name):
return get_table("users").find_one(username=name)
def _key_api_auth(name):
return _user_api_auth(name)["api_key"]
import io
import uuid
from PIL import Image
JSON_media = {"Accept": "application/json"}
def _png_bytes_media(color=(200, 30, 30)):
buf = io.BytesIO()
Image.new("RGB", (8, 8), color).save(buf, "PNG")
return buf.getvalue()
def _signup_media(prefix="media"):
s = requests.Session()
name = f"{prefix}_{uuid.uuid4().hex[:10]}"
s.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@test.devplace",
"password": "secret123",
"confirm_password": "secret123",
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
"birth_date": "1990-01-01",
"accept_terms": "1",
},
allow_redirects=True,
)
return s, name
def _login_media(seeded_db, who):
s = requests.Session()
creds = seeded_db[who]
s.post(
f"{BASE_URL}/auth/login",
data={"email": creds["email"], "password": creds["password"]},
allow_redirects=True,
)
return s
def _upload_media(s, name="pic.png", content=None, mime="image/png"):
files = {"file": (name, content if content is not None else _png_bytes_media(), mime)}
r = s.post(f"{BASE_URL}/uploads/upload", files=files)
assert r.status_code == 201, r.text
return r.json()["uid"]
def _create_post_media(s, attachment_uids, title="media post"):
r = s.post(
f"{BASE_URL}/posts/create",
data={
"content": "A post that carries media for the gallery tests.",
"title": title,
"topic": "random",
"attachment_uids": attachment_uids,
},
allow_redirects=True,
)
assert r.status_code == 200, r.text[:300]
assert "/posts/" in r.url, r.url
return r.url
def _create_project_media(s, attachment_uids, title="Media Project"):
r = s.post(
f"{BASE_URL}/projects/create",
data={
"title": title,
"description": "Project with media for gallery tests.",
"project_type": "software",
"platforms": "linux",
"status": "In Development",
"attachment_uids": attachment_uids,
},
allow_redirects=True,
)
assert r.status_code == 200, r.text[:300]
assert "/projects/" in r.url, r.url
return r.url
def _media_json(session, username):
r = session.get(f"{BASE_URL}/profile/{username}?tab=media", headers=JSON_media)
assert r.status_code == 200, r.text[:300]
return r.json()
def _media_uids(session, username):
return [m["uid"] for m in _media_json(session, username)["media"]]
def _seed_media_via_browser(page, title="ui media"):
up = page.request.post(
f"{BASE_URL}/uploads/upload",
multipart={
"file": {
"name": "pic.png",
"mimeType": "image/png",
"buffer": _png_bytes_media(),
}
},
)
assert up.ok, up.text()
uid = up.json()["uid"]
post = page.request.post(
f"{BASE_URL}/posts/create",
form={
"content": "media for the ui gallery test",
"title": title,
"topic": "random",
"attachment_uids": uid,
},
)
assert post.ok, post.text()
return uid
def _media_uids_via_page(page, username):
resp = page.request.get(f"{BASE_URL}/profile/{username}?tab=media", headers=JSON_media)
return [m["uid"] for m in resp.json()["media"]]
from datetime import datetime, timedelta, timezone
from devplacepy.database import (
get_table,
get_activity_calendar,
get_streaks,
get_activity_heatmap,
get_activity_months,
)
from devplacepy.utils import generate_uid, check_milestone_badges
_counter_streaks = [0]
def _make_user_streaks():
_counter_streaks[0] += 1
uid = generate_uid()
name = f"stk{int(time.time() * 1000)}{_counter_streaks[0]}"
get_table("users").insert(
{
"uid": uid,
"username": name,
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
"terms_version": "1",
"email": f"{name}@t.dev",
"role": "Member",
"is_active": True,
"xp": 0,
"level": 1,
"created_at": datetime.now(timezone.utc).isoformat(),
}
)
return uid
def _insert_post(user_uid, dt):
uid = generate_uid()
get_table("posts").insert(
{
"deleted_at": None,
"deleted_by": None,
"uid": uid,
"user_uid": user_uid,
"slug": f"{uid[:8]}-streak",
"title": None,
"content": "streak activity",
"topic": "random",
"project_uid": None,
"image": None,
"stars": 0,
"created_at": dt.isoformat(),
}
)
return uid
def _insert_comment(user_uid, post_uid, dt):
uid = generate_uid()
get_table("comments").insert(
{
"deleted_at": None,
"deleted_by": None,
"uid": uid,
"target_type": "post",
"target_uid": post_uid,
"post_uid": post_uid,
"user_uid": user_uid,
"content": "streak comment",
"parent_uid": None,
"created_at": dt.isoformat(),
}
)
return uid
def test_owner_sees_own_key_on_profile(app_server):
session, name, _, _ = _signup_api_auth()
html = session.get(f"{BASE_URL}/profile/{name}").text
assert "data-api-key-card" in html
assert _key_api_auth(name) in html
def test_member_cannot_see_others_key(app_server):
_signup_api_auth() # absorb the admin slot if the DB is empty
session_a, name_a, _, _ = _signup_api_auth()
_, name_b, _, _ = _signup_api_auth()
assert _user_api_auth(name_a)["role"] != "Admin"
html = session_a.get(f"{BASE_URL}/profile/{name_b}").text
assert "data-api-key-card" not in html
def test_media_tab_html_renders_grid(app_server):
s, name = _signup_media()
uid = _upload_media(s)
_create_post_media(s, uid)
r = s.get(f"{BASE_URL}/profile/{name}?tab=media")
assert r.status_code == 200
assert "media-grid" in r.text
assert "data-lightbox" in r.text
assert uid in r.text
def test_media_tab_empty_state_for_new_user(app_server):
s, name = _signup_media()
r = s.get(f"{BASE_URL}/profile/{name}?tab=media")
assert r.status_code == 200
assert "No media yet." in r.text
assert _media_json(s, name)["media"] == []
def test_media_pagination(app_server):
s, name = _signup_media()
# per_page is 24; create 26 linked attachments
for i in range(26):
uid = _upload_media(s)
_create_post_media(s, uid, title=f"p{i}")
data = _media_json(s, name)
pag = data["media_pagination"]
assert pag["total"] >= 26
assert pag["total_pages"] >= 2
assert len(data["media"]) == pag["per_page"] == 24
r2 = s.get(f"{BASE_URL}/profile/{name}?tab=media&page=2", headers=JSON_media)
assert r2.status_code == 200
assert len(r2.json()["media"]) >= 2
# the HTML pager keeps the tab so links do not fall back to posts
# (the ampersand is entity-escaped in the rendered href)
html = s.get(f"{BASE_URL}/profile/{name}?tab=media").text
assert "tab=media&amp;page=2" in html or "tab=media&page=2" in html
def test_profile_renders_heatmap_and_streak(app_server):
_counter_streaks[0] += 1
name = f"stkp{int(time.time() * 1000)}{_counter_streaks[0]}"
s = requests.Session()
s.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
"birth_date": "1990-01-01",
"accept_terms": "1",
},
allow_redirects=True,
)
s.post(
f"{BASE_URL}/posts/create",
data={
"content": "A post created today for the streak heatmap.",
"title": "Streak heatmap post",
"topic": "devlog",
},
allow_redirects=True,
)
html = s.get(f"{BASE_URL}/profile/{name}").text
assert "heatmap-grid" in html
assert "1 day streak" in html
test(sveta): Write API test verifying badge names in profile JSON response Outcome: done Changed: tests/api/profile/search.py:277-316 (new test function added) Verified by: python3 -m py_compile → exit 0 for both tests/api/profile/search.py and devplacepy/schemas/content.py Findings: - test_profile_badges_json_has_non_null_names at tests/api/profile/search.py:277 creates a user, awards "First Post" and "Member" badges via award_badge(), requests GET /profile/{username} with Accept: application/json, and asserts every badge has a non-null string name. - The test covers all acceptance criteria: requests JSON endpoint, asserts badges list is present, asserts every badge has a non-null name field, name is a string, and name is non-empty. - BadgeOut.name at devplacepy/schemas/content.py:66 maps DB column badge_name via Field(alias="badge_name") with populate_by_name=True on the model config, so badge_name from the DB correctly populates the name field in JSON responses. - Full suite (make test) cannot run in this environment due to missing dataset module (Python 3.11.2, pre-existing limitation). - No existing test behavior was modified — only new test lines added at the end of the file. Open: Full suite validation (make test) requires an environment where the project's Python >=3.12 dependency is satisfied and dataset is installed. Confidence: high — test structurally correct, compiles cleanly, follows all project patterns, and the data flow (DB badge_name column → BadgeOut.name alias → JSON response) is verified end-to-end through code inspect Typosaurus-Run: cf8155d8183146ecbb92790b22f8c980 Typosaurus-Node: c5e002cd07ca45e9bc4c9d23fdd3ff5b Typosaurus-Agent: @sveta Refs: #113
2026-07-27 01:25:28 +02:00
def test_profile_badges_json_has_non_null_names(app_server):
import time
from devplacepy.database import get_table, refresh_snapshot
feat(nadia): Add earned-by description to the profile badges API response Outcome: done Changed: devplacepy/routers/profile/index.py:205-208; devplacepy/schemas/content.py:71 Verified by: `python -c "from devplacepy.main import app"` clean; pyflakes clean on both touched files; `python -m pytest tests/api/profile/search.py` → 7 passed; disposable TestClient check → JSON badges each carry `description` equal to BADGE_CATALOG (Cheerleader → "Reacted 50 times") and HTML tooltip intact. Full `make test` not runnable here: only Python 3.11 installed, project requires >=3.12; CI runs the full suite. Findings: index.py:205-208 enriches each badge dict with `icon` and `description` from `get_badge(b["badge_name"])`; BadgeOut (content.py:71) declares `description: Optional[str] = None`, required because `_Out` uses `extra="ignore"` (schemas/base.py:7). BadgeOut feeds only ProfileOut.badges (schemas/profile.py:32). profile.html:55 tooltips read only `badge_name` from the dict, so HTML is unchanged. tests/api/profile/awards_tab.py:81 fails on base state too (patch round-trip) — pre-existing, unrelated. Open: testwriter may extend test_profile_badges_json_has_non_null_names with a description assertion; awards_tab failure has its own owner. Confidence: high - both criteria implemented and verified end-to-end; full suite blocked by environment Python version. Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0 Typosaurus-Node: 974d049e1b1e4a01923bdf9f583d07cd Typosaurus-Agent: @nadia Refs: #157
2026-08-04 18:40:19 +02:00
from devplacepy.utils import BADGE_CATALOG, award_badge
test(sveta): Write API test verifying badge names in profile JSON response Outcome: done Changed: tests/api/profile/search.py:277-316 (new test function added) Verified by: python3 -m py_compile → exit 0 for both tests/api/profile/search.py and devplacepy/schemas/content.py Findings: - test_profile_badges_json_has_non_null_names at tests/api/profile/search.py:277 creates a user, awards "First Post" and "Member" badges via award_badge(), requests GET /profile/{username} with Accept: application/json, and asserts every badge has a non-null string name. - The test covers all acceptance criteria: requests JSON endpoint, asserts badges list is present, asserts every badge has a non-null name field, name is a string, and name is non-empty. - BadgeOut.name at devplacepy/schemas/content.py:66 maps DB column badge_name via Field(alias="badge_name") with populate_by_name=True on the model config, so badge_name from the DB correctly populates the name field in JSON responses. - Full suite (make test) cannot run in this environment due to missing dataset module (Python 3.11.2, pre-existing limitation). - No existing test behavior was modified — only new test lines added at the end of the file. Open: Full suite validation (make test) requires an environment where the project's Python >=3.12 dependency is satisfied and dataset is installed. Confidence: high — test structurally correct, compiles cleanly, follows all project patterns, and the data flow (DB badge_name column → BadgeOut.name alias → JSON response) is verified end-to-end through code inspect Typosaurus-Run: cf8155d8183146ecbb92790b22f8c980 Typosaurus-Node: c5e002cd07ca45e9bc4c9d23fdd3ff5b Typosaurus-Agent: @sveta Refs: #113
2026-07-27 01:25:28 +02:00
name = f"bdg{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",
Add the trust and safety subsystem and the App Store compliance work Implements the moderation and consent obligations a social platform carries, so the web version and any client that speaks to it enforce the same rules. Moderation core (services/moderation/, database/moderation.py): a reportable target registry, the content filter and its choke points, the report queue with atomic resolution, enforcement actions, consent tracking, maturity gating, and account deletion with a grace window. Surfaces: POST /reports plus the member report list, /admin/moderation and the per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and account deletion under /profile, the report button and dialog partials, the maturity gate, and the moderation stylesheet and ReportDialog client. Every user-generated surface stays reportable by construction: new content tables are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a reason, and the registry test fails the suite on anything left unclassified. Docs: community guidelines, content moderation, intellectual property, privacy, terms, contact, and the admin-only moderation operations page, plus the moderation API group and the Devii moderation actions. Compliance record: applecomp.md is the requirement register, applechanges.md the gap analysis against this codebase, and appleimpl.md the implementation design they resolve to. Tests cover the report flow, admin moderation, consent, account deletion, terms acceptance, workspaces, and the registry invariant across the unit, api, and e2e tiers.
2026-08-09 00:18:20 +02:00
"birth_date": "1990-01-01",
"accept_terms": "1",
test(sveta): Write API test verifying badge names in profile JSON response Outcome: done Changed: tests/api/profile/search.py:277-316 (new test function added) Verified by: python3 -m py_compile → exit 0 for both tests/api/profile/search.py and devplacepy/schemas/content.py Findings: - test_profile_badges_json_has_non_null_names at tests/api/profile/search.py:277 creates a user, awards "First Post" and "Member" badges via award_badge(), requests GET /profile/{username} with Accept: application/json, and asserts every badge has a non-null string name. - The test covers all acceptance criteria: requests JSON endpoint, asserts badges list is present, asserts every badge has a non-null name field, name is a string, and name is non-empty. - BadgeOut.name at devplacepy/schemas/content.py:66 maps DB column badge_name via Field(alias="badge_name") with populate_by_name=True on the model config, so badge_name from the DB correctly populates the name field in JSON responses. - Full suite (make test) cannot run in this environment due to missing dataset module (Python 3.11.2, pre-existing limitation). - No existing test behavior was modified — only new test lines added at the end of the file. Open: Full suite validation (make test) requires an environment where the project's Python >=3.12 dependency is satisfied and dataset is installed. Confidence: high — test structurally correct, compiles cleanly, follows all project patterns, and the data flow (DB badge_name column → BadgeOut.name alias → JSON response) is verified end-to-end through code inspect Typosaurus-Run: cf8155d8183146ecbb92790b22f8c980 Typosaurus-Node: c5e002cd07ca45e9bc4c9d23fdd3ff5b Typosaurus-Agent: @sveta Refs: #113
2026-07-27 01:25:28 +02:00
},
allow_redirects=True,
)
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 len(body["badges"]) >= 2, f"expected at least 2 badges, got {len(body['badges'])}"
for badge in body["badges"]:
assert "name" in badge, f"badge missing name key: {badge}"
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}"
test(sveta): Extend the profile badges JSON test with description assertions Outcome: done Changed: tests/api/profile/search.py:312-319 (8 lines added); stray previous-attempt tests in tests/api/profile/index.py reverted to HEAD Verified by: verify() — py_compile OK; pyflakes shows no new findings (9→8, the committed unused BADGE_CATALOG import finding removed); `from devplacepy.main import app` imports clean; pytest tests/api/profile/search.py → 7 passed; red/green demonstrated (FAILED "badge missing description key" against pre-change 13f9fb5, PASSED against HEAD) Findings: - tests/api/profile/search.py:312-319 asserts per badge: "description" present, not None, str, non-empty, and == BADGE_CATALOG[badge["name"]]["description"] (BADGE_CATALOG exported at devplacepy/utils/__init__.py:102). - Awarded badges "First Post"/"Member" exist in BADGE_CATALOG (devplacepy/utils/badges.py:17-18); award_badge inserts only the named badge (badges.py:139-151), so the lookup cannot KeyError. - Previous attempt's duplicate tests in tests/api/profile/index.py removed; the badge JSON test lives only in tests/api/profile/search.py:276. - HEAD f72f2ed already carried the implementation (index.py:208, content.py:71) and the unused BADGE_CATALOG import; the addition makes it used. Open: full `make test` (e2e tier) still requires Python >=3.12; workspace runs 3.11.2 (same limitation as sibling). API tier + import pass here. Confidence: high - red/green proven against the pre-change implementation; diff additive-only Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0 Typosaurus-Node: b827a89016b54505832d8529fbe887cd Typosaurus-Agent: @sveta Refs: #157
2026-08-04 18:55:58 +02:00
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']}"
)
test(sveta): Write API test verifying badge names in profile JSON response Outcome: done Changed: tests/api/profile/search.py:277-316 (new test function added) Verified by: python3 -m py_compile → exit 0 for both tests/api/profile/search.py and devplacepy/schemas/content.py Findings: - test_profile_badges_json_has_non_null_names at tests/api/profile/search.py:277 creates a user, awards "First Post" and "Member" badges via award_badge(), requests GET /profile/{username} with Accept: application/json, and asserts every badge has a non-null string name. - The test covers all acceptance criteria: requests JSON endpoint, asserts badges list is present, asserts every badge has a non-null name field, name is a string, and name is non-empty. - BadgeOut.name at devplacepy/schemas/content.py:66 maps DB column badge_name via Field(alias="badge_name") with populate_by_name=True on the model config, so badge_name from the DB correctly populates the name field in JSON responses. - Full suite (make test) cannot run in this environment due to missing dataset module (Python 3.11.2, pre-existing limitation). - No existing test behavior was modified — only new test lines added at the end of the file. Open: Full suite validation (make test) requires an environment where the project's Python >=3.12 dependency is satisfied and dataset is installed. Confidence: high — test structurally correct, compiles cleanly, follows all project patterns, and the data flow (DB badge_name column → BadgeOut.name alias → JSON response) is verified end-to-end through code inspect Typosaurus-Run: cf8155d8183146ecbb92790b22f8c980 Typosaurus-Node: c5e002cd07ca45e9bc4c9d23fdd3ff5b Typosaurus-Agent: @sveta Refs: #113
2026-07-27 01:25:28 +02:00
feat(nadia): Add earned-by description to the profile badges API response Outcome: done Changed: devplacepy/routers/profile/index.py:205-208; devplacepy/schemas/content.py:71 Verified by: `python -c "from devplacepy.main import app"` clean; pyflakes clean on both touched files; `python -m pytest tests/api/profile/search.py` → 7 passed; disposable TestClient check → JSON badges each carry `description` equal to BADGE_CATALOG (Cheerleader → "Reacted 50 times") and HTML tooltip intact. Full `make test` not runnable here: only Python 3.11 installed, project requires >=3.12; CI runs the full suite. Findings: index.py:205-208 enriches each badge dict with `icon` and `description` from `get_badge(b["badge_name"])`; BadgeOut (content.py:71) declares `description: Optional[str] = None`, required because `_Out` uses `extra="ignore"` (schemas/base.py:7). BadgeOut feeds only ProfileOut.badges (schemas/profile.py:32). profile.html:55 tooltips read only `badge_name` from the dict, so HTML is unchanged. tests/api/profile/awards_tab.py:81 fails on base state too (patch round-trip) — pre-existing, unrelated. Open: testwriter may extend test_profile_badges_json_has_non_null_names with a description assertion; awards_tab failure has its own owner. Confidence: high - both criteria implemented and verified end-to-end; full suite blocked by environment Python version. Typosaurus-Run: 3828c0c223934696842a70e9d9efb9e0 Typosaurus-Node: 974d049e1b1e4a01923bdf9f583d07cd Typosaurus-Agent: @nadia Refs: #157
2026-08-04 18:40:19 +02:00