Make engine event payloads JSON safe
This commit is contained in:
parent
a90603f4f6
commit
247a1c5107
@ -112,3 +112,39 @@ def test_bootstrap_schema_contains_migrated_core_columns_and_tables():
|
|||||||
|
|
||||||
for snippet in required_snippets:
|
for snippet in required_snippets:
|
||||||
assert snippet in schema_sql
|
assert snippet in schema_sql
|
||||||
|
|
||||||
|
|
||||||
|
def test_insert_engine_event_serializes_nested_datetimes():
|
||||||
|
from indian_paper_trading_strategy.engine.db import insert_engine_event
|
||||||
|
|
||||||
|
class FakeCursor:
|
||||||
|
def __init__(self):
|
||||||
|
self.params = None
|
||||||
|
|
||||||
|
def execute(self, _sql, params):
|
||||||
|
self.params = params
|
||||||
|
|
||||||
|
cursor = FakeCursor()
|
||||||
|
event_ts = datetime(2026, 4, 15, 9, 0, tzinfo=timezone.utc)
|
||||||
|
payload_ts = datetime(2026, 4, 15, 9, 1, tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
insert_engine_event(
|
||||||
|
cursor,
|
||||||
|
"ORDER_PLACED",
|
||||||
|
data={
|
||||||
|
"order_id": "order-1",
|
||||||
|
"timestamp": payload_ts,
|
||||||
|
"history": [payload_ts],
|
||||||
|
},
|
||||||
|
meta={"checked_at": payload_ts},
|
||||||
|
ts=event_ts,
|
||||||
|
user_id="user-1",
|
||||||
|
run_id="run-1",
|
||||||
|
)
|
||||||
|
|
||||||
|
data_json = cursor.params[4]
|
||||||
|
meta_json = cursor.params[6]
|
||||||
|
|
||||||
|
assert data_json.adapted["timestamp"] == payload_ts.isoformat()
|
||||||
|
assert data_json.adapted["history"] == [payload_ts.isoformat()]
|
||||||
|
assert meta_json.adapted["checked_at"] == payload_ts.isoformat()
|
||||||
|
|||||||
@ -2,8 +2,10 @@ import os
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import date, datetime, timedelta, timezone
|
||||||
from contextvars import ContextVar
|
from contextvars import ContextVar
|
||||||
|
from decimal import Decimal
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
from psycopg2 import pool
|
from psycopg2 import pool
|
||||||
@ -146,6 +148,22 @@ def _utc_now():
|
|||||||
return datetime.utcnow().replace(tzinfo=timezone.utc)
|
return datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
|
def _json_safe(value):
|
||||||
|
if isinstance(value, datetime):
|
||||||
|
return value.isoformat()
|
||||||
|
if isinstance(value, date):
|
||||||
|
return value.isoformat()
|
||||||
|
if isinstance(value, Decimal):
|
||||||
|
return float(value)
|
||||||
|
if isinstance(value, UUID):
|
||||||
|
return str(value)
|
||||||
|
if isinstance(value, dict):
|
||||||
|
return {str(key): _json_safe(item) for key, item in value.items()}
|
||||||
|
if isinstance(value, (list, tuple, set)):
|
||||||
|
return [_json_safe(item) for item in value]
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def set_context(user_id: str | None, run_id: str | None):
|
def set_context(user_id: str | None, run_id: str | None):
|
||||||
token_user = _USER_ID.set(user_id)
|
token_user = _USER_ID.set(user_id)
|
||||||
token_run = _RUN_ID.set(run_id)
|
token_run = _RUN_ID.set(run_id)
|
||||||
@ -328,9 +346,9 @@ def insert_engine_event(
|
|||||||
scope_run,
|
scope_run,
|
||||||
when,
|
when,
|
||||||
event,
|
event,
|
||||||
Json(data) if data is not None else None,
|
Json(_json_safe(data)) if data is not None else None,
|
||||||
message,
|
message,
|
||||||
Json(meta) if meta is not None else None,
|
Json(_json_safe(meta)) if meta is not None else None,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user