File size: 7,482 Bytes
102dd4f
 
 
 
 
 
 
 
 
 
d4f6fe7
102dd4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fcf2812
102dd4f
 
 
 
 
 
 
 
 
 
 
cf6d08c
fcf2812
 
 
102dd4f
 
cf6d08c
102dd4f
 
 
 
 
 
 
 
 
d4f6fe7
102dd4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fcf2812
102dd4f
 
 
 
 
 
 
 
 
 
 
 
d4f6fe7
102dd4f
 
 
 
 
 
 
 
 
 
 
 
 
fcf2812
102dd4f
 
 
 
 
 
 
cf6d08c
102dd4f
edb038d
102dd4f
 
 
 
dc8cb49
 
aa966e0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf6d08c
aa966e0
 
 
 
 
 
 
 
 
 
 
 
 
 
c925062
 
aa966e0
 
 
 
 
 
 
 
dc8cb49
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fcf2812
dc8cb49
 
 
 
 
 
 
cf6d08c
dc8cb49
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
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",
    }