feat: feedback_api — build_issue_body

This commit is contained in:
pyr0ball 2026-03-03 12:00:01 -08:00
parent 6764ad4288
commit 1940cfb131
2 changed files with 102 additions and 0 deletions

View file

@ -82,3 +82,46 @@ def collect_listings(db_path: Path | None = None, n: int = 5) -> list[dict]:
).fetchall() ).fetchall()
conn.close() conn.close()
return [{"title": r["title"], "company": r["company"], "url": r["url"]} for r in rows] return [{"title": r["title"], "company": r["company"], "url": r["url"]} for r in rows]
def build_issue_body(form: dict, context: dict, attachments: dict) -> str:
"""Assemble the Forgejo issue markdown body from form data, context, and attachments."""
_TYPE_LABELS = {"bug": "🐛 Bug", "feature": "✨ Feature Request", "other": "💬 Other"}
lines: list[str] = [
f"## {_TYPE_LABELS.get(form.get('type', 'other'), '💬 Other')}",
"",
form.get("description", ""),
"",
]
if form.get("type") == "bug" and form.get("repro"):
lines += ["### Reproduction Steps", "", form["repro"], ""]
if context:
lines += ["### Context", ""]
for k, v in context.items():
lines.append(f"- **{k}:** {v}")
lines.append("")
if attachments.get("logs"):
lines += [
"<details>",
"<summary>App Logs (last 100 lines)</summary>",
"",
"```",
attachments["logs"],
"```",
"</details>",
"",
]
if attachments.get("listings"):
lines += ["### Recent Listings", ""]
for j in attachments["listings"]:
lines.append(f"- [{j['title']} @ {j['company']}]({j['url']})")
lines.append("")
if attachments.get("submitter"):
lines += ["---", f"*Submitted by: {attachments['submitter']}*"]
return "\n".join(lines)

View file

@ -119,3 +119,62 @@ def test_collect_listings_respects_n(tmp_path):
"salary": "", "description": "", "date_found": "2026-03-01", "salary": "", "description": "", "date_found": "2026-03-01",
}) })
assert len(collect_listings(db_path=db, n=3)) == 3 assert len(collect_listings(db_path=db, n=3)) == 3
# ── build_issue_body ──────────────────────────────────────────────────────────
def test_build_issue_body_contains_description():
from scripts.feedback_api import build_issue_body
form = {"type": "bug", "title": "Test", "description": "it broke", "repro": ""}
ctx = {"page": "Home", "version": "v1.0", "tier": "free",
"llm_backend": "ollama", "os": "Linux", "timestamp": "2026-03-03T00:00:00Z"}
body = build_issue_body(form, ctx, {})
assert "it broke" in body
assert "Home" in body
assert "v1.0" in body
def test_build_issue_body_bug_includes_repro():
from scripts.feedback_api import build_issue_body
form = {"type": "bug", "title": "X", "description": "desc", "repro": "step 1\nstep 2"}
body = build_issue_body(form, {}, {})
assert "step 1" in body
assert "Reproduction" in body
def test_build_issue_body_no_repro_for_feature():
from scripts.feedback_api import build_issue_body
form = {"type": "feature", "title": "X", "description": "add dark mode", "repro": "ignored"}
body = build_issue_body(form, {}, {})
assert "Reproduction" not in body
def test_build_issue_body_logs_in_collapsible():
from scripts.feedback_api import build_issue_body
form = {"type": "other", "title": "X", "description": "Y", "repro": ""}
body = build_issue_body(form, {}, {"logs": "log line 1\nlog line 2"})
assert "<details>" in body
assert "log line 1" in body
def test_build_issue_body_omits_logs_when_not_provided():
from scripts.feedback_api import build_issue_body
form = {"type": "bug", "title": "X", "description": "Y", "repro": ""}
body = build_issue_body(form, {}, {})
assert "<details>" not in body
def test_build_issue_body_submitter_attribution():
from scripts.feedback_api import build_issue_body
form = {"type": "bug", "title": "X", "description": "Y", "repro": ""}
body = build_issue_body(form, {}, {"submitter": "Jane Doe <jane@example.com>"})
assert "Jane Doe" in body
def test_build_issue_body_listings_shown():
from scripts.feedback_api import build_issue_body
form = {"type": "bug", "title": "X", "description": "Y", "repro": ""}
listings = [{"title": "CSM", "company": "Acme", "url": "https://example.com/1"}]
body = build_issue_body(form, {}, {"listings": listings})
assert "CSM" in body
assert "Acme" in body