whFalsa / tests /test_conversation_service.py
codeBOKER's picture
Allow drivers to publish trips in their own style; add reply_instruction for no-match results; improve privacy prompts
c925062
Raw
History Blame Contribute Delete
7.48 kB
import pytest
from app.models.domain import WhatsAppInboundMessage
from app.services.conversation_service import ConversationService
from tests.conftest import FakeAI, FakeEmbeddings, FakeRepository, FakeWhatsApp
@pytest.mark.asyncio
async def test_conversation_stores_messages_uses_last_four_context_and_sends_reply(settings):
repository = FakeRepository()
customer = await repository.upsert_customer(remote_jid="967700000001", name="Customer")
for index in range(6):
await repository.create_message(
customer_id=customer["id"],
sender_type="customer" if index % 2 == 0 else "assistant",
message=f"prior-{index}",
)
ai = FakeAI(reply="Found trips for you")
whatsapp = FakeWhatsApp()
service = ConversationService(
repository=repository,
embeddings=FakeEmbeddings(),
whatsapp=whatsapp,
ai=ai,
settings=settings,
)
reply = await service.handle_inbound_message(
WhatsAppInboundMessage(
message_id="wamid.new",
remoteJid="967700000001",
text="Aden to Mukalla tomorrow",
profile_name="Customer",
)
)
assert reply == "Found trips for you"
assert repository.messages[-2]["message"] == "Aden to Mukalla tomorrow"
assert repository.messages[-1]["sender_type"] == "assistant"
ai_messages = ai.calls[0]["messages"]
assert ai_messages[0]["role"] == "system"
assert "FALZH" in ai_messages[0]["content"]
context_contents = [message["content"] for message in ai_messages[1:]]
assert context_contents[-1] == "Aden to Mukalla tomorrow"
assert len(context_contents) <= 9
tool_names = {tool["function"]["name"] for tool in ai.calls[0]["tools"]}
assert tool_names == {
"about_falzh",
"create_driver_account",
"switch_to_driver",
"switch_to_passenger",
}
@pytest.mark.asyncio
async def test_conversation_skips_duplicate_whatsapp_message(settings):
repository = FakeRepository()
customer = await repository.upsert_customer(remote_jid="967700000001")
await repository.create_message(
customer_id=customer["id"],
sender_type="customer",
message="already handled",
whatsapp_message_id="wamid.duplicate",
)
ai = FakeAI()
whatsapp = FakeWhatsApp()
service = ConversationService(
repository=repository,
embeddings=FakeEmbeddings(),
whatsapp=whatsapp,
ai=ai,
settings=settings,
)
result = await service.handle_inbound_message(
WhatsAppInboundMessage(
message_id="wamid.duplicate",
remoteJid="967700000001",
text="same message",
)
)
assert result is None
assert ai.calls == []
assert whatsapp.sent == []
@pytest.mark.asyncio
async def test_conversation_uses_passenger_tools_when_user_mode_is_passenger(settings):
repository = FakeRepository()
customer = await repository.upsert_customer(remote_jid="967700000001", name="Customer")
customer["user_mode"] = "passenger"
ai = FakeAI(reply="Passenger reply")
service = ConversationService(
repository=repository,
embeddings=FakeEmbeddings(),
whatsapp=FakeWhatsApp(),
ai=ai,
settings=settings,
)
await service.handle_inbound_message(
WhatsAppInboundMessage(
message_id="wamid.passenger",
remoteJid="967700000001",
text="Aden to Mukalla tomorrow",
profile_name="Customer",
)
)
tool_names = {tool["function"]["name"] for tool in ai.calls[0]["tools"]}
assert tool_names == {
"about_falzh",
"search_trips",
"select_trip",
"create_driver_account",
"switch_to_driver",
}
assert "travel booking assistant" in ai.calls[0]["messages"][0]["content"]
@pytest.mark.asyncio
async def test_returning_driver_gets_welcome_and_upgraded_to_driver(settings):
"""An unregistered driver saved from group trips who later DMs the bot
should get a personalized welcome system note, use driver tools, and
have their user_mode upgraded to 'driver'."""
repository = FakeRepository()
# Step 1: Simulate group-trip extraction creating an unregistered driver
customer = await repository.upsert_customer(
remote_jid=None,
name="فهد",
phone_number="967712345678",
registered=False,
)
await repository.create_driver(customer_id=customer["id"])
assert customer["user_mode"] is None
assert customer["remoteJid"] is None
# Step 2: Same driver sends a DM
ai = FakeAI(reply="مرحباً فهد! نتابع رحلاتك...")
whatsapp = FakeWhatsApp()
service = ConversationService(
repository=repository,
embeddings=FakeEmbeddings(),
whatsapp=whatsapp,
ai=ai,
settings=settings,
)
reply = await service.handle_inbound_message(
WhatsAppInboundMessage(
message_id="wamid.returning",
remoteJid="967712345678",
text="مرحبا، أبي أضيف رحلة",
phone_number="967712345678",
profile_name="فهد",
)
)
assert reply == "مرحباً فهد! نتابع رحلاتك..."
# Should use driver tools (not new_user tools)
tool_names = {tool["function"]["name"] for tool in ai.calls[0]["tools"]}
assert tool_names == {
"about_falzh",
"check_driver_info",
"check_driver_trips",
"add_driver_car",
"add_trip_by_driver",
"initiate_trip_action",
"update_trip_field",
"switch_to_passenger",
}
# System note should be appended with returning-driver context
ai_messages = ai.calls[0]["messages"]
system_contents = [m["content"] for m in ai_messages if m["role"] == "system"]
assert any("فهد" in msg for msg in system_contents)
assert any("group" in msg.lower() or "مجموعات" in msg for msg in system_contents)
assert any("own preferred style/format" in msg for msg in system_contents)
assert any("phone numbers will be removed" in msg for msg in system_contents)
# user_mode should be upgraded to 'driver'
assert customer["user_mode"] == "driver"
# WhatsApp message was sent
assert len(whatsapp.sent) == 1
@pytest.mark.asyncio
async def test_conversation_uses_driver_tools_when_user_mode_is_driver(settings):
repository = FakeRepository()
customer = await repository.upsert_customer(remote_jid="967700000010", name="Ali")
customer["user_mode"] = "driver"
ai = FakeAI(reply="Driver reply")
whatsapp = FakeWhatsApp()
service = ConversationService(
repository=repository,
embeddings=FakeEmbeddings(),
whatsapp=whatsapp,
ai=ai,
settings=settings,
)
await service.handle_inbound_message(
WhatsAppInboundMessage(
message_id="wamid.driver",
remoteJid="967700000010",
text="Check my trips",
profile_name="Ali",
)
)
tool_names = {tool["function"]["name"] for tool in ai.calls[0]["tools"]}
assert tool_names == {
"about_falzh",
"check_driver_info",
"check_driver_trips",
"add_driver_car",
"add_trip_by_driver",
"initiate_trip_action",
"update_trip_field",
"switch_to_passenger",
}