Spaces:
Paused
Paused
Download apps/api/tests/test_presenter_coach.py from nathycodes/presentercoach: direct link, hf CLI and curl.
- Browser
- Download file 9.64 kB
-
https://huggingface.co/spaces/nathycodes/presentercoach/resolve/main/apps/api/tests/test_presenter_coach.py
- Command line
-
hf download hf://spaces/nathycodes/presentercoach/apps/api/tests/test_presenter_coach.py
-
curl -L -o test_presenter_coach.py https://huggingface.co/spaces/nathycodes/presentercoach/resolve/main/apps/api/tests/test_presenter_coach.py
9.64 kB
| import os | |
| import tempfile | |
| import time | |
| from uuid import uuid4 | |
| test_id = uuid4().hex | |
| os.environ["DATABASE_URL"] = f"sqlite:///./test_{test_id}.db" | |
| os.environ["LOCAL_ONLY_MODE"] = "true" | |
| os.environ["STORAGE_DIR"] = os.path.join(tempfile.gettempdir(), f"speech_machine_test_{test_id}") | |
| from fastapi.testclient import TestClient | |
| from app.db import Base, engine | |
| from app.main import app | |
| client = TestClient(app) | |
| Base.metadata.create_all(bind=engine) | |
| _headers: dict[str, str] | None = None | |
| def get_headers() -> dict[str, str]: | |
| global _headers | |
| if _headers is not None: | |
| return _headers | |
| status = client.get("/setup/status").json() | |
| if status["setup_required"]: | |
| res = client.post("/setup", json={"email": "presenter@example.com", "password": "secret123"}) | |
| assert res.status_code == 201 | |
| else: | |
| client.post("/auth/signup", json={"email": "presenter@example.com", "password": "secret123"}) | |
| login = client.post("/auth/login", json={"email": "presenter@example.com", "password": "secret123"}) | |
| assert login.status_code == 200 | |
| _headers = {"Authorization": f"Bearer {login.json()['access_token']}"} | |
| return _headers | |
| def test_curriculum_profile_and_30_day_plan_auto_completion(): | |
| headers = get_headers() | |
| profile_res = client.get("/curriculum/profile", headers=headers) | |
| assert profile_res.status_code == 200 | |
| assert profile_res.json()["target_wpm_min"] == 125 | |
| update_res = client.put( | |
| "/curriculum/profile", | |
| json={ | |
| "display_name": "Ada Presenter", | |
| "primary_role": "Staff Systems Engineer", | |
| "focus_track": "keynote_clarity", | |
| "target_wpm_min": 130, | |
| "target_wpm_max": 155, | |
| "daily_minutes_goal": 20, | |
| "weaknesses": ["filler_words", "eye_contact", "rushing_pace"], | |
| }, | |
| headers=headers, | |
| ) | |
| assert update_res.status_code == 200 | |
| assert update_res.json()["display_name"] == "Ada Presenter" | |
| assert update_res.json()["focus_track"] == "keynote_clarity" | |
| plan_res = client.get("/curriculum/plan", headers=headers) | |
| assert plan_res.status_code == 200 | |
| plan = plan_res.json() | |
| assert plan["summary"]["total_days"] == 30 | |
| assert plan["summary"]["recommended_day"] == 1 | |
| assert len(plan["days"]) == 30 | |
| # Save a Day 1 session and verify Day 1 auto-completes and Day 2 becomes recommended | |
| sess_res = client.post( | |
| "/sessions", | |
| json={ | |
| "title": "Day 1: Baseline 60-Second Tech Intro", | |
| "mode": "Pitch", | |
| "curriculum_day": 1, | |
| "target_keywords": ["building", "technology", "problem", "matters"], | |
| "duration_ms": 60000, | |
| "eye_contact_pct": 0.55, | |
| "transcript_segments": [ | |
| { | |
| "start_ms": 0, | |
| "end_ms": 30000, | |
| "text": "Hi, um I am building a technology product because solving this problem really matters.", | |
| }, | |
| { | |
| "start_ms": 30000, | |
| "end_ms": 60000, | |
| "text": "First we measure speaking pace, um like second we track camera eye contact, therefore every rep improves.", | |
| }, | |
| ], | |
| "metrics": [ | |
| {"t": 1, "wpm": 138, "rms": 55, "eye_contact": 0.55, "smile": 0.4, "yaw": 0.2, "pitch": 0.1, "roll": 0.1, "filler_count": 3, "pause_ms": 900} | |
| ], | |
| }, | |
| headers=headers, | |
| ) | |
| assert sess_res.status_code == 200 | |
| day1_session_id = sess_res.json()["id"] | |
| assert sess_res.json()["curriculum_day"] == 1 | |
| assert sess_res.json()["transcription_status"] == "complete" | |
| updated_plan = client.get("/curriculum/plan", headers=headers).json() | |
| assert updated_plan["summary"]["completed_days"] >= 1 | |
| assert updated_plan["summary"]["recommended_day"] == 2 | |
| assert updated_plan["days"][0]["completed"] is True | |
| assert updated_plan["days"][0]["completed_session_id"] == day1_session_id | |
| note_res = client.post( | |
| "/curriculum/days/1/complete", | |
| json={"reflection_notes": "Felt much calmer speaking right to the lens."}, | |
| headers=headers, | |
| ) | |
| assert note_res.status_code == 200 | |
| assert "calmer" in note_res.json()["reflection_notes"] | |
| def test_targeted_exercises_and_tech_news_storyboard(monkeypatch): | |
| headers = get_headers() | |
| exercises_res = client.get("/curriculum/exercises", headers=headers) | |
| assert exercises_res.status_code == 200 | |
| ex = exercises_res.json() | |
| assert len(ex["daily_warmups"]) >= 4 | |
| assert len(ex["camera_challenges"]) >= 5 | |
| assert len(ex["targeted_exercises"]) >= 2 | |
| # Avoid network dependency in unit test for news feed | |
| from app.services import tech_news | |
| tech_news._FEED_CACHE["timestamp"] = time.time() | |
| tech_news._FEED_CACHE["items"] = [] | |
| feed_res = client.get("/news/feed", headers=headers) | |
| assert feed_res.status_code == 200 | |
| feed = feed_res.json() | |
| assert len(feed["stories"]) >= 6 | |
| assert len(feed["frameworks"]) == 4 | |
| for fw in ("briefing_60s", "explainer_2m", "contrarian_take", "war_story"): | |
| sb_res = client.post( | |
| "/news/storyboard", | |
| json={ | |
| "headline": "SQLite WAL Mode Scales Single-Node Web Apps", | |
| "summary": "Modern NVMe disks let single-node SQLite handle tens of thousands of reads per second.", | |
| "category": "Systems & Cloud", | |
| "framework": fw, | |
| "personal_angle": "Start simple before adopting distributed microservices.", | |
| }, | |
| headers=headers, | |
| ) | |
| assert sb_res.status_code == 200 | |
| sb = sb_res.json() | |
| assert len(sb["sections"]) == 4 | |
| assert "SQLite" in sb["teleprompter_script"] | |
| assert len(sb["target_keywords"]) >= 3 | |
| def test_auto_builtin_coaching_transcript_patch_weekly_review_and_compare(): | |
| headers = get_headers() | |
| s1 = client.post( | |
| "/sessions", | |
| json={ | |
| "title": "Take 1 — Rushed & Filler-Heavy", | |
| "mode": "Storytelling", | |
| "duration_ms": 60000, | |
| "eye_contact_pct": 0.45, | |
| "transcript_segments": [ | |
| { | |
| "start_ms": 0, | |
| "end_ms": 60000, | |
| "text": "Um like basically you know we built an API um like actually very fast um yeah.", | |
| } | |
| ], | |
| "metrics": [ | |
| {"t": 1, "wpm": 175, "rms": 45, "eye_contact": 0.45, "smile": 0.2, "yaw": 0.5, "pitch": 0.4, "roll": 0.2, "filler_count": 6, "pause_ms": 0} | |
| ], | |
| }, | |
| headers=headers, | |
| ).json() | |
| # Auto coaching generates immediately using built-in presenter coach | |
| coach_res = client.get(f"/coaching/session/{s1['id']}?auto=true", headers=headers) | |
| assert coach_res.status_code == 200 | |
| coach = coach_res.json() | |
| assert coach["status"] == "completed" | |
| assert len(coach["strengths"]) >= 2 | |
| assert len(coach["improvements"]) >= 2 | |
| assert len(coach["drills"]) >= 1 | |
| # Patch transcript with cleaner take and verify score + metrics recompute | |
| s2 = client.post( | |
| "/sessions", | |
| json={ | |
| "title": "Take 2 — Crisp Keynote Structure", | |
| "mode": "Pitch", | |
| "duration_ms": 60000, | |
| "eye_contact_pct": 0.82, | |
| "transcript_segments": [], | |
| "metrics": [ | |
| {"t": 1, "wpm": 142, "rms": 62, "eye_contact": 0.82, "smile": 0.55, "yaw": 0.1, "pitch": 0.1, "roll": 0.1, "filler_count": 0, "pause_ms": 850} | |
| ], | |
| }, | |
| headers=headers, | |
| ).json() | |
| patch_res = client.patch( | |
| f"/sessions/{s2['id']}/transcript", | |
| json={ | |
| "transcript_segments": [ | |
| { | |
| "start_ms": 0, | |
| "end_ms": 30000, | |
| "text": "First, our engineering team simplified database queries because tail latency hurt mobile users.", | |
| }, | |
| { | |
| "start_ms": 30000, | |
| "end_ms": 60000, | |
| "text": "Second, response times dropped by seventy percent. Therefore, every page now loads under one second.", | |
| }, | |
| ] | |
| }, | |
| headers=headers, | |
| ) | |
| assert patch_res.status_code == 200 | |
| assert patch_res.json()["transcription_status"] == "complete" | |
| assert patch_res.json()["score"]["total"] > s1["score"]["total"] | |
| # Compare Take 1 vs Take 2 | |
| cmp_res = client.get(f"/dashboard/compare?session_a={s1['id']}&session_b={s2['id']}", headers=headers) | |
| assert cmp_res.status_code == 200 | |
| cmp_data = cmp_res.json() | |
| assert cmp_data["comparison"]["overall_score"]["delta"] > 0 | |
| assert len(cmp_data["insights"]) >= 1 | |
| assert cmp_data["session_b"]["title"] == "Take 2 — Crisp Keynote Structure" | |
| # Weekly review + reflection note | |
| weekly_res = client.get("/dashboard/weekly-review", headers=headers) | |
| assert weekly_res.status_code == 200 | |
| weekly = weekly_res.json() | |
| assert weekly["current_week"]["session_count"] >= 2 | |
| assert len(weekly["wins"]) >= 1 | |
| note_save = client.put( | |
| "/dashboard/weekly-review/note", | |
| json={ | |
| "week_start_date": weekly["week_start_date"], | |
| "wins_note": "Cut fillers dramatically on Take 2!", | |
| "focus_next_week": "Practice 2-minute Tech News explainers from bullet cues.", | |
| }, | |
| headers=headers, | |
| ) | |
| assert note_save.status_code == 200 | |
| assert "Cut fillers" in note_save.json()["wins_note"] | |