101 lines
3.8 KiB
Python
101 lines
3.8 KiB
Python
import importlib.util
|
|
from pathlib import Path
|
|
|
|
import sqlalchemy as sa
|
|
|
|
|
|
def _load_migration_module():
|
|
path = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "alembic"
|
|
/ "versions"
|
|
/ "20260326_create_feedback_center_tables.py"
|
|
)
|
|
spec = importlib.util.spec_from_file_location("feedback_center_migration", path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
assert spec is not None
|
|
assert spec.loader is not None
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def _feedback_center_records_table(metadata: sa.MetaData) -> sa.Table:
|
|
return sa.Table(
|
|
"feedback_center_records",
|
|
metadata,
|
|
sa.Column("id", sa.Integer(), primary_key=True, autoincrement=True),
|
|
sa.Column("tenant_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("project_id", sa.BigInteger(), nullable=True),
|
|
sa.Column("source_type", sa.VARCHAR(length=32), nullable=False),
|
|
sa.Column("source_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("object_type", sa.VARCHAR(length=32), nullable=False),
|
|
sa.Column("object_id", sa.BigInteger(), nullable=False),
|
|
sa.Column("field_name", sa.VARCHAR(length=128), nullable=False),
|
|
sa.Column("before_value", sa.JSON(), nullable=True),
|
|
sa.Column("after_value", sa.JSON(), nullable=True),
|
|
sa.Column("ai_snapshot", sa.JSON(), nullable=True),
|
|
sa.Column("ai_confidence", sa.Float(), nullable=True),
|
|
sa.Column("reason_candidates", sa.JSON(), nullable=True),
|
|
sa.Column("reason_code", sa.VARCHAR(length=64), nullable=True),
|
|
sa.Column("reason_text", sa.Text(), nullable=True),
|
|
sa.Column("review_status", sa.VARCHAR(length=16), nullable=False),
|
|
sa.Column("reviewed_by", sa.BigInteger(), nullable=True),
|
|
sa.Column("reviewed_at", sa.TIMESTAMP(), nullable=True),
|
|
sa.Column("source_title", sa.VARCHAR(length=500), nullable=True),
|
|
sa.Column("object_title", sa.VARCHAR(length=500), nullable=True),
|
|
sa.Column("auto_assigned", sa.Boolean(), nullable=False),
|
|
)
|
|
|
|
|
|
def test_insert_record_accepts_json_payloads_for_feedback_center_backfill() -> None:
|
|
migration = _load_migration_module()
|
|
metadata = sa.MetaData()
|
|
table = _feedback_center_records_table(metadata)
|
|
engine = sa.create_engine("sqlite:///:memory:")
|
|
metadata.create_all(engine)
|
|
|
|
payload = {
|
|
"tenant_id": 1,
|
|
"project_id": None,
|
|
"source_type": "qa_feedback",
|
|
"source_id": 118,
|
|
"object_type": "qa_record",
|
|
"object_id": 230,
|
|
"field_name": "qa_feedback",
|
|
"before_value": {"answer": "stub answer"},
|
|
"after_value": {"rating": "positive", "helpfulness": "fully"},
|
|
"ai_snapshot": {"question": "what happened", "answer": "stub answer"},
|
|
"ai_confidence": None,
|
|
"reason_candidates": [
|
|
{
|
|
"code": "answer_incorrect",
|
|
"label": "答案不准确",
|
|
"description": "AI 输出与业务事实不一致",
|
|
}
|
|
],
|
|
"reason_code": "answer_incorrect",
|
|
"reason_text": None,
|
|
"review_status": "reviewed",
|
|
"reviewed_by": 2,
|
|
"reviewed_at": None,
|
|
"source_title": "what happened",
|
|
"object_title": "what happened",
|
|
"auto_assigned": False,
|
|
}
|
|
|
|
with engine.begin() as conn:
|
|
migration._insert_record(conn, payload)
|
|
row = conn.execute(
|
|
sa.select(
|
|
table.c.before_value,
|
|
table.c.after_value,
|
|
table.c.ai_snapshot,
|
|
table.c.reason_candidates,
|
|
)
|
|
).mappings().one()
|
|
|
|
assert row["before_value"] == payload["before_value"]
|
|
assert row["after_value"] == payload["after_value"]
|
|
assert row["ai_snapshot"] == payload["ai_snapshot"]
|
|
assert row["reason_candidates"] == payload["reason_candidates"]
|