Files
root 691b8cdd0c feat: 初始化黄小瓜AI助手记忆仓库
- 核心配置: IDENTITY, USER, SOUL, AGENTS, TOOLS, HEARTBEAT, MEMORY
- memory/: 每日总结和临时记录
- skills/: 所有已安装技能
- notes/: 语音配置笔记
2026-04-04 02:42:48 +08:00

274 lines
9.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
Stock Monitor Pro - 完整测试套件
测试所有功能模块,确保系统稳定性
"""
import sys
import time
import unittest
from datetime import datetime, timedelta
from unittest.mock import Mock, patch
sys.path.insert(0, '/home/wesley/.openclaw/workspace/skills/stock-monitor/scripts')
from monitor import StockAlert, WATCHLIST
class TestDataFetching(unittest.TestCase):
"""测试1: 数据获取模块"""
def setUp(self):
self.monitor = StockAlert()
def test_sina_realtime_api(self):
"""测试新浪实时行情API"""
data = self.monitor.fetch_sina_realtime([WATCHLIST[0]])
self.assertIn('600362', data)
self.assertGreater(data['600362']['price'], 0)
print("✅ 新浪实时行情API正常")
def test_gold_api(self):
"""测试伦敦金API"""
data = self.monitor.fetch_sina_realtime([WATCHLIST[-1]])
self.assertIn('XAU', data)
self.assertGreater(data['XAU']['price'], 4000) # 黄金应该在4000以上
print("✅ 伦敦金API正常")
def test_data_validity(self):
"""测试数据有效性检查"""
data = self.monitor.fetch_sina_realtime(WATCHLIST[:3])
for code, d in data.items():
self.assertGreater(d['price'], 0, f"{code}价格无效")
self.assertGreater(d['prev_close'], 0, f"{code}昨收无效")
print("✅ 所有数据有效性检查通过")
class TestAlertRules(unittest.TestCase):
"""测试2: 预警规则模块"""
def setUp(self):
self.monitor = StockAlert()
def test_cost_percentage_alert(self):
"""测试成本百分比预警"""
stock = WATCHLIST[0].copy()
stock['alerts'] = {'cost_pct_above': 10.0, 'cost_pct_below': -10.0}
# 模拟盈利10%的数据
data = {'price': 62.7, 'prev_close': 57.0, 'cost': 57.0} # 成本57现价62.7=+10%
alerts, level = self.monitor.check_alerts(stock, data)
has_profit_alert = any('盈利' in text for _, text in alerts)
self.assertTrue(has_profit_alert, "应该有盈利预警")
print("✅ 成本百分比预警正常")
def test_daily_change_alert(self):
"""测试日内涨跌幅预警"""
stock = WATCHLIST[0].copy()
stock['alerts'] = {'change_pct_above': 5.0, 'change_pct_below': -5.0}
# 模拟大涨6%
data = {'price': 60.42, 'prev_close': 57.0, 'cost': 57.0}
alerts, level = self.monitor.check_alerts(stock, data)
has_change_alert = any('大涨' in text or '大跌' in text for _, text in alerts)
self.assertTrue(has_change_alert, "应该有涨跌幅预警")
print("✅ 日内涨跌幅预警正常")
def test_no_duplicate_alerts(self):
"""测试防重复机制"""
stock = WATCHLIST[0].copy()
stock['alerts'] = {'cost_pct_above': 5.0}
data = {'price': 60.0, 'prev_close': 57.0, 'cost': 57.0}
# 第一次应该触发
alerts1, _ = self.monitor.check_alerts(stock, data)
self.assertGreater(len(alerts1), 0, "第一次应该触发预警")
# 记录预警
for alert_type, _ in alerts1:
self.monitor.record_alert(stock['code'], alert_type)
# 第二次不应该触发 (30分钟内)
alerts2, _ = self.monitor.check_alerts(stock, data)
self.assertEqual(len(alerts2), 0, "30分钟内不应重复触发")
print("✅ 防重复机制正常")
class TestAlertLevel(unittest.TestCase):
"""测试3: 分级预警系统"""
def setUp(self):
self.monitor = StockAlert()
def test_critical_level(self):
"""测试紧急级别"""
alerts = [('a', 'test'), ('b', 'test'), ('c', 'test')]
weights = [3, 3, 3] # 总权重9
level = self.monitor._calculate_alert_level(alerts, weights, 'individual')
self.assertEqual(level, 'critical')
print("✅ 紧急级别判断正常")
def test_warning_level(self):
"""测试警告级别"""
alerts = [('a', 'test'), ('b', 'test')]
weights = [2, 2] # 总权重4
level = self.monitor._calculate_alert_level(alerts, weights, 'individual')
self.assertEqual(level, 'warning')
print("✅ 警告级别判断正常")
def test_info_level(self):
"""测试提醒级别"""
alerts = [('a', 'test')]
weights = [1]
level = self.monitor._calculate_alert_level(alerts, weights, 'individual')
self.assertEqual(level, 'info')
print("✅ 提醒级别判断正常")
class TestStockTypeDifferentiation(unittest.TestCase):
"""测试4: 差异化配置"""
def test_individual_stock_threshold(self):
"""测试个股阈值"""
stock = [s for s in WATCHLIST if s.get('type') == 'individual'][0]
self.assertEqual(stock['alerts']['change_pct_above'], 4.0)
print("✅ 个股阈值配置正确")
def test_etf_threshold(self):
"""测试ETF阈值"""
stock = [s for s in WATCHLIST if s.get('type') == 'etf'][0]
self.assertEqual(stock['alerts']['change_pct_above'], 2.0)
print("✅ ETF阈值配置正确")
def test_gold_threshold(self):
"""测试黄金阈值"""
stock = [s for s in WATCHLIST if s.get('type') == 'gold'][0]
self.assertEqual(stock['alerts']['change_pct_above'], 2.5)
print("✅ 黄金阈值配置正确")
class TestSmartSchedule(unittest.TestCase):
"""测试5: 智能频率控制"""
def setUp(self):
self.monitor = StockAlert()
def test_market_hours_detection(self):
"""测试交易时间检测"""
# 当前是纽约时间,转换成北京时间
ny_now = datetime.now()
beijing_now = ny_now + timedelta(hours=13)
schedule = self.monitor.should_run_now()
self.assertIn('mode', schedule)
self.assertIn(schedule['mode'], ['market', 'lunch', 'after_hours', 'night', 'weekend'])
print(f"✅ 时间检测正常 (当前模式: {schedule['mode']})")
def test_interval_settings(self):
"""测试不同模式的间隔设置"""
schedule = self.monitor.should_run_now()
interval = schedule.get('interval', 0)
self.assertGreater(interval, 0)
self.assertIn(interval, [300, 600, 1800, 3600]) # 5/10/30/60分钟
print(f"✅ 间隔设置正常 ({interval//60}分钟)")
class TestMessageFormat(unittest.TestCase):
"""测试6: 消息格式"""
def setUp(self):
self.monitor = StockAlert()
def test_message_contains_required_elements(self):
"""测试消息包含必要元素"""
# 模拟触发预警
stock = WATCHLIST[0]
data = {'price': 54.0, 'prev_close': 57.0, 'open': 55.0, 'high': 56.0, 'low': 53.0}
alerts, level = [('cost_below', '📉 亏损10%')], 'warning'
# 构建消息
change_pct = -5.26
msg = f"<b>⚠️ 【警告】🟢 {stock['name']} ({stock['code']})</b>\n"
msg += f"💰 当前价格: ¥{data['price']:.2f} ({change_pct:+.2f}%)\n"
msg += f"🎯 触发预警:\n{alerts[0][1]}\n"
# 检查必要元素
self.assertIn('【警告】', msg)
self.assertIn('🟢', msg) # 绿跌
self.assertIn('💰', msg)
self.assertIn('🎯', msg)
print("✅ 消息格式包含必要元素")
class TestIntegration(unittest.TestCase):
"""测试7: 集成测试"""
def setUp(self):
self.monitor = StockAlert()
def test_full_run_once(self):
"""测试完整run_once流程"""
start = time.time()
alerts_list = self.monitor.run_once(smart_mode=True)
elapsed = time.time() - start
# 执行时间应该合理 (10-30秒)
self.assertLess(elapsed, 60, "执行时间过长")
self.assertIsInstance(alerts_list, list)
print(f"✅ 完整流程正常 (执行时间: {elapsed:.2f}秒, 触发{len(alerts_list)}条)")
def test_all_stocks_monitored(self):
"""测试所有股票都被监控"""
data = self.monitor.fetch_sina_realtime(WATCHLIST)
# 至少应该获取到部分数据
self.assertGreater(len(data), 0)
print(f"✅ 监控覆盖正常 (获取到{len(data)}/{len(WATCHLIST)}只数据)")
def run_all_tests():
"""运行所有测试"""
print("=" * 70)
print("🧪 Stock Monitor Pro - 完整测试套件")
print("=" * 70)
# 创建测试套件
loader = unittest.TestLoader()
suite = unittest.TestSuite()
# 添加所有测试类
suite.addTests(loader.loadTestsFromTestCase(TestDataFetching))
suite.addTests(loader.loadTestsFromTestCase(TestAlertRules))
suite.addTests(loader.loadTestsFromTestCase(TestAlertLevel))
suite.addTests(loader.loadTestsFromTestCase(TestStockTypeDifferentiation))
suite.addTests(loader.loadTestsFromTestCase(TestSmartSchedule))
suite.addTests(loader.loadTestsFromTestCase(TestMessageFormat))
suite.addTests(loader.loadTestsFromTestCase(TestIntegration))
# 运行测试
runner = unittest.TextTestRunner(verbosity=2)
result = runner.run(suite)
# 输出总结
print("\n" + "=" * 70)
print("📊 测试总结")
print("=" * 70)
print(f" 测试总数: {result.testsRun}")
print(f" 通过: {result.testsRun - len(result.failures) - len(result.errors)}")
print(f" 失败: {len(result.failures)}")
print(f" 错误: {len(result.errors)}")
if result.wasSuccessful():
print("\n✅ 所有测试通过!系统可以正常运行。")
else:
print("\n⚠️ 部分测试失败,请检查日志。")
return result.wasSuccessful()
if __name__ == '__main__':
success = run_all_tests()
sys.exit(0 if success else 1)