TradingAgents/tests/test_scanner_complete_e2e.py

297 lines
13 KiB
Python

"""
Complete end-to-end test for TradingAgents scanner functionality.
This test verifies that:
1. All scanner tools work correctly and return expected data formats
2. The scanner tools can be used to generate market analysis reports
3. The CLI scan command works end-to-end
4. Results are properly saved to files
"""
import tempfile
import os
from pathlib import Path
import pytest
# Set up the Python path to include the project root
import sys
sys.path.insert(0, '/Users/Ahmet/Repo/TradingAgents')
from tradingagents.agents.utils.scanner_tools import (
get_market_movers,
get_market_indices,
get_sector_performance,
get_industry_performance,
get_topic_news,
)
class TestScannerToolsIndividual:
"""Test each scanner tool individually."""
def test_get_market_movers(self):
"""Test market movers tool for all categories."""
for category in ["day_gainers", "day_losers", "most_actives"]:
result = get_market_movers.invoke({"category": category})
assert isinstance(result, str), f"Result should be string for {category}"
assert not result.startswith("Error:"), f"Should not error for {category}: {result[:100]}"
assert "# Market Movers:" in result, f"Missing header for {category}"
assert "| Symbol |" in result, f"Missing table header for {category}"
# Verify we got actual data
lines = result.split('\n')
data_lines = [line for line in lines if line.startswith('|') and 'Symbol' not in line]
assert len(data_lines) > 0, f"No data rows found for {category}"
def test_get_market_indices(self):
"""Test market indices tool."""
result = get_market_indices.invoke({})
assert isinstance(result, str), "Result should be string"
assert not result.startswith("Error:"), f"Should not error: {result[:100]}"
assert "# Major Market Indices" in result, "Missing header"
assert "| Index |" in result, "Missing table header"
# Verify we got data for major indices
assert "S&P 500" in result, "Missing S&P 500 data"
assert "Dow Jones" in result, "Missing Dow Jones data"
def test_get_sector_performance(self):
"""Test sector performance tool."""
result = get_sector_performance.invoke({})
assert isinstance(result, str), "Result should be string"
assert not result.startswith("Error:"), f"Should not error: {result[:100]}"
assert "# Sector Performance Overview" in result, "Missing header"
assert "| Sector |" in result, "Missing table header"
# Verify we got data for sectors
assert "Technology" in result or "Healthcare" in result, "Missing sector data"
def test_get_industry_performance(self):
"""Test industry performance tool."""
result = get_industry_performance.invoke({"sector_key": "technology"})
assert isinstance(result, str), "Result should be string"
assert not result.startswith("Error:"), f"Should not error: {result[:100]}"
assert "# Industry Performance: Technology" in result, "Missing header"
assert "| Company |" in result, "Missing table header"
# Verify we got data for companies
assert "NVIDIA" in result or "Apple" in result or "Microsoft" in result, "Missing company data"
def test_get_topic_news(self):
"""Test topic news tool."""
result = get_topic_news.invoke({"topic": "market", "limit": 3})
assert isinstance(result, str), "Result should be string"
assert not result.startswith("Error:"), f"Should not error: {result[:100]}"
assert "# News for Topic: market" in result, "Missing header"
assert "### " in result, "Missing news article headers"
# Verify we got news content
assert len(result) > 100, "News result too short"
class TestScannerWorkflow:
"""Test the complete scanner workflow."""
def test_complete_scanner_workflow_to_files(self):
"""Test that scanner tools can generate complete market analysis and save to files."""
with tempfile.TemporaryDirectory() as temp_dir:
# Set up directory structure like the CLI scan command
scan_date = "2026-03-15"
save_dir = Path(temp_dir) / "results" / "macro_scan" / scan_date
save_dir.mkdir(parents=True)
# Generate data using all scanner tools (this is what the CLI scan command does)
market_movers = get_market_movers.invoke({"category": "day_gainers"})
market_indices = get_market_indices.invoke({})
sector_performance = get_sector_performance.invoke({})
industry_performance = get_industry_performance.invoke({"sector_key": "technology"})
topic_news = get_topic_news.invoke({"topic": "market", "limit": 5})
# Save results to files (simulating CLI behavior)
(save_dir / "market_movers.txt").write_text(market_movers)
(save_dir / "market_indices.txt").write_text(market_indices)
(save_dir / "sector_performance.txt").write_text(sector_performance)
(save_dir / "industry_performance.txt").write_text(industry_performance)
(save_dir / "topic_news.txt").write_text(topic_news)
# Verify all files were created
assert (save_dir / "market_movers.txt").exists()
assert (save_dir / "market_indices.txt").exists()
assert (save_dir / "sector_performance.txt").exists()
assert (save_dir / "industry_performance.txt").exists()
assert (save_dir / "topic_news.txt").exists()
# Verify file contents have expected structure
movers_content = (save_dir / "market_movers.txt").read_text()
indices_content = (save_dir / "market_indices.txt").read_text()
sectors_content = (save_dir / "sector_performance.txt").read_text()
industry_content = (save_dir / "industry_performance.txt").read_text()
news_content = (save_dir / "topic_news.txt").read_text()
# Check headers
assert "# Market Movers:" in movers_content
assert "# Major Market Indices" in indices_content
assert "# Sector Performance Overview" in sectors_content
assert "# Industry Performance: Technology" in industry_content
assert "# News for Topic: market" in news_content
# Check table structures
assert "| Symbol |" in movers_content
assert "| Index |" in indices_content
assert "| Sector |" in sectors_content
assert "| Company |" in industry_content
# Check that we have meaningful data (not just headers)
assert len(movers_content) > 200
assert len(indices_content) > 200
assert len(sectors_content) > 200
assert len(industry_content) > 200
assert len(news_content) > 200
class TestScannerIntegration:
"""Test integration with CLI components."""
def test_tools_have_expected_interface(self):
"""Test that scanner tools have the interface expected by CLI."""
# The CLI scan command expects to call .invoke() on each tool
assert hasattr(get_market_movers, 'invoke')
assert hasattr(get_market_indices, 'invoke')
assert hasattr(get_sector_performance, 'invoke')
assert hasattr(get_industry_performance, 'invoke')
assert hasattr(get_topic_news, 'invoke')
# Verify they're callable with expected arguments
# Market movers requires category argument
result = get_market_movers.invoke({"category": "day_gainers"})
assert isinstance(result, str)
# Others don't require arguments (or have defaults)
result = get_market_indices.invoke({})
assert isinstance(result, str)
result = get_sector_performance.invoke({})
assert isinstance(result, str)
result = get_industry_performance.invoke({"sector_key": "technology"})
assert isinstance(result, str)
result = get_topic_news.invoke({"topic": "market", "limit": 3})
assert isinstance(result, str)
def test_tool_descriptions_match_expectations(self):
"""Test that tool descriptions match what the CLI expects."""
# These descriptions are used for documentation and help
assert "market movers" in get_market_movers.description.lower()
assert "market indices" in get_market_indices.description.lower()
assert "sector performance" in get_sector_performance.description.lower()
assert "industry" in get_industry_performance.description.lower()
assert "news" in get_topic_news.description.lower()
def test_scanner_end_to_end_demo():
"""Demonstration test showing the complete end-to-end scanner functionality."""
print("\n" + "="*60)
print("TRADINGAGENTS SCANNER END-TO-END DEMONSTRATION")
print("="*60)
# Show that all tools work
print("\n1. Testing Individual Scanner Tools:")
print("-" * 40)
# Market Movers
movers = get_market_movers.invoke({"category": "day_gainers"})
print(f"✓ Market Movers: {len(movers)} characters")
# Market Indices
indices = get_market_indices.invoke({})
print(f"✓ Market Indices: {len(indices)} characters")
# Sector Performance
sectors = get_sector_performance.invoke({})
print(f"✓ Sector Performance: {len(sectors)} characters")
# Industry Performance
industry = get_industry_performance.invoke({"sector_key": "technology"})
print(f"✓ Industry Performance: {len(industry)} characters")
# Topic News
news = get_topic_news.invoke({"topic": "market", "limit": 3})
print(f"✓ Topic News: {len(news)} characters")
# Show file output capability
print("\n2. Testing File Output Capability:")
print("-" * 40)
with tempfile.TemporaryDirectory() as temp_dir:
scan_date = "2026-03-15"
save_dir = Path(temp_dir) / "results" / "macro_scan" / scan_date
save_dir.mkdir(parents=True)
# Save all results
files_data = [
("market_movers.txt", movers),
("market_indices.txt", indices),
("sector_performance.txt", sectors),
("industry_performance.txt", industry),
("topic_news.txt", news)
]
for filename, content in files_data:
filepath = save_dir / filename
filepath.write_text(content)
assert filepath.exists()
print(f"✓ Created {filename} ({len(content)} chars)")
# Verify we can read them back
for filename, _ in files_data:
content = (save_dir / filename).read_text()
assert len(content) > 50 # Sanity check
print("\n3. Verifying Content Quality:")
print("-" * 40)
# Check that we got real financial data, not just error messages
assert not movers.startswith("Error:"), "Market movers should not error"
assert not indices.startswith("Error:"), "Market indices should not error"
assert not sectors.startswith("Error:"), "Sector performance should not error"
assert not industry.startswith("Error:"), "Industry performance should not error"
assert not news.startswith("Error:"), "Topic news should not error"
# Check for expected content patterns
assert "# Market Movers: Day Gainers" in movers or "# Market Movers: Day Losers" in movers or "# Market Movers: Most Actives" in movers
assert "# Major Market Indices" in indices
assert "# Sector Performance Overview" in sectors
assert "# Industry Performance: Technology" in industry
assert "# News for Topic: market" in news
print("✓ All tools returned valid financial data")
print("✓ All tools have proper headers and formatting")
print("✓ All tools can save/load data correctly")
print("\n" + "="*60)
print("END-TO-END SCANNER TEST: PASSED 🎉")
print("="*60)
print("The TradingAgents scanner functionality is working correctly!")
print("All tools generate proper financial market data and can save results to files.")
if __name__ == "__main__":
# Run the demonstration test
test_scanner_end_to_end_demo()
# Also run the individual test classes
print("\nRunning individual tool tests...")
test_instance = TestScannerToolsIndividual()
test_instance.test_get_market_movers()
test_instance.test_get_market_indices()
test_instance.test_get_sector_performance()
test_instance.test_get_industry_performance()
test_instance.test_get_topic_news()
print("✓ Individual tool tests passed")
workflow_instance = TestScannerWorkflow()
workflow_instance.test_complete_scanner_workflow_to_files()
print("✓ Workflow tests passed")
integration_instance = TestScannerIntegration()
integration_instance.test_tools_have_expected_interface()
integration_instance.test_tool_descriptions_match_expectations()
print("✓ Integration tests passed")
print("\n✅ ALL TESTS PASSED - Scanner functionality is working correctly!")