finance-data-mcp / tests /test_edge_cases.py
dlrklc's picture
Initial commit: Gradio MCP app for real-time financial data
7169bc5
"""
Test cases for edge cases and error handling.
Tests various edge cases that might occur in production.
"""
import unittest
from unittest.mock import patch, MagicMock
import pandas as pd
from src.stock_quote import get_stock_quote
from src.historical_data import get_historical_data
from src.technical_analysis import get_technical_analysis
class TestEdgeCases(unittest.TestCase):
"""Test cases for edge cases and error scenarios."""
# ========================================================================
# STOCK QUOTE EDGE CASES
# ========================================================================
def test_get_stock_quote_nonexistent_ticker(self):
"""Test quote for non-existent ticker."""
with patch('src.stock_quote.get_cached_stock_data') as mock_fetch:
mock_fetch.return_value = ({}, pd.DataFrame()) # Empty data
result = get_stock_quote("INVALID")
self.assertIn("error", result)
self.assertEqual(result["error_code"], "NO_DATA")
def test_get_stock_quote_single_data_point(self):
"""Test quote with only one data point."""
with patch('src.stock_quote.get_cached_stock_data') as mock_fetch:
mock_hist = pd.DataFrame({
'Close': [150],
'Volume': [1000000]
})
mock_fetch.return_value = ({"longName": "Test"}, mock_hist)
result = get_stock_quote("TEST")
self.assertIn("price", result)
self.assertEqual(result["price"], 150)
# Change should be 0 when only one data point
self.assertEqual(result["change"], 0)
def test_get_stock_quote_zero_prev_close(self):
"""Test quote with zero previous close (edge case)."""
with patch('src.stock_quote.get_cached_stock_data') as mock_fetch:
mock_hist = pd.DataFrame({
'Close': [0, 150],
'Volume': [0, 1000000]
})
mock_fetch.return_value = ({"longName": "Test"}, mock_hist)
result = get_stock_quote("TEST")
# Should handle zero division gracefully
self.assertIn("price", result)
# ========================================================================
# HISTORICAL DATA EDGE CASES
# ========================================================================
def test_get_historical_data_empty_result(self):
"""Test historical data with empty result."""
with patch('src.historical_data.fetch_historical_data') as mock_fetch:
mock_fetch.return_value = pd.DataFrame()
result = get_historical_data("TEST", "1mo", "1d")
self.assertIn("error", result)
self.assertEqual(result["error_code"], "NO_DATA")
# ========================================================================
# TECHNICAL ANALYSIS EDGE CASES
# ========================================================================
def test_get_technical_analysis_insufficient_data(self):
"""Test technical analysis with insufficient data."""
with patch('src.technical_analysis.fetch_historical_data') as mock_fetch:
# Return data with less than 50 points
mock_hist = pd.DataFrame({'Close': range(30)})
mock_fetch.return_value = mock_hist
result = get_technical_analysis("TEST", "1mo")
self.assertIn("error", result)
self.assertEqual(result["error_code"], "INSUFFICIENT_DATA")
def test_get_technical_analysis_empty_data(self):
"""Test technical analysis with empty data."""
with patch('src.technical_analysis.fetch_historical_data') as mock_fetch:
mock_fetch.return_value = pd.DataFrame()
result = get_technical_analysis("TEST", "1mo")
self.assertIn("error", result)
# ========================================================================
# BOUNDARY VALUE TESTS
# ========================================================================
def test_ticker_boundary_values(self):
"""Test ticker validation with boundary values."""
from src.validators import validate_ticker
# Exactly 5 characters (max)
is_valid, _, _ = validate_ticker("ABCDE")
self.assertTrue(is_valid)
# Exactly 1 character (min)
is_valid, _, _ = validate_ticker("A")
self.assertTrue(is_valid)
# 6 characters (over limit)
is_valid, _, _ = validate_ticker("ABCDEF")
self.assertFalse(is_valid)
def test_portfolio_zero_cost_basis(self):
"""Test portfolio with zero cost basis."""
from src.portfolio import analyze_portfolio
portfolio_json = '{"AAPL": {"shares": 10, "cost_basis": 0}}'
# Should handle zero cost basis (might cause division by zero)
result = analyze_portfolio(portfolio_json)
# Should either return error or handle gracefully
self.assertIsInstance(result, dict)
if __name__ == '__main__':
unittest.main()