feat: add date validation and resolution for results endpoint

This commit is contained in:
2025-11-07 19:18:35 -05:00
parent 29c326a31f
commit 5c95180941
2 changed files with 122 additions and 0 deletions

View File

@@ -3,6 +3,8 @@
from fastapi import APIRouter, Query, Depends
from typing import Optional, Literal
import json
import os
from datetime import datetime, timedelta
from api.database import Database
@@ -14,6 +16,65 @@ def get_database() -> Database:
return Database()
def validate_and_resolve_dates(
start_date: Optional[str],
end_date: Optional[str]
) -> tuple[str, str]:
"""Validate and resolve date parameters.
Args:
start_date: Start date (YYYY-MM-DD) or None
end_date: End date (YYYY-MM-DD) or None
Returns:
Tuple of (resolved_start_date, resolved_end_date)
Raises:
ValueError: If dates are invalid
"""
# Default lookback days
default_lookback = int(os.getenv("DEFAULT_RESULTS_LOOKBACK_DAYS", "30"))
# Handle None cases
if start_date is None and end_date is None:
# Default to last N days
end_dt = datetime.now()
start_dt = end_dt - timedelta(days=default_lookback)
return start_dt.strftime("%Y-%m-%d"), end_dt.strftime("%Y-%m-%d")
if start_date is None:
# Only end_date provided -> single date
start_date = end_date
if end_date is None:
# Only start_date provided -> single date
end_date = start_date
# Validate date formats
try:
start_dt = datetime.strptime(start_date, "%Y-%m-%d")
end_dt = datetime.strptime(end_date, "%Y-%m-%d")
# Ensure strict YYYY-MM-DD format (e.g., reject "2025-1-16")
if start_date != start_dt.strftime("%Y-%m-%d"):
raise ValueError(f"Invalid date format. Expected YYYY-MM-DD")
if end_date != end_dt.strftime("%Y-%m-%d"):
raise ValueError(f"Invalid date format. Expected YYYY-MM-DD")
except ValueError:
raise ValueError(f"Invalid date format. Expected YYYY-MM-DD")
# Validate order
if start_dt > end_dt:
raise ValueError("start_date must be <= end_date")
# Validate not future
now = datetime.now()
if start_dt.date() > now.date() or end_dt.date() > now.date():
raise ValueError("Cannot query future dates")
return start_date, end_date
@router.get("/results")
async def get_results(
job_id: Optional[str] = None,

View File

@@ -0,0 +1,61 @@
"""Tests for results_v2 endpoint date validation."""
import pytest
from datetime import datetime, timedelta
from api.routes.results_v2 import validate_and_resolve_dates
def test_validate_no_dates_provided():
"""Test default to last 30 days when no dates provided."""
start, end = validate_and_resolve_dates(None, None)
# Should default to last 30 days
end_dt = datetime.strptime(end, "%Y-%m-%d")
start_dt = datetime.strptime(start, "%Y-%m-%d")
assert (end_dt - start_dt).days == 30
assert end_dt.date() <= datetime.now().date()
def test_validate_only_start_date():
"""Test single date when only start_date provided."""
start, end = validate_and_resolve_dates("2025-01-16", None)
assert start == "2025-01-16"
assert end == "2025-01-16"
def test_validate_only_end_date():
"""Test single date when only end_date provided."""
start, end = validate_and_resolve_dates(None, "2025-01-16")
assert start == "2025-01-16"
assert end == "2025-01-16"
def test_validate_both_dates():
"""Test date range when both provided."""
start, end = validate_and_resolve_dates("2025-01-16", "2025-01-20")
assert start == "2025-01-16"
assert end == "2025-01-20"
def test_validate_invalid_date_format():
"""Test error on invalid date format."""
with pytest.raises(ValueError, match="Invalid date format"):
validate_and_resolve_dates("2025-1-16", "2025-01-20")
def test_validate_start_after_end():
"""Test error when start_date > end_date."""
with pytest.raises(ValueError, match="start_date must be <= end_date"):
validate_and_resolve_dates("2025-01-20", "2025-01-16")
def test_validate_future_date():
"""Test error when dates are in future."""
future = (datetime.now() + timedelta(days=10)).strftime("%Y-%m-%d")
with pytest.raises(ValueError, match="Cannot query future dates"):
validate_and_resolve_dates(future, future)