42e56d940c
Only closed groups can be removed from local history; equity is unchanged. Co-authored-by: Cursor <cursoragent@cursor.com>
93 lines
3.2 KiB
Python
93 lines
3.2 KiB
Python
"""删除单条已平仓交易记录。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from app.api.trades import delete_group
|
|
from app.models.db import Database
|
|
|
|
|
|
def _seed_closed(db: Database, gid: str = "G-DEL-1") -> None:
|
|
with db._lock:
|
|
db._conn.execute(
|
|
"""INSERT INTO groups(group_id, status, open_at_ms, close_at_ms, realized_pnl)
|
|
VALUES (?, 'closed', 1, 2, 1.5)""",
|
|
(gid,),
|
|
)
|
|
db._conn.execute(
|
|
"""INSERT INTO fills(group_id, leg, action, side, inst_id, qty_eth,
|
|
fill_px, fee, slip, notional, ts_ms)
|
|
VALUES (?, 'option', 'open', 'buy', 'ETH-C', 1, 10, 0.1, 0, 10, 1)""",
|
|
(gid,),
|
|
)
|
|
db._conn.execute(
|
|
"""INSERT INTO residual_options(
|
|
group_id, option_inst_id, option_side, option_qty_eth,
|
|
option_entry_px, status, created_at_ms
|
|
) VALUES (?, 'ETH-C', 'call', 1, 10, 'pending', 1)""",
|
|
(gid,),
|
|
)
|
|
db._conn.execute(
|
|
"""INSERT INTO ledger_entries(group_id, kind, amount, balance_after, note, ts_ms)
|
|
VALUES (?, 'pnl', 1.5, 10001.5, 't', 2)""",
|
|
(gid,),
|
|
)
|
|
db._conn.commit()
|
|
|
|
|
|
def test_delete_closed_group(tmp_path, monkeypatch) -> None:
|
|
monkeypatch.setenv("MODE", "SIM")
|
|
db = Database(tmp_path / "del.db")
|
|
monkeypatch.setattr("app.api.trades.get_db", lambda: db)
|
|
_seed_closed(db)
|
|
|
|
out = asyncio.run(delete_group("G-DEL-1", _user="t"))
|
|
assert out["ok"] is True
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM groups")["c"] == 0
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM fills")["c"] == 0
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM residual_options")["c"] == 0
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM ledger_entries")["c"] == 0
|
|
db.close()
|
|
|
|
|
|
def test_delete_open_group_refused(tmp_path, monkeypatch) -> None:
|
|
monkeypatch.setenv("MODE", "SIM")
|
|
db = Database(tmp_path / "del_open.db")
|
|
monkeypatch.setattr("app.api.trades.get_db", lambda: db)
|
|
with db._lock:
|
|
db._conn.execute(
|
|
"""INSERT INTO groups(group_id, status, open_at_ms)
|
|
VALUES ('G-OPEN', 'open', 1)"""
|
|
)
|
|
db._conn.commit()
|
|
|
|
with pytest.raises(HTTPException) as ei:
|
|
asyncio.run(delete_group("G-OPEN", _user="t"))
|
|
assert ei.value.status_code == 409
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM groups")["c"] == 1
|
|
db.close()
|
|
|
|
|
|
def test_delete_active_position_refused(tmp_path, monkeypatch) -> None:
|
|
monkeypatch.setenv("MODE", "SIM")
|
|
db = Database(tmp_path / "del_pos.db")
|
|
monkeypatch.setattr("app.api.trades.get_db", lambda: db)
|
|
_seed_closed(db, "G-POS")
|
|
with db._lock:
|
|
# status closed but still referenced (edge)
|
|
db._conn.execute(
|
|
"UPDATE positions SET group_id=?, status='open' WHERE id=1",
|
|
("G-POS",),
|
|
)
|
|
db._conn.commit()
|
|
|
|
with pytest.raises(HTTPException) as ei:
|
|
asyncio.run(delete_group("G-POS", _user="t"))
|
|
assert ei.value.status_code == 409
|
|
assert db.fetchone("SELECT COUNT(*) AS c FROM groups")["c"] == 1
|
|
db.close()
|