Files
XingHuo/tests/_ddl.py
T

180 lines
8.8 KiB
Python
Raw 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.
"""sqlite 测试 DDL 单一事实源(B8 · B4 评审 P3-12 收敛 / B5 评审 P3-4 时区)。
以 VARCHAR/TEXT/INTEGER/REAL 近似 MySQL 的 ENUM/SMALLINT/JSON/DECIMAL,
枚举防线不在测试 DB 层(由 service 白名单保证,见 test_profile_l3 口径注释)。
时间默认值统一 `(datetime('now','localtime'))`:sqlite CURRENT_TIMESTAMP 为 UTC,
本地 23:00~24:00 跑测试时与 MySQL localtime 口径错位一个日界(B5 评审 P3-4)。
列集为各测试所需列的并集;新表/新列只改此处,禁止再回测试文件手写。
"""
from sqlalchemy import create_engine, text
from sqlalchemy.pool import StaticPool
_TS = "TIMESTAMP DEFAULT (datetime('now','localtime'))"
SQLITE_TABLES: dict[str, str] = {
# ---- Core 模拟库(只读侧) ----
"core_customer": """
CREATE TABLE core_customer (
customer_id VARCHAR(64) PRIMARY KEY, display_name VARCHAR(128), age INTEGER,
occupation VARCHAR(64), open_date DATE, is_active TINYINT DEFAULT 1,
is_hnw TINYINT DEFAULT 0, service_tier VARCHAR(16) DEFAULT 'normal')
""",
"core_customer_risk": """
CREATE TABLE core_customer_risk (
customer_id VARCHAR(64), risk_code VARCHAR(8), evaluated_at TIMESTAMP,
expires_at TIMESTAMP, is_authoritative TINYINT DEFAULT 1,
source VARCHAR(32) DEFAULT 'risk_questionnaire',
investor_category VARCHAR(32) DEFAULT 'ordinary',
questionnaire_score INTEGER, max_loss_tolerance_pct REAL,
investment_goal VARCHAR(128), investment_horizon VARCHAR(16),
professional_approved_at TIMESTAMP)
""",
"core_customer_advisor": """
CREATE TABLE core_customer_advisor (
advisor_id VARCHAR(64), customer_id VARCHAR(64), rel_status VARCHAR(16))
""",
"core_suitability_rule": """
CREATE TABLE core_suitability_rule (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_risk_code VARCHAR(8), product_risk_code VARCHAR(8),
match_result VARCHAR(32), rule_ref VARCHAR(32) DEFAULT 'JR-AST-012')
""",
"core_product": """
CREATE TABLE core_product (
product_id VARCHAR(64) PRIMARY KEY, product_name VARCHAR(128),
min_risk_code VARCHAR(8), product_type VARCHAR(32),
min_subscribe_amount DECIMAL DEFAULT 1.00, term_days INTEGER,
requires_disclosure TINYINT DEFAULT 0)
""",
"core_holding": """
CREATE TABLE core_holding (
customer_id VARCHAR(64), product_id VARCHAR(64),
market_value DECIMAL, quantity DECIMAL)
""",
"core_trade": """
CREATE TABLE core_trade (
trade_id VARCHAR(64) PRIMARY KEY, customer_id VARCHAR(64), product_id VARCHAR(64),
trade_type VARCHAR(16), amount DECIMAL, trade_status VARCHAR(16),
traded_at TIMESTAMP)
""",
# ---- agent 库 ----
"audit_log": f"""
CREATE TABLE audit_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64), event_type VARCHAR(64),
agent_type VARCHAR(16), actor_id VARCHAR(64), customer_id VARCHAR(64),
rule_id VARCHAR(64), input_summary TEXT, decision VARCHAR(64), risk_score INTEGER,
handler_id VARCHAR(64), handler_result VARCHAR(64), handler_comment VARCHAR(512),
created_at {_TS})
""",
"input_guard_log": f"""
CREATE TABLE input_guard_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64), session_id VARCHAR(64),
agent_type VARCHAR(16), actor_id VARCHAR(64), guard_type VARCHAR(24),
raw_excerpt VARCHAR(1024), action VARCHAR(16),
created_at {_TS})
""",
"agent_session": f"""
CREATE TABLE agent_session (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64) UNIQUE,
trace_id VARCHAR(64), agent_type VARCHAR(16), actor_id VARCHAR(64),
actor_role VARCHAR(32), customer_id VARCHAR(64), advisor_id VARCHAR(64),
title VARCHAR(256), status VARCHAR(16) DEFAULT 'active',
created_at {_TS}, closed_at TIMESTAMP)
""",
"agent_message": f"""
CREATE TABLE agent_message (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64),
trace_id VARCHAR(64), seq_no INTEGER, role VARCHAR(16),
content TEXT, has_disclaimer TINYINT DEFAULT 0, token_est INTEGER,
created_at {_TS})
""",
"agent_tool_call": f"""
CREATE TABLE agent_tool_call (
id INTEGER PRIMARY KEY AUTOINCREMENT, session_id VARCHAR(64),
trace_id VARCHAR(64), message_id INTEGER, tool_name VARCHAR(128),
tool_input TEXT, tool_output TEXT, status VARCHAR(16),
error_code VARCHAR(64), latency_ms INTEGER,
created_at {_TS})
""",
"risk_alert": f"""
CREATE TABLE risk_alert (
alert_id VARCHAR(64) PRIMARY KEY, trace_id VARCHAR(64), customer_id VARCHAR(64),
trade_id VARCHAR(64), alert_type VARCHAR(16), triggered_rules TEXT,
risk_score INTEGER, status VARCHAR(24) DEFAULT 'pending_review', payload TEXT,
handler_id VARCHAR(64), handler_result VARCHAR(64), handler_comment VARCHAR(512),
created_at {_TS}, handled_at TIMESTAMP)
""",
"risk_suitability_log": f"""
CREATE TABLE risk_suitability_log (
id INTEGER PRIMARY KEY AUTOINCREMENT, trace_id VARCHAR(64) NOT NULL,
customer_id VARCHAR(64) NOT NULL, product_id VARCHAR(64) NOT NULL,
product_name VARCHAR(128),
customer_risk_level VARCHAR(8) NOT NULL, product_risk_level VARCHAR(8) NOT NULL,
investor_category VARCHAR(32) DEFAULT 'ordinary',
match_result VARCHAR(32) NOT NULL, mismatch_type VARCHAR(32) DEFAULT 'none',
is_matched TINYINT, is_blocked TINYINT DEFAULT 0,
requires_disclosure TINYINT DEFAULT 0, needs_branch_confirm TINYINT DEFAULT 0,
risk_was_expired TINYINT DEFAULT 0,
block_reason VARCHAR(512), block_response_code VARCHAR(32),
check_source VARCHAR(16) DEFAULT 'r02_trade', actor_id VARCHAR(64),
request_ref VARCHAR(64), profile_l1_version VARCHAR(32),
rule_refs TEXT, created_at {_TS})
""",
"customer_profile_l3": f"""
CREATE TABLE customer_profile_l3 (
customer_id VARCHAR(64) PRIMARY KEY, monitor_tier VARCHAR(16) NOT NULL,
risk_score INTEGER, score_dimensions TEXT, monitor_tags TEXT,
last_alert_id VARCHAR(64), computed_at TIMESTAMP NOT NULL,
updated_at {_TS})
""",
"risk_aml_list": f"""
CREATE TABLE risk_aml_list (
id INTEGER PRIMARY KEY AUTOINCREMENT, list_id VARCHAR(64), list_type VARCHAR(16),
full_name VARCHAR(128), id_no VARCHAR(32), bank_card_no VARCHAR(32),
match_threshold REAL, source VARCHAR(64), list_version VARCHAR(16),
effective_date DATE, is_active TINYINT DEFAULT 1, created_at {_TS})
""",
}
def create_sqlite_engine():
"""内存 sqlite + 全部表(StaticPool 单连接共享,同现有测试模式)。"""
engine = create_engine(
"sqlite://", poolclass=StaticPool, connect_args={"check_same_thread": False}
)
with engine.begin() as conn:
for ddl in SQLITE_TABLES.values():
conn.execute(text(ddl))
return engine
# C×R 适当性矩阵测试种子(与 scripts/core/02-seed-base.sql 25 行逐格一致;
# AL-05 换核后 check_suitability 以矩阵表为 L0 权威,缺失即全量 forbidden)
SUITABILITY_MATRIX_SEED: list[tuple[str, str, str]] = [
("C1", "R1", "allowed"), ("C1", "R2", "allowed"), ("C1", "R3", "forbidden"),
("C1", "R4", "forbidden"), ("C1", "R5", "forbidden"),
("C2", "R1", "allowed"), ("C2", "R2", "allowed"), ("C2", "R3", "allowed"),
("C2", "R4", "forbidden"), ("C2", "R5", "forbidden"),
("C3", "R1", "allowed"), ("C3", "R2", "allowed"), ("C3", "R3", "allowed"),
("C3", "R4", "allowed_with_disclosure"), ("C3", "R5", "forbidden"),
("C4", "R1", "allowed"), ("C4", "R2", "allowed"), ("C4", "R3", "allowed"),
("C4", "R4", "allowed"), ("C4", "R5", "allowed_with_disclosure"),
("C5", "R1", "allowed"), ("C5", "R2", "allowed"), ("C5", "R3", "allowed"),
("C5", "R4", "allowed"), ("C5", "R5", "allowed"),
]
def seed_suitability_matrix(engine) -> None:
"""给 sqlite 测试库灌 25 行 C×R 矩阵(各涉及适当性的测试 fixture 调用)。"""
with engine.begin() as conn:
for c, r, m in SUITABILITY_MATRIX_SEED:
conn.execute(
text(
"INSERT INTO core_suitability_rule "
"(customer_risk_code, product_risk_code, match_result) "
"VALUES (:c, :r, :m)"
),
{"c": c, "r": r, "m": m},
)