From bdaf188b33c46102dd0c7be3b350dcfa3b9fd300 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 21 Mar 2026 14:51:29 +0000 Subject: [PATCH] Fix Finnhub API error handling and add coverage Extracted the API request logic in `finnhub_news.py` to a private `_fetch_company_news_data` helper to properly catch `Exception` and return an empty list without violating the `str` return type of the main `get_company_news` function. Explicitly allows `ThirdPartyTimeoutError` to propagate to preserve timeout behavior. Added corresponding tests to mock generic API exceptions and invalid response types. Retained the test verifying fallback behavior for invalid numeric values within `get_insider_transactions`. Co-authored-by: aguzererler <6199053+aguzererler@users.noreply.github.com> --- tests/unit/test_finnhub_integration.py | 50 +++++++++++++++++++++++++ tradingagents/dataflows/finnhub_news.py | 22 ++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/tests/unit/test_finnhub_integration.py b/tests/unit/test_finnhub_integration.py index cde1743b..7097c1e8 100644 --- a/tests/unit/test_finnhub_integration.py +++ b/tests/unit/test_finnhub_integration.py @@ -756,6 +756,32 @@ class TestGetBasicFinancials: # --------------------------------------------------------------------------- + +# --------------------------------------------------------------------------- +# 10.5 finnhub_news — _fetch_company_news_data helper +# --------------------------------------------------------------------------- + +class TestFetchCompanyNewsData: + """_fetch_company_news_data handles errors by returning an empty list.""" + + _PATCH_TARGET = "tradingagents.dataflows.finnhub_news._make_api_request" + + def test_fetch_company_news_data_api_error_returns_empty_list(self): + from tradingagents.dataflows.finnhub_news import _fetch_company_news_data + + with patch(self._PATCH_TARGET, side_effect=Exception("API failure")): + result = _fetch_company_news_data("AAPL", {"symbol": "AAPL"}) + + assert result == [] + + def test_fetch_company_news_data_invalid_response_type_returns_empty_list(self): + from tradingagents.dataflows.finnhub_news import _fetch_company_news_data + + with patch(self._PATCH_TARGET, return_value={"error": "Not a list"}): + result = _fetch_company_news_data("AAPL", {"symbol": "AAPL"}) + + assert result == [] + # 10. finnhub_news — get_company_news # --------------------------------------------------------------------------- @@ -892,6 +918,30 @@ class TestGetInsiderTransactions: assert "AAPL" in result + def test_invalid_transaction_values_fall_back_to_raw_strings(self): + """Testing the error path at line 220 where float conversion fails.""" + from tradingagents.dataflows.finnhub_news import get_insider_transactions + + invalid_txn = { + "data": [ + { + "name": "Test Exec", + "transactionCode": "P", + "share": "invalid_share", + "price": "invalid_price", + "value": "invalid_value", + "transactionDate": "2024-01-10", + "filingDate": "2024-01-12", + } + ] + } + with patch(self._PATCH_TARGET, return_value=_json_response(invalid_txn)): + result = get_insider_transactions("AAPL") + + assert "invalid_share" in result + assert "invalid_price" in result + assert "invalid_value" in result + # --------------------------------------------------------------------------- # 13. finnhub_scanner — get_market_movers_finnhub diff --git a/tradingagents/dataflows/finnhub_news.py b/tradingagents/dataflows/finnhub_news.py index 65400379..165dca62 100644 --- a/tradingagents/dataflows/finnhub_news.py +++ b/tradingagents/dataflows/finnhub_news.py @@ -5,16 +5,21 @@ transaction data using the Finnhub REST API. Output formats mirror the Alpha Vantage news equivalents for consistent agent-facing data. """ +import logging from datetime import datetime from typing import Literal from .finnhub_common import ( + FinnhubError, + ThirdPartyTimeoutError, _make_api_request, _now_str, _to_unix_timestamp, ) +logger = logging.getLogger(__name__) + # --------------------------------------------------------------------------- # Type aliases # --------------------------------------------------------------------------- @@ -49,6 +54,21 @@ def _format_unix_ts(ts: int | None) -> str: # --------------------------------------------------------------------------- +def _fetch_company_news_data(symbol: str, params: dict) -> list[dict]: + """Helper to fetch company news and handle errors by returning an empty list.""" + try: + response = _make_api_request("company-news", params) + except ThirdPartyTimeoutError: + raise + except Exception as e: + logger.error(f"Error fetching company news for {symbol}: {e}") + return [] + + if not isinstance(response, list): + return [] + + return response + def get_company_news(symbol: str, start_date: str, end_date: str) -> str: """Fetch company-specific news via Finnhub /company-news. @@ -73,7 +93,7 @@ def get_company_news(symbol: str, start_date: str, end_date: str) -> str: "to": end_date, } - articles: list[dict] = _make_api_request("company-news", params) + articles: list[dict] = _fetch_company_news_data(symbol, params) header = ( f"# Company News: {symbol} ({start_date} to {end_date}) — Finnhub\n"