feat(child): auto create parent-child conversations

This commit is contained in:
ChengCan
2026-04-30 00:58:07 +08:00
parent d21ff85807
commit 74a4819f82
6 changed files with 221 additions and 8 deletions

View File

@@ -60,6 +60,158 @@ def test_child_dao():
assert child["child_name"] == "Test Child"
def test_child_service_create_creates_parent_child_conversation():
"""Creating a child should also create a parent-child conversation."""
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from app.models import Base
from app.service.child import ChildService
engine = create_engine("sqlite:///:memory:", echo=False)
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
db = Session()
db.execute(text("INSERT INTO parents (openid, nickname, status) VALUES ('p_child_service', 'Parent A', 1)"))
db.commit()
user_id = int(db.execute(text("SELECT user_id FROM parents WHERE openid = 'p_child_service'")).scalar_one())
service = ChildService(db)
child = service.create(user_id, "Kid Service")
conversation = (
db.execute(
text(
"""
SELECT
conversation_type,
participant_a_type,
participant_a_id,
participant_b_type,
participant_b_id,
pair_key,
status,
last_seq,
message_count
FROM im_conversations
WHERE conversation_type = 2
AND pair_key = :pair_key
LIMIT 1
"""
),
{"pair_key": f"{child['child_id']}:{user_id}"},
)
.mappings()
.first()
)
assert conversation is not None
assert int(conversation["conversation_type"]) == 2
assert int(conversation["participant_a_type"]) == 2
assert str(conversation["participant_a_id"]) == str(child["child_id"])
assert int(conversation["participant_b_type"]) == 1
assert str(conversation["participant_b_id"]) == str(user_id)
assert int(conversation["status"]) == 1
assert int(conversation["last_seq"]) == 0
assert int(conversation["message_count"]) == 0
def test_parent_child_message_reuses_precreated_conversation():
"""Parent message creation should reuse the conversation created with the child."""
from sqlalchemy import create_engine, text
from sqlalchemy.orm import sessionmaker
from app.models import Base
from app.schemas.im import ParentChildMessageCreateRequest
from app.service.child import ChildService
from app.service.im import create_parent_child_message
engine = create_engine("sqlite:///:memory:", echo=False)
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
db = Session()
db.execute(text("INSERT INTO parents (openid, nickname, status) VALUES ('p_msg_reuse', 'Parent B', 1)"))
db.commit()
user_id = int(db.execute(text("SELECT user_id FROM parents WHERE openid = 'p_msg_reuse'")).scalar_one())
service = ChildService(db)
child = service.create(user_id, "Kid Reuse")
child_id = int(child["child_id"])
precreated_conversation_id = int(
db.execute(
text(
"""
SELECT id
FROM im_conversations
WHERE conversation_type = 2
AND pair_key = :pair_key
LIMIT 1
"""
),
{"pair_key": f"{child_id}:{user_id}"},
).scalar_one()
)
payload = ParentChildMessageCreateRequest(
content_type=1,
content_text="hello child",
client_msg_id="parent-msg-reuse-001",
)
result = create_parent_child_message(
db,
parent_user_id=user_id,
child_id=child_id,
payload=payload,
)
assert result.idempotent is False
assert result.conversation_id == precreated_conversation_id
duplicate = create_parent_child_message(
db,
parent_user_id=user_id,
child_id=child_id,
payload=payload,
)
assert duplicate.idempotent is True
assert duplicate.conversation_id == precreated_conversation_id
conversation = (
db.execute(
text(
"""
SELECT last_seq, message_count, last_message_preview
FROM im_conversations
WHERE id = :conversation_id
"""
),
{"conversation_id": precreated_conversation_id},
)
.mappings()
.first()
)
assert conversation is not None
assert int(conversation["last_seq"]) == 1
assert int(conversation["message_count"]) == 1
assert conversation["last_message_preview"] == "hello child"
conversation_count = int(
db.execute(
text(
"""
SELECT COUNT(*)
FROM im_conversations
WHERE conversation_type = 2
AND pair_key = :pair_key
"""
),
{"pair_key": f"{child_id}:{user_id}"},
).scalar_one()
)
assert conversation_count == 1
def test_binding_dao():
"""Test BindingDAO."""
from sqlalchemy import create_engine
@@ -285,6 +437,8 @@ if __name__ == "__main__":
test_sqlite_connection()
test_parent_dao()
test_child_dao()
test_child_service_create_creates_parent_child_conversation()
test_parent_child_message_reuses_precreated_conversation()
test_binding_dao()
test_confirm_bind_upserts_parent_child_relation()
test_direct_bind_reactivates_parent_child_relation()