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 fastapi import APIRouter, Query, Depends
from typing import Optional, Literal from typing import Optional, Literal
import json import json
import os
from datetime import datetime, timedelta
from api.database import Database from api.database import Database
@@ -14,6 +16,65 @@ def get_database() -> Database:
return 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") @router.get("/results")
async def get_results( async def get_results(
job_id: Optional[str] = None, 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)