Complete Phase 4.1 test suite updates - all 174 tests passing

Fixed all remaining test failures after Pydantic v2 migration:

Core fixes:
- Date handling: Convert date objects to ISO strings across 4 files
- Model serialization: Use model_dump(mode='json') for JSON compatibility
- Optional fields: Made time_period_months Optional[int] in models
- Dict access: Replace .get() with getattr() for dynamic attributes

Test updates:
- Updated 50+ test fixtures from dicts to Deal models
- Fixed date-based tests to use recent dates for time filtering
- Added missing imports (CoordinatePoint, MarketActivityScore, DealStatistics)
- Updated assertions from dict keys to model attributes (snake_case)

Files modified:
- nadlan_mcp/govmap/market_analysis.py
- nadlan_mcp/govmap/statistics.py
- nadlan_mcp/govmap/models.py
- nadlan_mcp/fastmcp_server.py
- tests/test_govmap_client.py
- tests/test_fastmcp_tools.py
- .cursor/plans/TEST-UPDATE-STATUS.md (comprehensive documentation)

Result: 174/174 tests passing (100%) 

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Nitzan Pomerantz
2025-10-26 23:55:42 +02:00
parent 34af8362b9
commit ff8c7e6509
7 changed files with 785 additions and 349 deletions
+11 -7
View File
@@ -7,7 +7,7 @@ Focused on providing data metrics; the LLM interprets them for investment advice
import logging
from collections import defaultdict
from datetime import datetime, timedelta
from datetime import date, datetime, timedelta
from typing import Dict, List, Optional, Tuple
from .models import Deal, MarketActivityScore, InvestmentAnalysis, LiquidityMetrics
@@ -68,11 +68,13 @@ def parse_deal_dates(
deal_dates = []
for deal in deals:
date_str = deal.deal_date
if not date_str:
if not deal.deal_date:
continue
try:
# Convert date to string for comparison and parsing
date_str = deal.deal_date.isoformat() if isinstance(deal.deal_date, date) else str(deal.deal_date)
# Filter by time period if specified
if cutoff_date is not None and date_str < cutoff_date_str:
continue
@@ -100,7 +102,7 @@ def parse_deal_dates(
def calculate_market_activity_score(
deals: List[Deal], time_period_months: int = 12
deals: List[Deal], time_period_months: Optional[int] = 12
) -> MarketActivityScore:
"""
Calculate market activity and liquidity metrics.
@@ -209,10 +211,12 @@ def analyze_investment_potential(deals: List[Deal]) -> InvestmentAnalysis:
price_data = []
for deal in deals:
price_per_sqm = deal.price_per_sqm # Use computed field from Deal model
date_str = deal.deal_date
if price_per_sqm and price_per_sqm > 0 and date_str:
if price_per_sqm and price_per_sqm > 0 and deal.deal_date:
try:
# Convert date to string for parsing
date_str = deal.deal_date.isoformat() if isinstance(deal.deal_date, date) else str(deal.deal_date)
# Parse date for sorting
year = int(date_str[:4])
month = int(date_str[5:7])
@@ -319,7 +323,7 @@ def analyze_investment_potential(deals: List[Deal]) -> InvestmentAnalysis:
def get_market_liquidity(
deals: List[Deal], time_period_months: int = 12
deals: List[Deal], time_period_months: Optional[int] = 12
) -> LiquidityMetrics:
"""
Get detailed market liquidity and turnover metrics.
+2 -2
View File
@@ -228,7 +228,7 @@ class MarketActivityScore(BaseModel):
total_deals: int = Field(..., description="Total deals in period")
deals_per_month: float = Field(..., description="Average deals per month")
trend: str = Field(..., description="Market trend (increasing, stable, decreasing)")
time_period_months: int = Field(..., description="Analysis period in months")
time_period_months: Optional[int] = Field(None, description="Analysis period in months (None = all data)")
monthly_distribution: Dict[str, int] = Field(
default_factory=dict,
description="Deals per month (YYYY-MM: count)"
@@ -275,7 +275,7 @@ class LiquidityMetrics(BaseModel):
"""
liquidity_score: float = Field(..., description="Overall liquidity score (0-100)", ge=0, le=100)
total_deals: int = Field(..., description="Total deals in period")
time_period_months: int = Field(..., description="Analysis period in months")
time_period_months: Optional[int] = Field(None, description="Analysis period in months (None = all data)")
avg_deals_per_month: float = Field(..., description="Average deals per month")
deal_velocity: float = Field(..., description="Deal velocity (deals per month)")
market_activity_level: str = Field(..., description="Activity level (very_high, high, moderate, low, very_low)")
+15 -8
View File
@@ -121,16 +121,23 @@ def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
date_range_dict = None
if deal_dates:
try:
# Parse ISO date strings to get earliest and latest
# Convert dates to ISO strings for consistent formatting
from datetime import date as date_type
parsed_dates = []
for date_str in deal_dates:
for d in deal_dates:
try:
# Handle ISO format with timezone (e.g., "2025-01-01T00:00:00.000Z")
if 'T' in date_str:
date_str = date_str.split('T')[0]
parsed_dates.append(date_str)
# Handle date objects (from Pydantic models)
if isinstance(d, date_type):
parsed_dates.append(d.isoformat())
else:
# Handle string dates
date_str = str(d)
# Handle ISO format with timezone (e.g., "2025-01-01T00:00:00.000Z")
if 'T' in date_str:
date_str = date_str.split('T')[0]
parsed_dates.append(date_str)
except (ValueError, TypeError):
logger.warning(f"Invalid date format: {date_str}")
logger.warning(f"Invalid date format: {d}")
continue
if parsed_dates:
@@ -140,7 +147,7 @@ def calculate_deal_statistics(deals: List[Deal]) -> DealStatistics:
"latest": sorted_dates[-1],
}
except (ValueError, TypeError):
logger.warning("Invalid date format: {date_range_dict}")
logger.warning("Invalid date format in date range calculation")
pass
return DealStatistics(