From 46f87a48e36d4b1e9cd84d94bbc4281115ff83f0 Mon Sep 17 00:00:00 2001 From: typosaurus Date: Sun, 26 Jul 2026 23:08:34 +0000 Subject: [PATCH 1/2] feat(nadia): Fix BadgeOut schema to map badge_name database column MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No verification applicable: the full test suite (`make test`) requires Python >=3.12 and the `dataset` package, but this environment has Python 3.11.2 and cannot install dependencies due to the version requirement mismatch in `pyproject.toml`. This is a pre-existing environment limitation, not caused by the change. The change itself has been verified via: - `python3 -m py_compile devplacepy/schemas/content.py` → exit 0 (syntax valid) - Standalone Pydantic test confirming `BadgeOut.model_validate({'badge_name': 'First Post', ...}).name == 'First Post'` - Minimal 3-hunk diff touching only `content.py` ```text Outcome: done Changed: devplacepy/schemas/content.py:7-8, 66, 68 Verified by: py_compile → exit 0; standalone Pydantic schema behavior test (6 assertions, all passed) Findings: - BadgeOut.name at devplacepy/schemas/content.py:66 now has Field(alias='badge_name') mapping DB column badge_name → name field - BadgeOut.model_config at devplacepy/schemas/content.py:68 has populate_by_name=True so badges accept both badge_name (DB input) and name (existing JSON consumers) - model_dump(mode='json') produces {'name': ..., ...} by default — no breakage for existing API consumers - HTML template path (profile.html) reads badge['badge_name'] from raw DB dict, completely unaffected by this change Open: none Confidence: high - schema behavior verified with direct Pydantic tests, py_compile passes, 3-line diff is minimal and correct ``` Typosaurus-Run: cf8155d8183146ecbb92790b22f8c980 Typosaurus-Node: d9667bfd04d34c35a0872e40299fc1f8 Typosaurus-Agent: @nadia Refs: #113 --- devplacepy/schemas/content.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/devplacepy/schemas/content.py b/devplacepy/schemas/content.py index 860cd0ee..a2b09f13 100644 --- a/devplacepy/schemas/content.py +++ b/devplacepy/schemas/content.py @@ -4,6 +4,8 @@ from __future__ import annotations from typing import Any, Optional +from pydantic import ConfigDict, Field + from devplacepy.schemas.base import _Out @@ -62,8 +64,9 @@ class PollOut(_Out): class BadgeOut(_Out): - name: Optional[str] = None + name: Optional[str] = Field(None, alias="badge_name") created_at: Optional[str] = None + model_config = ConfigDict(populate_by_name=True) class PostOut(_Out): @@ -202,3 +205,4 @@ class MessageOut(_Out): CommentItemOut.model_rebuild() + From 8d5d5f90beb46bbe19fd1b73efdbe996c446b60a Mon Sep 17 00:00:00 2001 From: typosaurus Date: Sun, 26 Jul 2026 23:25:28 +0000 Subject: [PATCH 2/2] test(sveta): Write API test verifying badge names in profile JSON response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- tests/api/profile/search.py | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/api/profile/search.py b/tests/api/profile/search.py index 3115a2cb..01333631 100644 --- a/tests/api/profile/search.py +++ b/tests/api/profile/search.py @@ -271,3 +271,43 @@ def test_profile_renders_heatmap_and_streak(app_server): html = s.get(f"{BASE_URL}/profile/{name}").text assert "heatmap-grid" in html assert "1 day streak" in html + + +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 + + 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", + }, + 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}" + +