PITCHFIGHT_AI / tests /test_deck_critique.py
keikei2023's picture
Expose deck critique via Gradio API
5979324 verified
Raw
History Blame
6.67 kB
import json
import importlib
import types
import unittest
from pathlib import Path
from unittest.mock import patch
import sys
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
# Keep the handler unit test lightweight. The app imports optional runtime
# clients at module import time, but this test never calls external services.
dotenv_module = types.ModuleType("dotenv")
dotenv_module.load_dotenv = lambda *args, **kwargs: None
sys.modules.setdefault("dotenv", dotenv_module)
openai_module = types.ModuleType("openai")
openai_module.OpenAI = object
openai_module.APIConnectionError = type("APIConnectionError", (Exception,), {})
openai_module.APIStatusError = type("APIStatusError", (Exception,), {})
openai_module.APITimeoutError = type("APITimeoutError", (Exception,), {})
sys.modules.setdefault("openai", openai_module)
pymongo_module = types.ModuleType("pymongo")
pymongo_module.MongoClient = object
pymongo_errors_module = types.ModuleType("pymongo.errors")
pymongo_collection_module = types.ModuleType("pymongo.collection")
pymongo_collection_module.Collection = object
pymongo_database_module = types.ModuleType("pymongo.database")
pymongo_database_module.Database = object
pymongo_errors_module.PyMongoError = type("PyMongoError", (Exception,), {})
pymongo_errors_module.ServerSelectionTimeoutError = type(
"ServerSelectionTimeoutError", (Exception,), {}
)
sys.modules.setdefault("pymongo", pymongo_module)
sys.modules.setdefault("pymongo.collection", pymongo_collection_module)
sys.modules.setdefault("pymongo.database", pymongo_database_module)
sys.modules.setdefault("pymongo.errors", pymongo_errors_module)
class FakeServer:
def __init__(self):
self.routes = []
self.api_routes = {}
def _route(self, method, path=None, **kwargs):
def decorator(func):
self.routes.append((method, path, kwargs, func))
return func
return decorator
def get(self, path, **kwargs):
return self._route("GET", path, **kwargs)
def post(self, path, **kwargs):
return self._route("POST", path, **kwargs)
def api(self, name):
def decorator(func):
self.api_routes[name] = func
return func
return decorator
def mount(self, *args, **kwargs):
return None
fastapi_module = types.ModuleType("fastapi")
fastapi_module.Body = lambda *args, **kwargs: None
fastapi_responses_module = types.ModuleType("fastapi.responses")
fastapi_responses_module.HTMLResponse = str
fastapi_staticfiles_module = types.ModuleType("fastapi.staticfiles")
fastapi_staticfiles_module.StaticFiles = lambda *args, **kwargs: object()
gradio_module = types.ModuleType("gradio")
gradio_module.Server = FakeServer
sys.modules.setdefault("fastapi", fastapi_module)
sys.modules.setdefault("fastapi.responses", fastapi_responses_module)
sys.modules.setdefault("fastapi.staticfiles", fastapi_staticfiles_module)
sys.modules.setdefault("gradio", gradio_module)
from core.api_handlers import handle_deck_critique
DECK_TEXT = """
EventRadar AI
Problem: students miss hackathons because events are scattered across WhatsApp, LinkedIn, Luma, and club pages.
Solution: an AI event discovery assistant ranks events by skills, goals, location, and deadline urgency.
Users: college students and student founders.
Traction: tested with 80 students; 62 missed at least one relevant event last month.
Ask: mentor feedback and pilot introductions.
"""
class DeckCritiqueTests(unittest.TestCase):
def test_deck_critique_returns_local_fallback_instead_of_placeholder(self):
with patch(
"core.api_handlers.model_router.generate_deck_critique_response",
return_value={
"ok": False,
"model_mode": "premium_nvidia",
"provider": "nvidia",
"content": "",
"error": "model unavailable",
},
):
result = handle_deck_critique({"deck_text": DECK_TEXT})
self.assertTrue(result["ok"])
self.assertNotEqual(result.get("status"), "not_implemented")
self.assertEqual(result["provider"], "local")
self.assertFalse(result["model_ok"])
self.assertTrue(result["summary"])
self.assertGreaterEqual(len(result["strengths"]), 1)
self.assertGreaterEqual(len(result["risks"]), 1)
self.assertGreaterEqual(len(result["recommended_fixes"]), 1)
self.assertTrue(result["next_pitch_question"].endswith("?"))
def test_deck_critique_requires_text_or_startup_context(self):
result = handle_deck_critique({})
self.assertFalse(result["ok"])
self.assertIn("deck_text", result["error"])
def test_deck_critique_uses_model_json_when_available(self):
model_payload = {
"summary": "The deck has a focused student-founder wedge but needs sharper proof.",
"strengths": ["Clear student pain", "Specific initial traction"],
"risks": ["Business model is still vague"],
"recommended_fixes": ["Add one slide on who pays and why this is urgent"],
"next_pitch_question": "Who pays for this, and what evidence shows they will pay?",
}
with patch(
"core.api_handlers.model_router.generate_deck_critique_response",
return_value={
"ok": True,
"model_mode": "premium_nvidia",
"provider": "nvidia",
"content": json.dumps(model_payload),
"error": None,
},
create=True,
):
result = handle_deck_critique(
{"deck_text": DECK_TEXT, "model_mode": "premium_nvidia"}
)
self.assertTrue(result["ok"])
self.assertTrue(result["model_ok"])
self.assertEqual(result["provider"], "nvidia")
self.assertEqual(result["summary"], model_payload["summary"])
self.assertEqual(result["recommended_fixes"], model_payload["recommended_fixes"])
def test_deck_critique_is_registered_as_gradio_api(self):
pitchfight_app = importlib.import_module("app")
self.assertIn("deck_critique", pitchfight_app.app.api_routes)
with patch(
"app.handle_deck_critique",
return_value={"ok": True, "summary": "registered"},
) as handler:
result = pitchfight_app.app.api_routes["deck_critique"]({"deck_text": DECK_TEXT})
handler.assert_called_once_with({"deck_text": DECK_TEXT})
self.assertEqual(result["summary"], "registered")
if __name__ == "__main__":
unittest.main()