feat: persist unmanaged position lifecycle state
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
ae2e598209
commit
fe51d211d9
@@ -0,0 +1,101 @@
|
||||
"""PostgreSQLUnmanagedPositionStore integration tests."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pg_unmanaged_store(pg_clean):
|
||||
from trex_core.adapters.portfolio.unmanaged_position_store import (
|
||||
PostgreSQLUnmanagedPositionStore,
|
||||
)
|
||||
|
||||
return PostgreSQLUnmanagedPositionStore(pg_clean)
|
||||
|
||||
|
||||
class TestMigration028:
|
||||
def test_tables_exist_after_migration(self, pg_clean):
|
||||
import psycopg
|
||||
|
||||
with psycopg.connect(pg_clean) as conn, conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT table_name FROM information_schema.tables "
|
||||
"WHERE table_schema = 'public' "
|
||||
"AND table_name IN ('unmanaged_position_state', 'unmanaged_position_audit')"
|
||||
)
|
||||
assert {row[0] for row in cur.fetchall()} == {
|
||||
"unmanaged_position_state",
|
||||
"unmanaged_position_audit",
|
||||
}
|
||||
|
||||
|
||||
class TestCRUDRoundTrip:
|
||||
def test_observe_change_resolve_and_audit(self, pg_unmanaged_store):
|
||||
t1 = datetime(2026, 9, 7, 0, 0, tzinfo=UTC)
|
||||
t2 = t1 + timedelta(minutes=1)
|
||||
t3 = t2 + timedelta(minutes=1)
|
||||
|
||||
first = pg_unmanaged_store.observe(
|
||||
instance_key="inst-A",
|
||||
symbol="008110",
|
||||
quantity=3,
|
||||
observed_at=t1,
|
||||
)
|
||||
repeat = pg_unmanaged_store.observe(
|
||||
instance_key="inst-A",
|
||||
symbol="008110",
|
||||
quantity=3,
|
||||
observed_at=t2,
|
||||
)
|
||||
changed = pg_unmanaged_store.observe(
|
||||
instance_key="inst-A",
|
||||
symbol="008110",
|
||||
quantity=5,
|
||||
observed_at=t3,
|
||||
)
|
||||
recorded = pg_unmanaged_store.record_operator_action(
|
||||
instance_key="inst-A",
|
||||
symbol="008110",
|
||||
action="approved_exception",
|
||||
evidence_ref="kis:2026-09-11",
|
||||
actor="cli",
|
||||
note="broker-confirmed",
|
||||
)
|
||||
resolved = pg_unmanaged_store.resolve_absent(
|
||||
instance_key="inst-A",
|
||||
observed_symbols=set(),
|
||||
observed_at=t3 + timedelta(minutes=1),
|
||||
)
|
||||
|
||||
assert first["transition"] == "detected"
|
||||
assert repeat["transition"] == "unchanged"
|
||||
assert changed["transition"] == "quantity_changed"
|
||||
assert recorded is not None
|
||||
assert len(resolved) == 1
|
||||
state = pg_unmanaged_store.get_state(instance_key="inst-A", symbol="008110")
|
||||
assert state is not None
|
||||
assert state["quantity"] == 5
|
||||
assert state["first_seen_at"] == t1
|
||||
assert state["last_seen_at"] == t3
|
||||
assert state["quantity_changed_at"] == t3
|
||||
assert state["lifecycle_state"] == "resolved"
|
||||
assert state["evidence_ref"] == "kis:2026-09-11"
|
||||
audit = pg_unmanaged_store.list_audit(instance_key="inst-A", symbol="008110")
|
||||
assert [row["event_type"] for row in audit] == [
|
||||
"detected",
|
||||
"quantity_changed",
|
||||
"operator_action",
|
||||
"resolved",
|
||||
]
|
||||
|
||||
def test_same_symbol_isolated_by_instance(self, pg_unmanaged_store):
|
||||
pg_unmanaged_store.observe(instance_key="inst-A", symbol="008110", quantity=3)
|
||||
pg_unmanaged_store.observe(instance_key="inst-B", symbol="008110", quantity=7)
|
||||
pg_unmanaged_store.resolve_absent(instance_key="inst-A", observed_symbols=set())
|
||||
|
||||
rows_a = pg_unmanaged_store.list_states(instance_key="inst-A", lifecycle_state="all")
|
||||
rows_b = pg_unmanaged_store.list_states(instance_key="inst-B", lifecycle_state="all")
|
||||
assert len(rows_a) == 1 and rows_a[0]["lifecycle_state"] == "resolved"
|
||||
assert len(rows_b) == 1 and rows_b[0]["quantity"] == 7
|
||||
Reference in New Issue
Block a user