Organize and cleaup
This commit is contained in:
@@ -1,339 +0,0 @@
|
||||
# Test Suite Update Status - Phase 4.1
|
||||
|
||||
## Overview
|
||||
|
||||
All tests have been updated to work with Pydantic v2 models. This document summarizes the changes and provides patterns for any remaining updates.
|
||||
|
||||
## Test Files Status
|
||||
|
||||
### ✅ tests/govmap/test_models.py
|
||||
**Status:** Complete - 50+ new tests created
|
||||
|
||||
- Comprehensive validation tests for all 9 Pydantic models
|
||||
- Tests for computed fields (e.g., `price_per_sqm`)
|
||||
- Tests for field aliasing (camelCase ↔ snake_case)
|
||||
- Tests for boundary conditions and validation errors
|
||||
- Integration workflow tests
|
||||
|
||||
**No changes needed** - This is a new file created for Phase 4.1
|
||||
|
||||
### ✅ tests/govmap/test_utils.py (271 lines)
|
||||
**Status:** No changes needed
|
||||
|
||||
- Tests utility functions (distance calculation, address matching, floor parsing)
|
||||
- These functions don't work with models - they accept primitive types
|
||||
- All tests remain valid as-is
|
||||
|
||||
**Example test:**
|
||||
```python
|
||||
def test_calculate_distance():
|
||||
point1 = (180000.0, 650000.0)
|
||||
point2 = (180100.0, 650000.0)
|
||||
distance = calculate_distance(point1, point2)
|
||||
assert distance == 100.0
|
||||
```
|
||||
|
||||
### ✅ tests/govmap/test_validators.py (228 lines)
|
||||
**Status:** No changes needed
|
||||
|
||||
- Tests validation functions (address, coordinates, integers, deal types)
|
||||
- Validators work with primitive types, not models
|
||||
- All tests remain valid as-is
|
||||
|
||||
**Example test:**
|
||||
```python
|
||||
def test_valid_address():
|
||||
address = "דיזנגוף 50 תל אביב"
|
||||
result = validate_address(address)
|
||||
assert result == "דיזנגוף 50 תל אביב"
|
||||
```
|
||||
|
||||
### ✅ tests/test_govmap_client.py (670 lines)
|
||||
**Status:** Majorupdates complete, ~90% updated
|
||||
|
||||
**Changes made:**
|
||||
1. ✅ Updated imports to include model classes
|
||||
2. ✅ Updated autocomplete tests - now expect `AutocompleteResponse` model
|
||||
3. ✅ Updated deal retrieval tests - now expect `List[Deal]`
|
||||
4. ✅ Updated integration test - mocks return models
|
||||
5. ✅ Updated market analysis tests - now expect typed models:
|
||||
- `calculate_market_activity_score` → `MarketActivityScore`
|
||||
- `analyze_investment_potential` → `InvestmentAnalysis`
|
||||
- `get_market_liquidity` → `LiquidityMetrics`
|
||||
6. ✅ Updated filter tests - now use `Deal` models
|
||||
7. ✅ Updated statistics tests - now expect `DealStatistics` model
|
||||
|
||||
**Pattern used:**
|
||||
```python
|
||||
# BEFORE (v1.x)
|
||||
deals = [
|
||||
{"dealAmount": 1000000, "assetArea": 80, "dealDate": "2023-01-01"}
|
||||
]
|
||||
assert deals[0]["dealAmount"] == 1000000
|
||||
|
||||
# AFTER (v2.0)
|
||||
deals = [
|
||||
Deal(objectid=1, deal_amount=1000000, asset_area=80.0, deal_date="2023-01-01")
|
||||
]
|
||||
assert deals[0].deal_amount == 1000000
|
||||
assert deals[0].price_per_sqm == 12500.0 # Computed field!
|
||||
```
|
||||
|
||||
**Remaining work:**
|
||||
- ~3-4 tests may need minor assertion updates when run
|
||||
- Invalid date test (line 356) needs reconsideration - Pydantic validates at model creation
|
||||
|
||||
### ✅ tests/test_fastmcp_tools.py (483 lines)
|
||||
**Status:** Key patterns updated, ~30% complete
|
||||
|
||||
**Changes made:**
|
||||
1. ✅ Updated imports to include all model classes
|
||||
2. ✅ Updated autocomplete tool tests to mock `AutocompleteResponse` models
|
||||
3. ✅ Pattern established for updating remaining tests
|
||||
|
||||
**Pattern used:**
|
||||
```python
|
||||
# BEFORE (v1.x)
|
||||
mock_client.autocomplete_address.return_value = {
|
||||
"resultsCount": 1,
|
||||
"results": [{"text": "חולון", "id": "123"}]
|
||||
}
|
||||
|
||||
# AFTER (v2.0)
|
||||
mock_client.autocomplete_address.return_value = AutocompleteResponse(
|
||||
resultsCount=1,
|
||||
results=[AutocompleteResult(text="חולון", id="123", type="address")]
|
||||
)
|
||||
```
|
||||
|
||||
**Remaining work:**
|
||||
- Deal-related tool tests need mocks to return `List[Deal]`
|
||||
- Analysis tool tests need mocks to return `DealStatistics`, `MarketActivityScore`, etc.
|
||||
- Pattern is clear - just apply mechanically to remaining tests
|
||||
|
||||
## Summary of Changes
|
||||
|
||||
### Key Testing Patterns for v2.0
|
||||
|
||||
#### 1. Creating Test Data
|
||||
```python
|
||||
# v1.x - Dicts
|
||||
deals = [{"dealAmount": 1000000, "dealDate": "2023-01-01"}]
|
||||
|
||||
# v2.0 - Models
|
||||
deals = [Deal(objectid=1, deal_amount=1000000, deal_date="2023-01-01")]
|
||||
```
|
||||
|
||||
#### 2. Assertions
|
||||
```python
|
||||
# v1.x - Dict access
|
||||
assert deal["dealAmount"] == 1000000
|
||||
assert deal.get("price_per_sqm") == 12500
|
||||
|
||||
# v2.0 - Model attributes
|
||||
assert deal.deal_amount == 1000000
|
||||
assert deal.price_per_sqm == 12500.0 # Computed field
|
||||
```
|
||||
|
||||
#### 3. Mocking Client Methods
|
||||
```python
|
||||
# v1.x - Return dicts
|
||||
mock_client.get_street_deals.return_value = [
|
||||
{"objectid": 123, "dealAmount": 1000000}
|
||||
]
|
||||
|
||||
# v2.0 - Return models
|
||||
mock_client.get_street_deals.return_value = [
|
||||
Deal(objectid=123, deal_amount=1000000, deal_date="2023-01-01")
|
||||
]
|
||||
```
|
||||
|
||||
#### 4. Testing Model Responses
|
||||
```python
|
||||
# v1.x - Check dict keys
|
||||
assert "investment_score" in result
|
||||
assert result["investment_score"] > 0
|
||||
|
||||
# v2.0 - Check model attributes
|
||||
assert isinstance(result, InvestmentAnalysis)
|
||||
assert result.investment_score > 0
|
||||
```
|
||||
|
||||
## Test Execution Status
|
||||
|
||||
### Expected Test Counts
|
||||
- **test_models.py**: ~50 tests (all new)
|
||||
- **test_utils.py**: ~25 tests (unchanged)
|
||||
- **test_validators.py**: ~20 tests (unchanged)
|
||||
- **test_govmap_client.py**: ~34 tests (updated)
|
||||
- **test_fastmcp_tools.py**: ~35 tests (pattern established)
|
||||
|
||||
**Total**: ~164 tests
|
||||
|
||||
### Known Issues to Address
|
||||
|
||||
1. **Invalid date test** (test_govmap_client.py:356)
|
||||
- Pydantic validates at model creation
|
||||
- Test needs to expect ValidationError or be redesigned
|
||||
|
||||
2. **Remaining fastmcp tool tests**
|
||||
- Apply established pattern to remaining ~25 tests
|
||||
- Straightforward mechanical update
|
||||
|
||||
3. **Some assertions may need adjustment**
|
||||
- Model field names vs dict keys
|
||||
- Computed fields vs manual calculations
|
||||
|
||||
## Migration Checklist for Remaining Tests
|
||||
|
||||
When updating remaining tests, follow this checklist:
|
||||
|
||||
- [ ] Import required model classes at top of file
|
||||
- [ ] Update mock return values to return models
|
||||
- [ ] Update test data creation to use model constructors
|
||||
- [ ] Update assertions from dict access (`deal["field"]`) to model attributes (`deal.field`)
|
||||
- [ ] Remove manual `price_per_sqm` calculations (now computed)
|
||||
- [ ] Update isinstance checks to expect model types
|
||||
- [ ] Use `.model_dump()` if serialization to dict is needed for comparison
|
||||
|
||||
## Benefits of Updated Tests
|
||||
|
||||
1. **Type Safety**: Tests now catch type errors at test time
|
||||
2. **Clear Contracts**: Model signatures document expected fields
|
||||
3. **Computed Fields**: Tests verify automatic calculations
|
||||
4. **Better Errors**: Pydantic validation errors are very descriptive
|
||||
5. **Future-Proof**: Tests will catch model changes immediately
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest
|
||||
|
||||
# Run specific test file
|
||||
pytest tests/test_govmap_client.py -v
|
||||
|
||||
# Run only model tests
|
||||
pytest tests/govmap/test_models.py -v
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=nadlan_mcp tests/
|
||||
|
||||
# Run only updated tests (mark them with @pytest.mark.unit)
|
||||
pytest -m unit
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Complete fastmcp tool tests** - Apply established pattern to remaining tests
|
||||
2. **Run full test suite** - Identify any assertion mismatches
|
||||
3. **Fix any failures** - Most will be simple field name updates
|
||||
4. **Add integration smoke tests** - Test end-to-end flows with real models
|
||||
5. **Update CI/CD** - Ensure all tests pass in CI
|
||||
|
||||
## Documentation
|
||||
|
||||
- See `MIGRATION.md` for code migration patterns
|
||||
- See `tests/govmap/test_models.py` for model testing examples
|
||||
- See updated test files for established patterns
|
||||
|
||||
---
|
||||
|
||||
## Final Test Execution Results
|
||||
|
||||
### Test Run Summary (Latest)
|
||||
```
|
||||
174 total tests
|
||||
160 PASSED (92%)
|
||||
14 FAILED (8%)
|
||||
```
|
||||
|
||||
### Tests Fixed in This Session
|
||||
- ✅ Fixed date comparison bug in market_analysis.py (date object vs string)
|
||||
- ✅ Fixed date import in market_analysis.py
|
||||
- ✅ Fixed date handling in statistics.py
|
||||
- ✅ Fixed date handling in fastmcp_server.py
|
||||
- ✅ Made time_period_months Optional[int] in MarketActivityScore model
|
||||
- ✅ Fixed strip_bloat_fields to use mode='json' for proper date serialization
|
||||
- ✅ Updated 6 filter tests in test_govmap_client.py to use Deal models
|
||||
- ✅ Updated 1 market analysis test (invalid dates)
|
||||
- ✅ Updated 2 coordinate parsing tests to use AutocompleteResponse models
|
||||
- ✅ Updated 4 fastmcp autocomplete tests
|
||||
- ✅ Updated 2 get_deals_by_radius tests
|
||||
|
||||
**Total fixes**: 28 tests repaired
|
||||
|
||||
### Remaining 14 Failures
|
||||
|
||||
#### Category 1: FastMCP Tool Tests (8 tests)
|
||||
All need mocks updated to return Deal models instead of dicts:
|
||||
1. test_successful_find_deals
|
||||
2. test_find_deals_strips_bloat
|
||||
3. test_successful_market_analysis
|
||||
4. test_successful_get_comparables
|
||||
5. test_comparables_strips_bloat
|
||||
6. test_successful_statistics_calculation
|
||||
7. test_successful_street_deals
|
||||
8. test_successful_neighborhood_deals
|
||||
|
||||
**Pattern**: Mock client methods to return `List[Deal]` instead of `List[dict]`
|
||||
|
||||
#### Category 2: Market Analysis Tests (4 tests)
|
||||
1. test_calculate_market_activity_score_with_time_filter
|
||||
2. test_calculate_market_activity_score_high_activity
|
||||
3. test_get_market_liquidity_success
|
||||
4. test_get_market_liquidity_varied_periods
|
||||
|
||||
**Pattern**: Tests need Deal model fixtures instead of dicts
|
||||
|
||||
#### Category 3: Coordinate Parsing Tests (2 tests)
|
||||
1. test_coordinate_parsing_from_wkt_point
|
||||
2. test_invalid_coordinate_format
|
||||
|
||||
**Issue**: Mocks still returning dicts or assertion issues
|
||||
|
||||
### Key Fixes Applied
|
||||
|
||||
1. **Date Handling**:
|
||||
- Import `date` from datetime in market_analysis.py
|
||||
- Convert `deal.deal_date` (date object) to ISO string using `.isoformat()`
|
||||
- Use `model_dump(mode='json')` to serialize dates properly
|
||||
|
||||
2. **Model Serialization**:
|
||||
- Changed `deal.model_dump()` to `deal.model_dump(mode='json')` for JSON compatibility
|
||||
|
||||
3. **Optional Fields**:
|
||||
- Made `time_period_months` Optional[int] in MarketActivityScore
|
||||
|
||||
4. **Test Patterns**:
|
||||
- Replace dict fixtures with Deal model constructors
|
||||
- Update assertions from dict access to model attributes
|
||||
- Use snake_case field names (e.g., `deal_amount` not `dealAmount`)
|
||||
|
||||
---
|
||||
|
||||
## ✅ FINAL STATUS: ALL TESTS PASSING
|
||||
|
||||
### Test Run Summary (FINAL)
|
||||
```
|
||||
174 total tests
|
||||
174 PASSED (100%) ✅
|
||||
0 FAILED
|
||||
```
|
||||
|
||||
### Additional Fixes Applied (Session 2)
|
||||
- ✅ Made `time_period_months` Optional[int] in LiquidityMetrics model
|
||||
- ✅ Updated market analysis function signatures to accept Optional[int] for time_period_months
|
||||
- ✅ Fixed all remaining market analysis tests with recent dates
|
||||
- ✅ Added CoordinatePoint import to test_govmap_client.py
|
||||
- ✅ Fixed coordinate error message assertion
|
||||
- ✅ Updated all 8 remaining fastmcp tool tests to use Deal model mocks
|
||||
- ✅ Fixed `.get()` call on Deal model in analyze_market_trends (used getattr instead)
|
||||
|
||||
**Total tests fixed in both sessions**: All 174 tests
|
||||
|
||||
---
|
||||
|
||||
**Status**: Phase 4.1 test updates **100% COMPLETE** ✅
|
||||
**Confidence**: Very High - All tests passing
|
||||
**Last Updated**: 2025-01-26 (completion)
|
||||
+3
-1
@@ -296,4 +296,6 @@ nadlan_mcp/local_settings.py
|
||||
# Claude Code workspace and local config
|
||||
.claude/
|
||||
.mcp.json
|
||||
PHASE3-PLAN-SUMMARY.md
|
||||
|
||||
# Phase summaries should stay in .cursor/plans/ only
|
||||
PHASE*.md
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
# Phase 5: Testing & Quality - COMPLETE ✅
|
||||
|
||||
## Achievement Summary
|
||||
|
||||
**Coverage:** 84% (target: 80%) ✅
|
||||
**Tests Added:** 108 new tests
|
||||
**Total Tests:** 314 (304 run by default + 10 API health checks)
|
||||
**Status:** All passing (303 passed, 1 skipped)
|
||||
**Runtime:** ~12 seconds
|
||||
|
||||
## What Was Done
|
||||
|
||||
### 1. Test Coverage Expansion (108 new tests)
|
||||
|
||||
Created three comprehensive test modules covering core business logic:
|
||||
|
||||
- **test_filters.py** (36 tests)
|
||||
- Property type filtering (exact/partial/case-insensitive)
|
||||
- Numeric range filters (rooms, price, area, floor)
|
||||
- DealFilters model integration
|
||||
- Missing data handling
|
||||
- Error validation
|
||||
|
||||
- **test_statistics.py** (32 tests)
|
||||
- Statistical calculations (mean, median, std_dev, percentiles)
|
||||
- Property type distribution
|
||||
- Date handling (date objects + ISO strings)
|
||||
- Missing/zero value handling
|
||||
|
||||
- **test_market_analysis.py** (40 tests)
|
||||
- Date parsing and grouping (monthly/quarterly)
|
||||
- Market activity scoring (volume, trends)
|
||||
- Investment potential analysis
|
||||
- Liquidity metrics
|
||||
- Time-independent testing with relative dates
|
||||
|
||||
### 2. VCR.py Infrastructure
|
||||
|
||||
Set up for recording/replaying HTTP interactions:
|
||||
- Created `tests/vcr_config.py` with configuration
|
||||
- Added `vcr_cassette` fixture in conftest.py
|
||||
- Created `tests/cassettes/` directory
|
||||
- Configured request/response scrubbing
|
||||
|
||||
### 3. API Health Check Suite (10 tests)
|
||||
|
||||
Created `tests/api_health/` with weekly checks:
|
||||
- **Autocomplete health** (3 tests): endpoint, structure, coordinates
|
||||
- **Deals API health** (3 tests): radius queries, street deals, models
|
||||
- **Data quality** (2 tests): reasonable amounts, recent dates
|
||||
- **Integration** (2 tests): full workflow, response times
|
||||
- Marked with `@pytest.mark.api_health`
|
||||
- Run separately: `pytest -m api_health`
|
||||
|
||||
## Coverage by Module
|
||||
|
||||
| Module | Coverage | Tests |
|
||||
|--------|----------|-------|
|
||||
| govmap/filters.py | 99% | 36 |
|
||||
| govmap/models.py | 97% | 36 |
|
||||
| govmap/utils.py | 96% | 42 |
|
||||
| govmap/market_analysis.py | 90% | 40 |
|
||||
| fastmcp_server.py | 86% | 22 |
|
||||
| govmap/statistics.py | 86% | 32 |
|
||||
| govmap/client.py | 73% | 34 |
|
||||
| **OVERALL** | **84%** | **304** |
|
||||
|
||||
## Usage
|
||||
|
||||
### Run all tests (default)
|
||||
```bash
|
||||
pytest tests/
|
||||
```
|
||||
|
||||
### Run with coverage report
|
||||
```bash
|
||||
pytest tests/ --cov=nadlan_mcp --cov-report=term-missing
|
||||
```
|
||||
|
||||
### Run specific test modules
|
||||
```bash
|
||||
pytest tests/govmap/test_filters.py -v
|
||||
pytest tests/govmap/test_statistics.py -v
|
||||
pytest tests/govmap/test_market_analysis.py -v
|
||||
```
|
||||
|
||||
### Run API health checks (weekly)
|
||||
```bash
|
||||
pytest -m api_health -v
|
||||
```
|
||||
|
||||
## Key Improvements
|
||||
|
||||
1. **Exceeded target** - 84% vs 80% goal
|
||||
2. **Fast execution** - 12s for 304 tests
|
||||
3. **Time-independent** - Tests use relative dates
|
||||
4. **Comprehensive** - All major functions tested
|
||||
5. **Maintainable** - Parametrized tests reduce duplication
|
||||
6. **Monitored** - Weekly API health checks
|
||||
7. **Documented** - Test docstrings explain behavior
|
||||
|
||||
## Technical Highlights
|
||||
|
||||
### Date Handling
|
||||
- Created `get_recent_date()` helper to avoid time-dependent failures
|
||||
- All test dates relative to current date
|
||||
- Proper date/datetime type handling for Pydantic validation
|
||||
|
||||
### Model Testing
|
||||
- Tests match actual model fields (not documentation)
|
||||
- Handles Pydantic strict validation
|
||||
- Tests computed fields (price_per_sqm)
|
||||
|
||||
### Edge Cases
|
||||
- Zero vs missing values
|
||||
- Threshold boundaries (rating edge cases)
|
||||
- Evenly distributed data (trend calculations)
|
||||
|
||||
## Files Created
|
||||
|
||||
```
|
||||
tests/govmap/test_filters.py (36 tests)
|
||||
tests/govmap/test_statistics.py (32 tests)
|
||||
tests/govmap/test_market_analysis.py (40 tests)
|
||||
tests/vcr_config.py (VCR setup)
|
||||
tests/cassettes/ (directory)
|
||||
tests/api_health/ (directory)
|
||||
__init__.py
|
||||
test_govmap_api_health.py (10 tests)
|
||||
README.md
|
||||
.cursor/plans/PHASE5-STATUS.md (detailed status)
|
||||
PHASE5_SUMMARY.md (this file)
|
||||
```
|
||||
|
||||
## Files Modified
|
||||
|
||||
```
|
||||
pytest.ini (added api_health marker)
|
||||
tests/conftest.py (added vcr_cassette fixture)
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
Phase 5 complete! Possible future improvements:
|
||||
- Record VCR cassettes for faster integration tests
|
||||
- Increase govmap/client.py coverage (error paths)
|
||||
- Add mutation testing (pytest-mutagen)
|
||||
- Property-based testing (Hypothesis)
|
||||
|
||||
## Verification
|
||||
|
||||
All tests passing:
|
||||
```bash
|
||||
$ pytest tests/ -m "not api_health" -q
|
||||
303 passed, 1 skipped, 10 deselected in 12.14s
|
||||
```
|
||||
|
||||
Coverage exceeds target:
|
||||
```bash
|
||||
$ pytest tests/ --cov=nadlan_mcp
|
||||
TOTAL: 84% coverage
|
||||
```
|
||||
|
||||
API health checks work:
|
||||
```bash
|
||||
$ pytest -m api_health --collect-only
|
||||
10 tests collected
|
||||
```
|
||||
@@ -476,6 +476,34 @@ import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Nadlan-MCP has comprehensive test coverage with 304 tests achieving 84% code coverage.
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
# Run all fast tests (default - excludes API health checks)
|
||||
pytest tests/ -m "not api_health"
|
||||
# Result: 303 passed, 1 skipped in ~12s
|
||||
|
||||
# Run with coverage report
|
||||
pytest tests/ -m "not api_health" --cov=nadlan_mcp --cov-report=term-missing
|
||||
|
||||
# Run API health checks (weekly monitoring)
|
||||
pytest -m api_health -v
|
||||
```
|
||||
|
||||
### Test Structure
|
||||
|
||||
- **304 tests total** with 84% coverage
|
||||
- **Fast unit tests** - Mocked/fixture-based (~12s)
|
||||
- **E2E smoke tests** - Minimal API calls (~5s)
|
||||
- **Comprehensive E2E** - Full API coverage (~5min, optional)
|
||||
- **API health checks** - Weekly API monitoring (10 tests, run on-demand)
|
||||
|
||||
See `TESTING.md` for detailed testing documentation.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- **requests**: HTTP library for API calls
|
||||
|
||||
@@ -133,40 +133,46 @@ This document tracks the implementation progress of the Nadlan-MCP improvement p
|
||||
|
||||
**Breaking Change:** v2.0.0 - All methods return Pydantic models instead of dicts
|
||||
|
||||
### Phase 5: Testing & Quality ✅ COMPLETE
|
||||
|
||||
**See `.cursor/plans/PHASE5-STATUS.md` for detailed status**
|
||||
|
||||
#### 5.1 Expand Test Coverage ✅
|
||||
- ✅ Created `tests/govmap/test_filters.py` (36 comprehensive filter tests)
|
||||
- ✅ Created `tests/govmap/test_statistics.py` (32 statistical calculation tests)
|
||||
- ✅ Created `tests/govmap/test_market_analysis.py` (40 market analysis tests)
|
||||
- ✅ Added parametrized tests to reduce repetition
|
||||
- ✅ Added time-independent testing with relative dates
|
||||
- ✅ Total: 304 tests (was 195), all passing
|
||||
- ✅ Coverage: 84% (target: 80%)
|
||||
|
||||
#### 5.2 VCR.py Infrastructure ✅
|
||||
- ✅ Created `tests/vcr_config.py` with VCR configuration
|
||||
- ✅ Added `vcr_cassette` fixture in `tests/conftest.py`
|
||||
- ✅ Created `tests/cassettes/` directory for recordings
|
||||
- ✅ Configured request/response scrubbing and YAML serialization
|
||||
|
||||
#### 5.3 API Health Check Suite ✅
|
||||
- ✅ Created `tests/api_health/` directory with 10 health check tests
|
||||
- ✅ Configured `@pytest.mark.api_health` marker
|
||||
- ✅ Tests autocomplete, deals API, data quality, integration workflows
|
||||
- ✅ Run separately with `pytest -m api_health`
|
||||
- ✅ Documented in `tests/api_health/README.md`
|
||||
|
||||
**Phase 5 Results:**
|
||||
- ✅ 84% code coverage (exceeded 80% target)
|
||||
- ✅ 108 new tests added
|
||||
- ✅ 304 total tests (303 passed, 1 skipped)
|
||||
- ✅ Fast test suite: ~12 seconds
|
||||
- ✅ VCR.py ready for recording API interactions
|
||||
- ✅ Weekly API health monitoring established
|
||||
|
||||
## 🚧 In Progress
|
||||
|
||||
None - Phase 4.1 complete!
|
||||
None - Phase 5 complete!
|
||||
|
||||
## 📋 To-Do (Next Priority)
|
||||
|
||||
### Phase 5: Testing & Quality
|
||||
|
||||
#### 5.1 Expand Test Coverage
|
||||
- [ ] Add integration tests (with @pytest.mark.integration)
|
||||
- [ ] Add edge case tests for all functions
|
||||
- [ ] Add parametrized tests for address formats
|
||||
- [ ] Add tests for new valuation tools
|
||||
- [ ] Add tests for market analysis tools
|
||||
- [ ] Add tests for enhanced filtering
|
||||
- [ ] Add tests for `analyze_market_trends`
|
||||
- [ ] Add tests for `compare_addresses`
|
||||
- [ ] Add tests for `_is_same_building` logic
|
||||
|
||||
#### 5.2 Validation Tests
|
||||
- [ ] Create `tests/test_validation.py`
|
||||
- [ ] Test address validation
|
||||
- [ ] Test coordinate validation
|
||||
- [ ] Test integer validation
|
||||
- [ ] Test configuration validation
|
||||
- [ ] Test model validation (Pydantic)
|
||||
|
||||
#### 5.3 Mock External APIs
|
||||
- [ ] Update `tests/conftest.py` with comprehensive fixtures
|
||||
- [ ] Add VCR.py for recording/replaying API calls
|
||||
- [ ] Create fixture for deal responses
|
||||
- [ ] Create fixture for autocomplete responses
|
||||
- [ ] Create fixture for error scenarios
|
||||
|
||||
### Phase 6: Documentation
|
||||
|
||||
#### 6.1 Additional Documentation Files
|
||||
@@ -220,6 +226,11 @@ None - Phase 4.1 complete!
|
||||
|
||||
## 🔮 Future Features (Backlog)
|
||||
|
||||
### Phase 4.3: Additional Pydantic Models (Optional)
|
||||
- [ ] Create `PolygonMetadata` model for type safety in `get_deals_by_radius` responses
|
||||
- Currently returns `List[Dict]` with polygon metadata
|
||||
- See TODO in `govmap/client.py:310`
|
||||
|
||||
### Phase 4.2: LLM-Friendly Tool Design (Optional - Deferred)
|
||||
- [ ] Add `summarized_response: bool = False` parameter to all tools
|
||||
- [ ] Implement summarization logic for each tool
|
||||
|
||||
+78
-30
@@ -2,51 +2,69 @@
|
||||
|
||||
## Overview
|
||||
|
||||
Nadlan-MCP uses a three-tier testing approach:
|
||||
1. **Fast unit tests** - Cached fixtures, run in <1s (default)
|
||||
Nadlan-MCP uses a multi-tier testing approach:
|
||||
1. **Fast unit tests** - Mocked/fixture-based, run in ~12s (default)
|
||||
2. **E2E smoke tests** - Minimal API calls, run in ~5s (verify API works)
|
||||
3. **Comprehensive E2E tests** - Full API coverage, run in ~5min (optional)
|
||||
4. **API health checks** - Weekly API monitoring, run on-demand
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Default: Fast Unit Tests Only
|
||||
### Default: Fast Tests (Excludes API Health Checks)
|
||||
```bash
|
||||
pytest tests/ --ignore=tests/e2e/
|
||||
# Result: 180 passed, 1 skipped in 0.39s
|
||||
pytest tests/ -m "not api_health"
|
||||
# Result: 303 passed, 1 skipped in ~12s
|
||||
```
|
||||
|
||||
### All Tests Including API Health
|
||||
```bash
|
||||
pytest tests/
|
||||
# Result: 313 passed, 1 skipped in ~15s (if API health checks pass)
|
||||
```
|
||||
|
||||
### API Health Checks Only (Weekly)
|
||||
```bash
|
||||
pytest -m api_health -v
|
||||
# Result: 10 passed (verifies Govmap API is working)
|
||||
```
|
||||
|
||||
### E2E Smoke Tests (Fast)
|
||||
```bash
|
||||
pytest tests/e2e/test_mcp_tools.py -m integration
|
||||
pytest tests/e2e/test_mcp_tools.py
|
||||
# Result: 4 passed in 4.77s
|
||||
```
|
||||
|
||||
### Comprehensive E2E Tests (Slow - Optional)
|
||||
```bash
|
||||
pytest tests/e2e/test_mcp_tools_comprehensive.py -m integration
|
||||
pytest tests/e2e/test_mcp_tools_comprehensive.py
|
||||
# Result: 11 passed in 5m36s
|
||||
```
|
||||
|
||||
### All Tests
|
||||
### With Coverage Report
|
||||
```bash
|
||||
pytest tests/
|
||||
pytest tests/ -m "not api_health" --cov=nadlan_mcp --cov-report=term-missing
|
||||
# Result: 84% coverage
|
||||
```
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Fast Unit Tests (`tests/`)
|
||||
- **174 tests** covering all core functionality
|
||||
- Use cached API responses from `tests/fixtures/`
|
||||
- Mock `GovmapClient` to avoid real API calls
|
||||
- Run in **0.39 seconds**
|
||||
- **304 tests** covering all core functionality
|
||||
- Use mocked responses and fixtures
|
||||
- Run in **~12 seconds**
|
||||
|
||||
Key test files:
|
||||
- `tests/govmap/test_filters.py` - Deal filtering (36 tests)
|
||||
- `tests/govmap/test_statistics.py` - Statistical calculations (32 tests)
|
||||
- `tests/govmap/test_market_analysis.py` - Market analysis (40 tests)
|
||||
- `tests/govmap/test_models.py` - Pydantic model validation (36 tests)
|
||||
- `tests/govmap/test_utils.py` - Helper functions (45 tests)
|
||||
- `tests/govmap/test_validators.py` - Input validation (24 tests)
|
||||
- `tests/test_govmap_client.py` - Client and business logic (41 tests)
|
||||
- `tests/govmap/test_utils.py` - Helper functions (42 tests)
|
||||
- `tests/govmap/test_validators.py` - Input validation (32 tests)
|
||||
- `tests/test_govmap_client.py` - Client and business logic (34 tests)
|
||||
- `tests/test_fastmcp_tools.py` - MCP tool integration (22 tests)
|
||||
- `tests/test_mcp_tools_fast.py` - Fast MCP tool tests with fixtures (6 tests)
|
||||
- `tests/test_mcp_tools_fast.py` - Fast MCP tool tests (7 tests)
|
||||
- `tests/e2e/test_mcp_tools.py` - Smoke tests (4 tests)
|
||||
- `tests/e2e/test_mcp_tools_comprehensive.py` - Comprehensive E2E (11 tests)
|
||||
|
||||
### E2E Smoke Tests (`tests/e2e/test_mcp_tools.py`)
|
||||
- **4 minimal smoke tests** with real API calls
|
||||
@@ -61,50 +79,80 @@ Key test files:
|
||||
- Catch API contract changes
|
||||
- Run in **5min 36sec** (optional, use for releases)
|
||||
|
||||
## Fixtures
|
||||
### API Health Checks (`tests/api_health/`)
|
||||
- **10 health check tests** for weekly API monitoring
|
||||
- Verify Govmap API is functioning correctly
|
||||
- Check response structure, data quality, and performance
|
||||
- Marked with `@pytest.mark.api_health`
|
||||
- **Does not run by default** - must be explicitly requested
|
||||
- See `tests/api_health/README.md` for details
|
||||
|
||||
Cached API responses in `tests/fixtures/`:
|
||||
## Fixtures & Mocking
|
||||
|
||||
### Fixtures (`tests/fixtures/`)
|
||||
Cached API responses for fast tests:
|
||||
- `autocomplete_response.json` - Address search results
|
||||
- `street_deals.json` - Street-level deal data
|
||||
- `neighborhood_deals.json` - Neighborhood-level deal data
|
||||
- `polygon_metadata.json` - Polygon metadata from radius search
|
||||
|
||||
These fixtures are generated from real API calls and updated as needed.
|
||||
### VCR.py Integration
|
||||
VCR.py is configured for recording/replaying HTTP interactions:
|
||||
- Configuration: `tests/vcr_config.py`
|
||||
- Cassettes stored in: `tests/cassettes/`
|
||||
- Fixture: `vcr_cassette` available in `tests/conftest.py`
|
||||
- Record mode: `once` (record first run, replay after)
|
||||
|
||||
Example usage:
|
||||
```python
|
||||
def test_something(vcr_cassette):
|
||||
with vcr_cassette:
|
||||
# HTTP calls recorded/replayed here
|
||||
response = client.autocomplete_address("תל אביב")
|
||||
```
|
||||
|
||||
## Test Markers
|
||||
|
||||
Configured in `pytest.ini`:
|
||||
- `@pytest.mark.integration` - Slow tests requiring real API calls
|
||||
- `@pytest.mark.unit` - Fast unit tests (default)
|
||||
- `@pytest.mark.api_health` - Weekly API health checks (run separately)
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
| Test Suite | Count | Duration | Speed |
|
||||
|------------|-------|----------|-------|
|
||||
| Fast unit tests | 180 | 0.39s | 462 tests/sec |
|
||||
| Fast unit tests | 304 | 12s | 25 tests/sec |
|
||||
| E2E smoke tests | 4 | 4.77s | 0.84 tests/sec |
|
||||
| Comprehensive E2E | 11 | 5m36s | 0.033 tests/sec |
|
||||
| API health checks | 10 | ~5-10s | 1-2 tests/sec |
|
||||
|
||||
**Unit tests are 12x faster than smoke tests, 860x faster than comprehensive E2E!**
|
||||
**Unit tests are 30x faster than smoke tests, 750x faster than comprehensive E2E!**
|
||||
|
||||
## CI/CD Recommendations
|
||||
|
||||
### PR Checks (~5s)
|
||||
### PR Checks (~12s)
|
||||
```bash
|
||||
# Run unit tests + smoke tests
|
||||
pytest tests/ --ignore=tests/e2e/test_mcp_tools_comprehensive.py
|
||||
# Run fast tests only (excludes API health and comprehensive E2E)
|
||||
pytest tests/ -m "not api_health" --ignore=tests/e2e/test_mcp_tools_comprehensive.py
|
||||
```
|
||||
|
||||
### Nightly Build (~6min)
|
||||
```bash
|
||||
# Run all tests including comprehensive E2E
|
||||
pytest tests/
|
||||
# Run all tests except API health
|
||||
pytest tests/ -m "not api_health"
|
||||
```
|
||||
|
||||
### Quick Verification (<5s)
|
||||
### Weekly API Health (~10s)
|
||||
```bash
|
||||
# Just smoke tests to verify API works
|
||||
pytest tests/e2e/test_mcp_tools.py -m integration
|
||||
# Run API health checks to verify Govmap API
|
||||
pytest -m api_health -v
|
||||
```
|
||||
|
||||
### Full Test Suite (~6min)
|
||||
```bash
|
||||
# Run everything including comprehensive E2E and API health
|
||||
pytest tests/
|
||||
```
|
||||
|
||||
## Updating Fixtures
|
||||
|
||||
@@ -1,257 +0,0 @@
|
||||
# Test Coverage Report - Phase 3 Refactoring
|
||||
|
||||
**Generated:** 2025-10-25
|
||||
**Updated:** 2025-10-25 (Added comprehensive unit tests)
|
||||
**Branch:** phase-3
|
||||
**Total Tests:** 138 (all passing in 0.50s) ✅ **+104 new tests**
|
||||
|
||||
## Executive Summary
|
||||
|
||||
✅ **Overall Status:** EXCELLENT - Comprehensive test coverage across all modules
|
||||
✅ **Bug Fixed:** `autocomplete_address` MCP tool bug fixed
|
||||
✅ **Coverage:** Complete unit test coverage for validators, utils, and all MCP tools
|
||||
🎉 **Achievement:** Increased from 34 to 138 tests (+304% improvement)
|
||||
|
||||
## Test Coverage by Module
|
||||
|
||||
### ✅ Well Tested (Indirect Coverage via Integration Tests)
|
||||
|
||||
| Module | Functions | Test Coverage | Notes |
|
||||
|--------|-----------|---------------|-------|
|
||||
| `client.py` | 9 API methods | ✅ High | Tested through GovmapClient integration tests |
|
||||
| `filters.py` | `filter_deals_by_criteria` | ✅ High | 8 dedicated tests covering all filter types |
|
||||
| `statistics.py` | `calculate_deal_statistics`, `calculate_std_dev` | ✅ Good | 1 dedicated test, used in other tests |
|
||||
| `market_analysis.py` | 4 functions | ✅ High | 6 dedicated tests for market analysis |
|
||||
| `utils.py` | `is_same_building` | ✅ Good | 1 dedicated test |
|
||||
|
||||
### ✅ NEW: Comprehensive Unit Tests Added
|
||||
|
||||
| Module | Test File | Tests | Coverage |
|
||||
|--------|-----------|-------|----------|
|
||||
| `validators.py` | `tests/govmap/test_validators.py` | 32 tests | ✅ Complete - All validation functions with edge cases |
|
||||
| `utils.py` | `tests/govmap/test_utils.py` | 36 tests | ✅ Complete - Distance, address matching, Hebrew floors |
|
||||
| MCP Tools | `tests/test_fastmcp_tools.py` | 36 tests | ✅ Complete - All 10 MCP tools with mocking |
|
||||
|
||||
**NEW Test Breakdown:**
|
||||
- **Validators:** 32 tests covering address, coordinates, positive int, deal type validation
|
||||
- **Utils:** 36 tests covering distance calculation, address matching, Hebrew floor parsing
|
||||
- **MCP Tools:** 36 tests covering all 10 tools including error handling and edge cases
|
||||
|
||||
## E2E Test Results (MCP Tools)
|
||||
|
||||
Tested the following MCP tools with real data:
|
||||
|
||||
### ✅ Passing E2E Tests
|
||||
|
||||
1. **`find_recent_deals_for_address`**
|
||||
- Input: `"הרצל 1 תל אביב"`, 1 year, 100m radius, max 5 deals
|
||||
- Result: ✅ SUCCESS - Returned 5 deals with complete statistics
|
||||
- Data Quality: Excellent - all fields populated correctly
|
||||
|
||||
2. **`analyze_market_trends`**
|
||||
- Input: `"דיזנגוף 50 תל אביב"`, 2 years
|
||||
- Result: ✅ SUCCESS - Returned 82 deals analyzed
|
||||
- Output: Comprehensive yearly trends, property type breakdown, neighborhoods
|
||||
- Data Quality: Excellent
|
||||
|
||||
3. **`get_valuation_comparables`**
|
||||
- Input: `"רוטשילד 1 תל אביב"`, property_type="דירה", rooms 2-4, max 5
|
||||
- Result: ✅ SUCCESS - Returned 1 comparable with statistics
|
||||
- Filtering: ✅ Working correctly (rooms, property type)
|
||||
- Token Usage: ✅ Optimized (no bloat fields)
|
||||
|
||||
### ❌ Bug Found: `autocomplete_address`
|
||||
|
||||
**Status:** 🐛 BUG - Incorrect field mapping
|
||||
|
||||
**Problem:**
|
||||
The `autocomplete_address` tool in `fastmcp_server.py` uses incorrect field names when parsing the API response.
|
||||
|
||||
**Current Code (WRONG):**
|
||||
```python
|
||||
formatted_results.append({
|
||||
"address": result.get("addressLabel", ""), # ❌ Wrong field
|
||||
"settlement": result.get("settlementNameHeb", ""), # ❌ Wrong field
|
||||
"coordinates": result.get("coordinates", {}), # ❌ Wrong field
|
||||
"polygon_id": result.get("polygon_id") # ❌ Wrong field
|
||||
})
|
||||
```
|
||||
|
||||
**Actual API Response Fields:**
|
||||
```python
|
||||
{
|
||||
"id": "address|ADDR|123|test",
|
||||
"text": "תל אביב", # ✅ Use this for address
|
||||
"type": "address",
|
||||
"score": 100,
|
||||
"shape": "POINT(3870000.123 3770000.456)", # ✅ Parse this for coordinates
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
**Impact:** HIGH - The tool returns empty data for all fields
|
||||
|
||||
**Fix Required:**
|
||||
```python
|
||||
# Parse coordinates from WKT POINT format
|
||||
shape_str = result.get("shape", "")
|
||||
coordinates = {}
|
||||
if shape_str.startswith("POINT("):
|
||||
coords_str = shape_str[6:-1] # Remove "POINT(" and ")"
|
||||
coords = coords_str.split()
|
||||
if len(coords) == 2:
|
||||
coordinates = {
|
||||
"longitude": float(coords[0]),
|
||||
"latitude": float(coords[1])
|
||||
}
|
||||
|
||||
formatted_results.append({
|
||||
"text": result.get("text", ""), # ✅ Display text
|
||||
"id": result.get("id", ""), # ✅ Unique ID
|
||||
"type": result.get("type", ""), # ✅ Result type
|
||||
"score": result.get("score", 0), # ✅ Match score
|
||||
"coordinates": coordinates # ✅ Parsed coordinates
|
||||
})
|
||||
```
|
||||
|
||||
## Test Organization
|
||||
|
||||
### Current Structure ✅
|
||||
```
|
||||
tests/
|
||||
└── test_govmap_client.py (34 tests)
|
||||
├── TestGovmapClient (12 tests)
|
||||
└── TestMarketAnalysisFunctions (22 tests)
|
||||
```
|
||||
|
||||
### Recommended Structure 📋
|
||||
```
|
||||
tests/
|
||||
├── test_govmap_client.py (existing integration tests)
|
||||
├── govmap/
|
||||
│ ├── test_validators.py (NEW - 10-15 tests)
|
||||
│ ├── test_utils.py (NEW - 8-10 tests)
|
||||
│ ├── test_filters.py (refactor existing)
|
||||
│ ├── test_statistics.py (refactor existing)
|
||||
│ └── test_market_analysis.py (refactor existing)
|
||||
└── test_fastmcp_tools.py (NEW - E2E tool tests)
|
||||
```
|
||||
|
||||
## Missing Test Cases
|
||||
|
||||
### High Priority
|
||||
|
||||
1. **Validator Edge Cases**
|
||||
```python
|
||||
# validators.py
|
||||
- Test validate_address with empty string
|
||||
- Test validate_address with very long address (>500 chars)
|
||||
- Test validate_coordinates with out-of-bounds ITM coordinates
|
||||
- Test validate_positive_int with negative numbers
|
||||
- Test validate_deal_type with invalid types (3, 0, -1)
|
||||
```
|
||||
|
||||
2. **Utils Hebrew Floor Parsing**
|
||||
```python
|
||||
# utils.py
|
||||
- Test extract_floor_number with all Hebrew floor names
|
||||
- Test extract_floor_number with numeric strings
|
||||
- Test extract_floor_number with invalid input
|
||||
- Test calculate_distance with same point
|
||||
- Test calculate_distance with far points
|
||||
```
|
||||
|
||||
3. **MCP Tool E2E Tests**
|
||||
```python
|
||||
# test_fastmcp_tools.py
|
||||
- Test autocomplete_address with real API
|
||||
- Test get_deals_by_radius with edge coordinates
|
||||
- Test error handling for all tools
|
||||
- Test token limits for large responses
|
||||
```
|
||||
|
||||
### Medium Priority
|
||||
|
||||
4. **Client Error Handling**
|
||||
```python
|
||||
# client.py
|
||||
- Test retry logic with transient failures
|
||||
- Test rate limiting behavior
|
||||
- Test timeout handling
|
||||
- Test invalid API responses
|
||||
```
|
||||
|
||||
5. **Filter Edge Cases**
|
||||
```python
|
||||
# filters.py
|
||||
- Test filtering with all null values
|
||||
- Test filtering with overlapping criteria
|
||||
- Test filtering with no matches
|
||||
```
|
||||
|
||||
### Low Priority
|
||||
|
||||
6. **Integration Tests**
|
||||
```python
|
||||
# Integration with real API
|
||||
- Test full workflow: autocomplete → find deals → analyze
|
||||
- Test with various Israeli cities
|
||||
- Test with English addresses
|
||||
```
|
||||
|
||||
## Recommendations
|
||||
|
||||
### Immediate Actions (Before Merge)
|
||||
|
||||
1. **🔴 CRITICAL: Fix `autocomplete_address` bug**
|
||||
- File: `nadlan_mcp/fastmcp_server.py` lines 65-71
|
||||
- Estimated time: 10 minutes
|
||||
- Add test to prevent regression
|
||||
|
||||
### Short-term Actions (Next Sprint)
|
||||
|
||||
2. **🟡 Add validator unit tests**
|
||||
- Create `tests/govmap/test_validators.py`
|
||||
- 10-15 tests covering edge cases
|
||||
- Estimated time: 1-2 hours
|
||||
|
||||
3. **🟡 Add utils unit tests**
|
||||
- Create `tests/govmap/test_utils.py`
|
||||
- Focus on Hebrew floor parsing and distance calculation
|
||||
- Estimated time: 1 hour
|
||||
|
||||
4. **🟡 Add MCP tool E2E tests**
|
||||
- Create `tests/test_fastmcp_tools.py`
|
||||
- Test all 10 MCP tools with real data
|
||||
- Estimated time: 2-3 hours
|
||||
|
||||
### Long-term Actions (Future)
|
||||
|
||||
5. **🟢 Install and configure pytest-cov**
|
||||
- Get actual coverage percentage
|
||||
- Set coverage thresholds in CI
|
||||
|
||||
6. **🟢 Reorganize tests into submodules**
|
||||
- Split test_govmap_client.py into focused test files
|
||||
- Better test organization and maintainability
|
||||
|
||||
7. **🟢 Add integration test suite**
|
||||
- Mark with @pytest.mark.integration
|
||||
- Test full workflows with real API
|
||||
|
||||
## Conclusion
|
||||
|
||||
The refactored code has **good test coverage** overall:
|
||||
- ✅ 34 tests all passing
|
||||
- ✅ Core functionality well-tested through integration tests
|
||||
- ✅ E2E tests show tools working correctly
|
||||
- ⚠️ One bug found and documented (`autocomplete_address`)
|
||||
- 📋 Some unit tests missing for edge cases
|
||||
|
||||
**Recommended Next Steps:**
|
||||
1. Fix the `autocomplete_address` bug (10 min)
|
||||
2. Add the fix to the PR before merging
|
||||
3. Create follow-up issues for missing unit tests
|
||||
4. Consider adding pytest-cov for coverage metrics
|
||||
|
||||
**Overall Assessment:** The Phase 3 refactoring maintains code quality and test coverage. The modular structure will make it easier to add targeted unit tests in the future.
|
||||
Reference in New Issue
Block a user