Files
ProStock/tests/test_moneyflow.py

251 lines
8.9 KiB
Python
Raw Normal View History

"""Tests for api_moneyflow module.
Tests the moneyflow (个股资金流向) API wrapper.
"""
import pytest
import pandas as pd
from unittest.mock import patch, MagicMock
from src.data.api_wrappers.api_moneyflow import (
get_moneyflow,
sync_moneyflow,
preview_moneyflow_sync,
MoneyflowSync,
)
class TestMoneyflowAPI:
"""Test suite for moneyflow API wrapper."""
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_get_moneyflow_by_date(self, mock_client_class):
"""Test fetching moneyflow data by date."""
# Setup mock
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.query.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ", "000002.SZ"],
"trade_date": ["20240115", "20240115"],
"buy_sm_vol": [10000, 20000],
"buy_sm_amount": [100.5, 200.5],
"sell_sm_vol": [8000, 15000],
"sell_sm_amount": [80.5, 150.5],
"buy_md_vol": [5000, 10000],
"buy_md_amount": [500.5, 1000.5],
"sell_md_vol": [4000, 8000],
"sell_md_amount": [400.5, 800.5],
"buy_lg_vol": [2000, 5000],
"buy_lg_amount": [2000.5, 5000.5],
"sell_lg_vol": [1500, 4000],
"sell_lg_amount": [1500.5, 4000.5],
"buy_elg_vol": [1000, 3000],
"buy_elg_amount": [5000.5, 15000.5],
"sell_elg_vol": [800, 2500],
"sell_elg_amount": [4000.5, 12500.5],
"net_mf_vol": [2700, 8000],
"net_mf_amount": [220.0, 550.0],
}
)
# Test
result = get_moneyflow(trade_date="20240115")
# Assert
assert not result.empty
assert len(result) == 2
assert "ts_code" in result.columns
assert "trade_date" in result.columns
assert "buy_sm_vol" in result.columns
assert "net_mf_amount" in result.columns
mock_client.query.assert_called_once_with("moneyflow", trade_date="20240115")
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_get_moneyflow_by_stock(self, mock_client_class):
"""Test fetching moneyflow data by stock code."""
# Setup mock
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.query.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ", "000001.SZ"],
"trade_date": ["20240114", "20240115"],
"buy_sm_vol": [10000, 11000],
"buy_sm_amount": [100.5, 110.5],
"sell_sm_vol": [8000, 8500],
"sell_sm_amount": [80.5, 85.5],
"buy_md_vol": [5000, 5500],
"buy_md_amount": [500.5, 550.5],
"sell_md_vol": [4000, 4200],
"sell_md_amount": [400.5, 420.5],
"buy_lg_vol": [2000, 2200],
"buy_lg_amount": [2000.5, 2200.5],
"sell_lg_vol": [1500, 1600],
"sell_lg_amount": [1500.5, 1600.5],
"buy_elg_vol": [1000, 1100],
"buy_elg_amount": [5000.5, 5500.5],
"sell_elg_vol": [800, 850],
"sell_elg_amount": [4000.5, 4250.5],
"net_mf_vol": [2700, 2950],
"net_mf_amount": [220.0, 245.0],
}
)
# Test
result = get_moneyflow(
ts_code="000001.SZ", start_date="20240101", end_date="20240131"
)
# Assert
assert not result.empty
assert len(result) == 2
mock_client.query.assert_called_once_with(
"moneyflow", ts_code="000001.SZ", start_date="20240101", end_date="20240131"
)
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_get_moneyflow_empty_response(self, mock_client_class):
"""Test handling empty response."""
# Setup mock
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.query.return_value = pd.DataFrame()
# Test
result = get_moneyflow(trade_date="20240101")
# Assert
assert result.empty
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_get_moneyflow_with_shared_client(self, mock_client_class):
"""Test fetching with shared client for rate limiting."""
# Setup mock shared client
mock_shared_client = MagicMock()
mock_shared_client.query.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ"],
"trade_date": ["20240115"],
"buy_sm_vol": [10000],
"net_mf_amount": [220.0],
}
)
# Test with shared client
result = get_moneyflow(trade_date="20240115", client=mock_shared_client)
# Assert
assert not result.empty
mock_shared_client.query.assert_called_once_with(
"moneyflow", trade_date="20240115"
)
mock_client_class.assert_not_called() # Should not create new client
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_get_moneyflow_date_column_rename(self, mock_client_class):
"""Test that 'date' column is renamed to 'trade_date'."""
# Setup mock with 'date' column (should be renamed)
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.query.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ"],
"date": ["20240115"], # Using 'date' instead of 'trade_date'
"buy_sm_vol": [10000],
"net_mf_amount": [220.0],
}
)
# Test
result = get_moneyflow(trade_date="20240115")
# Assert - date column should be renamed to trade_date
assert "trade_date" in result.columns
assert "date" not in result.columns
assert result["trade_date"].iloc[0] == "20240115"
class TestMoneyflowSync:
"""Test suite for MoneyflowSync class."""
def test_moneyflow_sync_class_attributes(self):
"""Test MoneyflowSync class has correct attributes."""
assert MoneyflowSync.table_name == "moneyflow"
assert MoneyflowSync.default_start_date == "20100101"
assert "ts_code" in MoneyflowSync.TABLE_SCHEMA
assert "trade_date" in MoneyflowSync.TABLE_SCHEMA
assert "buy_sm_vol" in MoneyflowSync.TABLE_SCHEMA
assert "net_mf_amount" in MoneyflowSync.TABLE_SCHEMA
assert MoneyflowSync.PRIMARY_KEY == ("ts_code", "trade_date")
@patch("src.data.api_wrappers.api_moneyflow.get_moneyflow")
@patch("src.data.api_wrappers.api_moneyflow.TushareClient")
def test_fetch_single_date(self, mock_client_class, mock_get_moneyflow):
"""Test fetch_single_date method."""
# Setup mock
mock_get_moneyflow.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ"],
"trade_date": ["20240115"],
"buy_sm_vol": [10000],
"net_mf_amount": [220.0],
}
)
# Create sync instance
sync = MoneyflowSync()
# Test
result = sync.fetch_single_date("20240115")
# Assert
assert not result.empty
mock_get_moneyflow.assert_called_once_with(
trade_date="20240115", client=sync.client
)
@patch("src.data.api_wrappers.api_moneyflow.MoneyflowSync.sync_all")
def test_sync_moneyflow_function(self, mock_sync_all):
"""Test sync_moneyflow convenience function."""
# Setup mock
mock_sync_all.return_value = pd.DataFrame(
{
"ts_code": ["000001.SZ"],
"trade_date": ["20240115"],
}
)
# Test
result = sync_moneyflow(start_date="20240101", end_date="20240131")
# Assert
assert not result.empty
mock_sync_all.assert_called_once_with(
start_date="20240101", end_date="20240131", force_full=False
)
@patch("src.data.api_wrappers.api_moneyflow.MoneyflowSync.preview_sync")
def test_preview_moneyflow_sync_function(self, mock_preview_sync):
"""Test preview_moneyflow_sync convenience function."""
# Setup mock
mock_preview_sync.return_value = {
"sync_needed": True,
"date_count": 31,
"start_date": "20240101",
"end_date": "20240131",
"estimated_records": 10000,
"sample_data": pd.DataFrame(),
"mode": "incremental",
}
# Test
result = preview_moneyflow_sync(start_date="20240101", end_date="20240131")
# Assert
assert result["sync_needed"] is True
assert result["date_count"] == 31
mock_preview_sync.assert_called_once_with(
start_date="20240101", end_date="20240131", force_full=False, sample_size=3
)