launch-desk / tests /test_agent.py
Big Brain Ape
Deploy Launch Desk to HuggingFace Spaces
b484b4d
Raw History Blame Contribute Delete
8.14 kB
"""
Launch Desk — Agent & Tool Tests
================================
Pytest tests that verify the agent has 4 tools registered and that each
tool returns the expected structure.
Because @function_tool wraps functions in a FunctionTool dataclass, we
access the original callable via `tool.on_invoke_tool._get_wrapped_callable()`.
"""
import pytest
from server.agent import agent
from server.tools import (
extract_tasks,
check_readiness,
generate_checklist,
draft_copy,
)
# ---------------------------------------------------------------------------
# Helper: get the original wrapped callable from a FunctionTool
# ---------------------------------------------------------------------------
def _get_callable(tool):
"""Extract the original Python function from a @function_tool wrapper."""
return tool.on_invoke_tool._get_wrapped_callable()
# ---------------------------------------------------------------------------
# Test: agent has 4 tools registered
# ---------------------------------------------------------------------------
def test_agent_has_four_tools():
"""The Launch Planner agent should have exactly 4 tools registered."""
assert agent is not None
assert agent.name == "Launch Planner"
assert agent.tools is not None
assert len(agent.tools) == 4, f"Expected 4 tools, got {len(agent.tools)}"
def test_agent_has_expected_tool_names():
"""The agent should have tools with the expected names."""
tool_names = {t.name for t in agent.tools}
expected = {"extract_tasks", "check_readiness", "generate_checklist", "draft_copy"}
assert tool_names == expected, f"Expected {expected}, got {tool_names}"
# ---------------------------------------------------------------------------
# Test: extract_tasks returns expected structure
# ---------------------------------------------------------------------------
def test_extract_tasks_returns_list_of_dicts():
"""extract_tasks should return a list of dicts with task/priority/owner/deadline."""
fn = _get_callable(extract_tasks)
brief = "We are launching a new API with backend, QA testing, and marketing campaign"
result = fn(brief)
assert isinstance(result, list)
assert len(result) > 0
for task in result:
assert isinstance(task, dict)
assert "task" in task
assert "priority" in task
assert "owner" in task
assert "deadline" in task
def test_extract_tasks_handles_empty_brief():
"""extract_tasks should handle an empty brief gracefully."""
fn = _get_callable(extract_tasks)
result = fn("")
assert isinstance(result, list)
assert len(result) > 0
assert "task" in result[0]
def test_extract_tasks_priority_values():
"""All priorities should be high, medium, or low."""
fn = _get_callable(extract_tasks)
result = fn("API, QA, docs, marketing, support, security, pricing, analytics, design, legal")
valid_priorities = {"high", "medium", "low"}
for task in result:
assert task["priority"] in valid_priorities
# ---------------------------------------------------------------------------
# Test: check_readiness returns expected structure
# ---------------------------------------------------------------------------
def test_check_readiness_returns_dict():
"""check_readiness should return a dict with ready/score/gaps."""
fn = _get_callable(check_readiness)
tasks = "code freeze, QA testing, marketing campaign, documentation, support staffing"
result = fn(tasks, "2026-10-01")
assert isinstance(result, dict)
assert "ready" in result
assert "score" in result
assert "gaps" in result
assert isinstance(result["ready"], bool)
assert isinstance(result["score"], int)
assert isinstance(result["gaps"], list)
def test_check_readiness_high_score_when_covered():
"""check_readiness should give a high score when all rubric areas are covered."""
fn = _get_callable(check_readiness)
tasks = "code freeze, QA testing, marketing campaign, documentation, support staffing"
result = fn(tasks, "2026-10-01")
assert result["score"] >= 80, f"Expected high score, got {result['score']}"
def test_check_readiness_low_score_when_gaps():
"""check_readiness should give a low score when rubric areas are missing."""
fn = _get_callable(check_readiness)
tasks = "just some random text with no keywords"
result = fn(tasks, "2026-10-01")
assert result["score"] < 100
assert len(result["gaps"]) > 0
# ---------------------------------------------------------------------------
# Test: generate_checklist returns expected structure
# ---------------------------------------------------------------------------
def test_generate_checklist_returns_list_of_dicts():
"""generate_checklist should return a list of {owner, items} dicts."""
fn = _get_callable(generate_checklist)
import json
tasks_json = json.dumps([
{"task": "Freeze code", "owner": "Engineering", "priority": "high", "deadline": "T-14"},
{"task": "QA testing", "owner": "QA", "priority": "high", "deadline": "T-7"},
{"task": "Marketing campaign", "owner": "Marketing", "priority": "high", "deadline": "T-5"},
])
result = fn(tasks_json)
assert isinstance(result, list)
assert len(result) > 0
for checklist in result:
assert isinstance(checklist, dict)
assert "owner" in checklist
assert "items" in checklist
assert isinstance(checklist["items"], list)
assert len(checklist["items"]) > 0
for item in checklist["items"]:
assert isinstance(item, str)
def test_generate_checklist_includes_launch_lead():
"""generate_checklist should include a Launch Lead checklist."""
fn = _get_callable(generate_checklist)
result = fn("[]")
owners = [c["owner"] for c in result]
assert "Launch Lead" in owners
def test_generate_checklist_groups_by_owner():
"""generate_checklist should group tasks by owner."""
import json
fn = _get_callable(generate_checklist)
tasks_json = json.dumps([
{"task": "Task A", "owner": "Engineering"},
{"task": "Task B", "owner": "Engineering"},
{"task": "Task C", "owner": "Marketing"},
])
result = fn(tasks_json)
owners = {c["owner"] for c in result}
assert "Engineering" in owners
assert "Marketing" in owners
# ---------------------------------------------------------------------------
# Test: draft_copy returns expected structure
# ---------------------------------------------------------------------------
def test_draft_copy_returns_dict():
"""draft_copy should return a dict with headline/body/cta."""
fn = _get_callable(draft_copy)
result = fn("email", "LaunchDesk Pro", "enterprise DevOps teams")
assert isinstance(result, dict)
assert "headline" in result
assert "body" in result
assert "cta" in result
assert isinstance(result["headline"], str)
assert isinstance(result["body"], str)
assert isinstance(result["cta"], str)
def test_draft_copy_channel_specific():
"""draft_copy should produce different copy for different channels."""
fn = _get_callable(draft_copy)
email = fn("email", "ProductX", "developers")
twitter = fn("twitter", "ProductX", "developers")
assert email["headline"] != twitter["headline"], "Copy should differ by channel"
def test_draft_copy_includes_product_and_audience():
"""draft_copy should reference the product name and audience."""
fn = _get_callable(draft_copy)
result = fn("email", "MyProduct", "data scientists")
assert "MyProduct" in result["headline"] or "MyProduct" in result["body"]
assert "data scientists" in result["headline"] or "data scientists" in result["body"]
def test_draft_copy_handles_unknown_channel():
"""draft_copy should handle an unknown channel gracefully."""
fn = _get_callable(draft_copy)
result = fn("radio", "ProductX", "general audience")
assert isinstance(result, dict)
assert "headline" in result
assert "body" in result
assert "cta" in result