Files
xiaobaifupan/next/tests/test_review.py
T

143 lines
5.0 KiB
Python

from __future__ import annotations
from backend.database import MIGRATIONS, Database, MigrationRunner
from backend.features.review.prompt import messages
from backend.features.review.repository import ReviewRepository
from backend.features.review.views import trade_summary
def _database(tmp_path) -> Database:
database = Database(tmp_path / "review.db")
MigrationRunner(database).upgrade(MIGRATIONS)
with database.transaction() as connection:
connection.executemany(
"""INSERT INTO users (
id, username, username_key, password_hash, is_admin,
status, created_at, updated_at
) VALUES (?, ?, ?, 'hash', 0, 'active', 'now', 'now')""",
((1, "first", "first"), (2, "second", "second")),
)
return database
def test_private_review_records_are_strictly_scoped_and_daily_notes_upsert(tmp_path) -> None:
database = _database(tmp_path)
repository = ReviewRepository()
note = {
"code": "",
"stock_name": "",
"trade_date": "2026-07-30",
"summary": "缩量分化",
"content": "追高一次",
"plan": "等待承接",
}
trade = {
"trade_date": "2026-07-30",
"code": "000001",
"name": "平安银行",
"action": "buy",
"price": 10.0,
"quantity": 100,
"position_pct": 20.0,
"pnl_amount": None,
"pnl_pct": None,
"emotion": "calm",
"tags": ["计划内"],
"thesis": "承接",
"execution": "符合",
"id": None,
}
with database.transaction() as connection:
repository.save_watch(
connection,
1,
{
"identifier": "000001.SZ",
"name": "平安银行",
"sector": "银行",
},
"now",
)
repository.save_watch(
connection,
2,
{
"identifier": "600000.SH",
"name": "浦发银行",
"sector": "银行",
},
"now",
)
assert repository.save_watch_remark(connection, 1, "000001.SZ", "观察承接")
repository.save_watch(
connection,
1,
{"identifier": "000001.SZ", "name": "平安银行", "sector": "银行"},
"later",
)
note_id = repository.save_note(connection, 1, note, "now")
note["summary"] = "更新后的盘面"
assert repository.save_note(connection, 1, note, "later") == note_id
trade_id = repository.save_trade(connection, 1, trade, "now")
repository.save_alert(
connection,
1,
{
"kind": "manual",
"title": "复核",
"content": "看承接",
"available_date": "2026-07-30",
"code": "000001",
"dedupe_key": "one",
},
"now",
)
repository.add_message(
connection,
1,
{
"role": "user",
"content": "今天如何",
"context_date": "2026-07-30",
"request_id": None,
"status": "complete",
},
"now",
)
with database.read() as connection:
assert [row["identifier"] for row in repository.watchlist(connection, 1)] == ["000001.SZ"]
assert repository.watchlist(connection, 1)[0]["remark"] == "观察承接"
assert [row["identifier"] for row in repository.watchlist(connection, 2)] == ["600000.SH"]
assert repository.note(connection, 1, "", "2026-07-30")["summary"] == "更新后的盘面"
assert repository.note(connection, 2, "", "2026-07-30") is None
assert [row["id"] for row in repository.trades(connection, 1)] == [trade_id]
assert repository.trades(connection, 2) == ()
assert len(repository.alerts(connection, 1, False)) == 1
assert repository.alerts(connection, 2, False) == ()
assert len(repository.messages(connection, 1)) == 1
assert repository.messages(connection, 2) == ()
def test_trade_summary_uses_only_realized_fields_and_keeps_zero_in_denominator() -> None:
rows = [
{"pnl_amount": None, "pnl_pct": None, "position_pct": None},
{"pnl_amount": 100.0, "pnl_pct": None, "position_pct": 20.0},
{"pnl_amount": None, "pnl_pct": 0.0, "position_pct": 40.0},
{"pnl_amount": -20.0, "pnl_pct": -2.0, "position_pct": 60.0},
]
summary = trade_summary(rows)
assert summary == {
"total": 4,
"realized": 3,
"win_rate": 33.3,
"pnl_amount": 80.0,
"average_position": 40.0,
}
def test_review_prompt_separates_facts_records_inference_and_plan() -> None:
prompt = messages({"market_facts": {"temperature": 42}}, [], "明天怎么看")
system = prompt[0]["content"]
assert all(label in system for label in ("市场事实", "用户记录", "推断", "条件化计划"))
assert "不执行交易" in system