Skip to main content
Glama
schema_validators.py8.89 kB
"""Schema validation helpers for tests. Provides factory functions that guarantee valid schema structures, reducing test maintenance burden when schema evolves. """ from __future__ import annotations import uuid from typing import Any def create_valid_insight( summary: str, importance: int, *, insight_id: str | None = None, supporting_queries: list[dict[str, Any]] | None = None, citations: list[dict[str, Any]] | None = None, status: str = "active", draft_changes: dict[str, Any] | None = None, ) -> dict[str, Any]: """Create insight with guaranteed valid schema. Args: summary: Insight summary text (required) importance: Importance score 0-10 (required) insight_id: Optional UUID string (auto-generates if None) supporting_queries: Optional list of query dicts citations: Optional list of citation dicts status: Insight status (default: "active") draft_changes: Optional draft changes dict Returns: Valid insight dict matching InsightChange schema Example: >>> insight = create_valid_insight("Revenue grew 25%", importance=9) >>> insight = create_valid_insight( ... "Key finding", ... importance=8, ... citations=[{"execution_id": "qid-123"}] ... ) """ if not 0 <= importance <= 10: raise ValueError(f"importance must be 0-10, got {importance}") insight: dict[str, Any] = { "summary": summary, "importance": importance, } # Only include optional fields if provided if insight_id is not None: # Validate UUID format try: uuid.UUID(insight_id) except ValueError as e: raise ValueError(f"insight_id must be valid UUID: {e}") from e insight["insight_id"] = insight_id if supporting_queries is not None: insight["supporting_queries"] = supporting_queries if citations is not None: insight["citations"] = citations if status != "active": insight["status"] = status if draft_changes is not None: insight["draft_changes"] = draft_changes return insight def create_valid_section( title: str, order: int, *, section_id: str | None = None, content: str | None = None, content_format: str = "markdown", notes: str | None = None, insight_ids: list[str] | None = None, insights: list[dict[str, Any]] | None = None, insight_ids_to_add: list[str] | None = None, insight_ids_to_remove: list[str] | None = None, ) -> dict[str, Any]: """Create section with guaranteed valid schema. Args: title: Section title (required) order: Section order/position (required, >= 0) section_id: Optional UUID string (auto-generates if None) content: Optional markdown/HTML content content_format: Content format (default: "markdown") notes: Optional private notes insight_ids: Optional list of linked insight IDs insights: Optional inline insights (creates section + insights together) insight_ids_to_add: For sections_to_modify - IDs to link insight_ids_to_remove: For sections_to_modify - IDs to unlink Returns: Valid section dict matching SectionChange schema Example: >>> section = create_valid_section("Revenue Analysis", order=1) >>> section = create_valid_section( ... "Findings", ... order=2, ... insights=[create_valid_insight("Finding", 8)] ... ) """ if order < 0: raise ValueError(f"order must be >= 0, got {order}") if content_format not in ("markdown", "html", "plain"): raise ValueError(f"content_format must be markdown/html/plain, got {content_format}") section: dict[str, Any] = { "title": title, "order": order, } # Only include optional fields if provided if section_id is not None: # Validate UUID format try: uuid.UUID(section_id) except ValueError as e: raise ValueError(f"section_id must be valid UUID: {e}") from e section["section_id"] = section_id if content is not None: section["content"] = content if content_format != "markdown": section["content_format"] = content_format if notes is not None: section["notes"] = notes if insight_ids is not None: section["insight_ids"] = insight_ids if insights is not None: section["insights"] = insights if insight_ids_to_add is not None: section["insight_ids_to_add"] = insight_ids_to_add if insight_ids_to_remove is not None: section["insight_ids_to_remove"] = insight_ids_to_remove return section def create_mock_citation( execution_id: str = "mock-execution-id", *, query_text: str | None = None, row_count: int | None = None, timestamp: str | None = None, ) -> dict[str, Any]: """Create mock citation for test insights. Args: execution_id: Query execution ID (default: "mock-execution-id") query_text: Optional SQL query text row_count: Optional result row count timestamp: Optional ISO timestamp Returns: Valid citation dict Example: >>> citation = create_mock_citation("qid-123") >>> citation = create_mock_citation( ... "qid-456", ... query_text="SELECT * FROM sales", ... row_count=100 ... ) """ citation: dict[str, Any] = {"execution_id": execution_id} if query_text is not None: citation["query_text"] = query_text if row_count is not None: citation["row_count"] = row_count if timestamp is not None: citation["timestamp"] = timestamp return citation def create_insight_with_citation( summary: str, importance: int, execution_id: str = "mock-qid-123", **kwargs: Any, ) -> dict[str, Any]: """Create insight with mock citation (for tests requiring citations). Convenience wrapper around create_valid_insight that adds a mock citation. Args: summary: Insight summary text importance: Importance score 0-10 execution_id: Mock execution ID for citation **kwargs: Additional insight fields Returns: Valid insight dict with citation Example: >>> insight = create_insight_with_citation("Revenue up 25%", 9) >>> # Automatically includes citation with execution_id """ citation = create_mock_citation(execution_id) return create_valid_insight( summary, importance, citations=[citation], **kwargs, ) def create_section_with_insights( title: str, order: int, insights: list[tuple[str, int]], **kwargs: Any, ) -> dict[str, Any]: """Create section with inline insights (avoids section_id validation). Args: title: Section title order: Section order insights: List of (summary, importance) tuples **kwargs: Additional section fields Returns: Valid section dict with inline insights Example: >>> section = create_section_with_insights( ... "Findings", ... order=1, ... insights=[ ... ("Revenue grew 25%", 9), ... ("Costs decreased 10%", 8), ... ] ... ) """ insight_dicts = [create_valid_insight(summary, importance) for summary, importance in insights] return create_valid_section( title, order, insights=insight_dicts, **kwargs, ) def validate_uuid_format(value: str, field_name: str = "ID") -> str: """Validate that a string is a valid UUID. Args: value: String to validate field_name: Field name for error message Returns: The validated UUID string Raises: ValueError: If value is not a valid UUID Example: >>> validate_uuid_format("123e4567-e89b-12d3-a456-426614174000") '123e4567-e89b-12d3-a456-426614174000' >>> validate_uuid_format("not-a-uuid") # Raises ValueError """ try: uuid.UUID(value) return value except ValueError as e: raise ValueError(f"{field_name} must be valid UUID: {value}") from e def validate_importance(value: int) -> int: """Validate importance score is in valid range. Args: value: Importance score to validate Returns: The validated importance score Raises: ValueError: If value not in 0-10 range Example: >>> validate_importance(5) 5 >>> validate_importance(15) # Raises ValueError """ if not 0 <= value <= 10: raise ValueError(f"importance must be 0-10, got {value}") return value

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Evan-Kim2028/igloo-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server