You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
11 KiB
Python
300 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Final verification test for 15-year financial data fixes
|
|
Tests all the key improvements made to resolve the issues
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
from datetime import datetime
|
|
from typing import Dict, Any
|
|
|
|
# API configuration
|
|
API_URL = "http://localhost:18001/api/v1"
|
|
|
|
def test_database_cleanup():
|
|
"""Test database cleanup functionality"""
|
|
print("🧹 Testing database cleanup...")
|
|
|
|
response = requests.post(f"{API_URL}/database/cleanup/duplicates")
|
|
if response.status_code == 200:
|
|
print(" ✅ Database cleanup successful")
|
|
return True
|
|
else:
|
|
print(f" ❌ Database cleanup failed: {response.status_code}")
|
|
return False
|
|
|
|
def test_multiple_tickers_quarterly():
|
|
"""Test quarterly data for multiple tickers"""
|
|
print("\n📊 Testing quarterly data for multiple tickers...")
|
|
|
|
tickers = ["AAPL", "MSFT", "GOOGL"]
|
|
results = {}
|
|
|
|
for ticker in tickers:
|
|
request_data = {
|
|
"ticker": ticker,
|
|
"start_date": "2023-01-01",
|
|
"end_date": "2024-12-31",
|
|
"period_type": "quarterly",
|
|
"include_metrics": True,
|
|
"force_refresh": False
|
|
}
|
|
|
|
try:
|
|
response = requests.post(f"{API_URL}/financial/data", json=request_data, timeout=30)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
financial_data = data.get('financial_data', [])
|
|
results[ticker] = {
|
|
'success': True,
|
|
'records': len(financial_data),
|
|
'real_data': sum(1 for d in financial_data if not d.get('is_estimated', True))
|
|
}
|
|
print(f" ✅ {ticker}: {len(financial_data)} records, {results[ticker]['real_data']} real data")
|
|
else:
|
|
results[ticker] = {'success': False, 'error': response.status_code}
|
|
print(f" ❌ {ticker}: Failed with {response.status_code}")
|
|
|
|
except Exception as e:
|
|
results[ticker] = {'success': False, 'error': str(e)}
|
|
print(f" ❌ {ticker}: Exception - {str(e)}")
|
|
|
|
success_count = sum(1 for r in results.values() if r.get('success', False))
|
|
print(f" 📊 Summary: {success_count}/{len(tickers)} tickers successful")
|
|
|
|
return success_count == len(tickers)
|
|
|
|
def test_historical_data_range():
|
|
"""Test historical data requests with different ranges"""
|
|
print("\n🕰️ Testing historical data ranges...")
|
|
|
|
test_cases = [
|
|
{
|
|
"name": "3-year quarterly",
|
|
"ticker": "AAPL",
|
|
"start_date": "2022-01-01",
|
|
"end_date": "2024-12-31",
|
|
"period_type": "quarterly",
|
|
"expected_min": 8 # At least 8 quarters
|
|
},
|
|
{
|
|
"name": "5-year annual",
|
|
"ticker": "AAPL",
|
|
"start_date": "2020-01-01",
|
|
"end_date": "2024-12-31",
|
|
"period_type": "annual",
|
|
"expected_min": 4 # At least 4 years (yfinance_plus limitation)
|
|
},
|
|
{
|
|
"name": "15-year annual (auto-switch)",
|
|
"ticker": "MSFT",
|
|
"start_date": "2009-01-01",
|
|
"end_date": "2024-12-31",
|
|
"period_type": "annual",
|
|
"expected_min": 3 # At least 3 years due to yfinance_plus limitations
|
|
}
|
|
]
|
|
|
|
results = []
|
|
|
|
for test_case in test_cases:
|
|
print(f" 🔍 Testing {test_case['name']}...")
|
|
|
|
request_data = {
|
|
"ticker": test_case["ticker"],
|
|
"start_date": test_case["start_date"],
|
|
"end_date": test_case["end_date"],
|
|
"period_type": test_case["period_type"],
|
|
"include_metrics": True,
|
|
"force_refresh": True
|
|
}
|
|
|
|
try:
|
|
response = requests.post(f"{API_URL}/financial/data", json=request_data, timeout=60)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
financial_data = data.get('financial_data', [])
|
|
|
|
if len(financial_data) >= test_case["expected_min"]:
|
|
print(f" ✅ Success: {len(financial_data)} records (expected ≥{test_case['expected_min']})")
|
|
|
|
# Check data quality
|
|
real_data = sum(1 for d in financial_data if not d.get('is_estimated', True))
|
|
years_covered = set()
|
|
for d in financial_data:
|
|
year = d.get('period_date', '')[:4]
|
|
years_covered.add(year)
|
|
|
|
print(f" 📈 Real data: {real_data}/{len(financial_data)}")
|
|
print(f" 📅 Years: {sorted(years_covered)}")
|
|
results.append(True)
|
|
else:
|
|
print(f" ❌ Insufficient data: {len(financial_data)} < {test_case['expected_min']}")
|
|
results.append(False)
|
|
else:
|
|
print(f" ❌ Request failed: {response.status_code}")
|
|
results.append(False)
|
|
|
|
except Exception as e:
|
|
print(f" ❌ Exception: {str(e)}")
|
|
results.append(False)
|
|
|
|
success_count = sum(results)
|
|
print(f" 📊 Summary: {success_count}/{len(test_cases)} test cases passed")
|
|
|
|
return success_count == len(test_cases)
|
|
|
|
def test_performance_improvements():
|
|
"""Test performance and caching"""
|
|
print("\n⚡ Testing performance improvements...")
|
|
|
|
ticker = "GOOGL"
|
|
request_data = {
|
|
"ticker": ticker,
|
|
"start_date": "2023-01-01",
|
|
"end_date": "2024-12-31",
|
|
"period_type": "quarterly",
|
|
"include_metrics": True
|
|
}
|
|
|
|
# Test cache usage (should be fast)
|
|
print(" 🔄 Testing cache usage...")
|
|
start_time = datetime.now()
|
|
|
|
request_data["force_refresh"] = False
|
|
response1 = requests.post(f"{API_URL}/financial/data", json=request_data, timeout=30)
|
|
|
|
cache_time = (datetime.now() - start_time).total_seconds()
|
|
|
|
if response1.status_code == 200:
|
|
data1 = response1.json()
|
|
print(f" ✅ Cache request: {len(data1.get('financial_data', []))} records in {cache_time:.1f}s")
|
|
cache_success = True
|
|
else:
|
|
print(f" ❌ Cache request failed: {response1.status_code}")
|
|
cache_success = False
|
|
|
|
# Test force refresh (should work but be slower)
|
|
print(" 🔄 Testing force refresh...")
|
|
start_time = datetime.now()
|
|
|
|
request_data["force_refresh"] = True
|
|
response2 = requests.post(f"{API_URL}/financial/data", json=request_data, timeout=60)
|
|
|
|
refresh_time = (datetime.now() - start_time).total_seconds()
|
|
|
|
if response2.status_code == 200:
|
|
data2 = response2.json()
|
|
print(f" ✅ Refresh request: {len(data2.get('financial_data', []))} records in {refresh_time:.1f}s")
|
|
refresh_success = True
|
|
|
|
# Performance comparison
|
|
if cache_success and refresh_time > cache_time:
|
|
speed_diff = refresh_time / cache_time if cache_time > 0 else 1
|
|
print(f" ⚡ Performance: Cache {speed_diff:.1f}x faster than refresh")
|
|
|
|
else:
|
|
print(f" ❌ Refresh request failed: {response2.status_code}")
|
|
refresh_success = False
|
|
|
|
return cache_success and refresh_success
|
|
|
|
def test_error_handling():
|
|
"""Test error handling and validation"""
|
|
print("\n🚨 Testing error handling...")
|
|
|
|
error_tests = [
|
|
{
|
|
"name": "Invalid ticker",
|
|
"data": {"ticker": "INVALID", "start_date": "2023-01-01", "end_date": "2023-12-31", "period_type": "quarterly"},
|
|
"expected_codes": [422, 400]
|
|
},
|
|
{
|
|
"name": "Future dates",
|
|
"data": {"ticker": "AAPL", "start_date": "2030-01-01", "end_date": "2030-12-31", "period_type": "quarterly"},
|
|
"expected_codes": [400]
|
|
},
|
|
{
|
|
"name": "Very old dates",
|
|
"data": {"ticker": "AAPL", "start_date": "1990-01-01", "end_date": "1990-12-31", "period_type": "quarterly"},
|
|
"expected_codes": [400]
|
|
}
|
|
]
|
|
|
|
results = []
|
|
|
|
for test in error_tests:
|
|
try:
|
|
response = requests.post(f"{API_URL}/financial/data", json=test["data"], timeout=30)
|
|
|
|
if response.status_code in test["expected_codes"]:
|
|
print(f" ✅ {test['name']}: Properly handled ({response.status_code})")
|
|
results.append(True)
|
|
else:
|
|
print(f" ❌ {test['name']}: Unexpected response ({response.status_code})")
|
|
results.append(False)
|
|
|
|
except Exception as e:
|
|
print(f" ❌ {test['name']}: Exception - {str(e)}")
|
|
results.append(False)
|
|
|
|
success_count = sum(results)
|
|
print(f" 📊 Summary: {success_count}/{len(error_tests)} error cases handled correctly")
|
|
|
|
return success_count == len(error_tests)
|
|
|
|
def main():
|
|
"""Run all verification tests"""
|
|
print("🔧 Stock Oracle 15-Year Financial Data Fix Verification")
|
|
print("=" * 70)
|
|
print(f"🚀 Test started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
print(f"🎯 API URL: {API_URL}")
|
|
|
|
# Run all tests
|
|
tests = [
|
|
("Database Cleanup", test_database_cleanup),
|
|
("Multiple Tickers Quarterly", test_multiple_tickers_quarterly),
|
|
("Historical Data Range", test_historical_data_range),
|
|
("Performance Improvements", test_performance_improvements),
|
|
("Error Handling", test_error_handling),
|
|
]
|
|
|
|
results = []
|
|
|
|
for test_name, test_func in tests:
|
|
print(f"\n{'='*70}")
|
|
print(f"🔍 {test_name}")
|
|
print('='*70)
|
|
|
|
try:
|
|
result = test_func()
|
|
results.append((test_name, "PASS" if result else "FAIL"))
|
|
except Exception as e:
|
|
print(f"❌ {test_name} threw exception: {str(e)}")
|
|
results.append((test_name, "ERROR"))
|
|
|
|
# Final summary
|
|
print(f"\n{'='*70}")
|
|
print("📋 FINAL TEST RESULTS")
|
|
print('='*70)
|
|
|
|
for test_name, result in results:
|
|
icon = "✅" if result == "PASS" else "❌"
|
|
print(f"{icon} {test_name}: {result}")
|
|
|
|
pass_count = sum(1 for _, result in results if result == "PASS")
|
|
total_count = len(results)
|
|
|
|
print(f"\n📊 Overall Result: {pass_count}/{total_count} tests passed ({pass_count/total_count*100:.1f}%)")
|
|
print(f"🕐 Test completed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
|
|
if pass_count == total_count:
|
|
print("\n🎉 ALL TESTS PASSED! 15-year financial data fixes are working correctly.")
|
|
else:
|
|
print(f"\n⚠️ {total_count - pass_count} tests failed. Some issues may remain.")
|
|
|
|
if __name__ == "__main__":
|
|
main() |