12 "github.com/stretchr/testify/require"
14 "go.mau.fi/whatsmeow/proto/waE2E"
15 "go.mau.fi/whatsmeow/proto/waWeb"
16 "go.mau.fi/whatsmeow/types"
17 "go.mau.fi/whatsmeow/types/events"
20 func TestWhatsAppStatusFollowsTheConnection(t *testing.T) {
22 whatsappTestSession(t, dir, true)
23 clock := newTestClock()
24 whatsapp, fake, _ := whatsappStartTest(t, dir, clock)
25 daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
28 require.Equal(t, "WhatsApp is linked and not connected yet.", whatsapp.Status())
30 require.True(t, fake.Emit(&events.Connected{}))
31 require.Equal(t, "WhatsApp is linked and connected since 2026-01-01 12:00.", whatsapp.Status())
33 clock.now = clock.now.Add(20 * time.Minute)
34 require.True(t, fake.Emit(&events.Disconnected{}))
35 clock.now = clock.now.Add(time.Minute)
36 require.True(t, fake.Emit(&events.Disconnected{}))
37 require.Equal(t, "WhatsApp is linked and disconnected since 2026-01-01 12:20. The last connection started at 2026-01-01 12:00.", whatsapp.Status())
40 func TestWhatsAppStatusOfAFirstConnectionThatFailed(t *testing.T) {
42 whatsappTestSession(t, dir, true)
43 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
44 daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
47 require.True(t, fake.Emit(&events.Disconnected{}))
48 require.Equal(t, "WhatsApp is linked and disconnected since 2026-01-01 12:00.", whatsapp.Status())
51 func TestWhatsAppIsNotLinkedWithoutALinkedDevice(t *testing.T) {
52 testCases := []struct {
54 session func(t *testing.T, dir string)
57 name: "no session file",
58 session: func(t *testing.T, dir string) {
62 name: "a session of a pairing that did not end",
63 session: func(t *testing.T, dir string) {
64 whatsappTestSession(t, dir, false)
69 for _, tc := range testCases {
70 t.Run(tc.name, func(t *testing.T) {
73 whatsapp, _, _ := whatsappStartTest(t, dir, newTestClock())
74 require.Equal(t, "WhatsApp is not linked. Run /whatsapp pair PHONE to link it.", whatsapp.Status())
79 func TestWhatsAppRefusesASessionThatOthersCanRead(t *testing.T) {
81 whatsappTestSession(t, dir, true)
82 require.NoError(t, os.Chmod(filepath.Join(dir, whatsappDirName, whatsappSessionFileName), 0o644))
84 _, err := openWhatsApp(t.Context(), dir, time.Now, whatsappDiscardLog(), whatsappNewClient)
85 require.EqualError(t, err, "whatsapp/session.db holds the WhatsApp session, so its mode must be 0600")
88 func TestWhatsAppCommandsAnswerOnlyTheClientThatAsked(t *testing.T) {
89 server := newModelFake(t)
90 telegram := newTelegramFake(t)
91 bot, _ := telegramStartTestBot(t, telegram)
92 socket := daemonStartTestWithBot(t, server, bot)
93 asking := terminalStartTestClient(t, socket)
94 other := terminalStartTestClient(t, socket)
95 asking.WaitFor(t, "[connected to OneChat]")
96 other.WaitFor(t, "[connected to OneChat]")
98 asking.Type("/whatsapp status")
99 asking.WaitFor(t, "WhatsApp is not linked.")
100 asking.Type(" /whatsapp unknown ")
101 asking.WaitFor(t, whatsappUsage)
102 asking.Type("/whatsapp pair 0123")
103 asking.WaitFor(t, "PHONE must be the number of your WhatsApp account")
105 telegram.Add(TelegramUpdate{
107 Message: telegramOwnerMessage("/whatsapp status"),
110 telegramWaitForSent(t, telegram, "WhatsApp is not linked. Run /whatsapp pair PHONE to link it.")
111 require.Len(t, telegram.Sent(), 1)
112 require.Equal(t, "[connected to OneChat]\n", other.Output())
113 require.Zero(t, server.Calls())
116 func TestWhatsAppPairsWithACode(t *testing.T) {
118 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
119 server := newModelFake(t)
120 socket, memory := daemonStartTestWithWhatsApp(t, server, nil, whatsapp)
121 asking := terminalStartTestClient(t, socket)
122 other := terminalStartTestClient(t, socket)
123 asking.WaitFor(t, "[connected to OneChat]")
124 other.WaitFor(t, "[connected to OneChat]")
126 asking.Type("/whatsapp pair +1 555-000-1111")
127 items := <-fake.pairings
128 items <- whatsmeow.QRChannelItem{
129 Event: whatsmeow.QRChannelEventCode,
132 asking.WaitFor(t, whatsappPairSteps+"\n"+whatsappTestCode+"\n")
133 require.True(t, fake.Emit(&events.PairSuccess{
134 ID: types.NewADJID(whatsappTestNumber, 0, 1),
135 LID: types.NewADJID(whatsappTestLID, 0, 1),
137 items <- whatsmeow.QRChannelSuccess
138 asking.WaitFor(t, "WhatsApp is linked to +15550001111.")
140 require.Equal(t, []string{whatsappTestNumber}, fake.Phones())
141 require.Equal(t, "WhatsApp is linked and not connected yet.", whatsapp.Status())
142 require.Equal(t, "[connected to OneChat]\n", other.Output())
143 require.Zero(t, server.Calls())
144 require.Equal(t, []string{"WhatsApp is linked."}, whatsappTestLogTexts(t, memory))
146 info, err := os.Stat(filepath.Join(dir, whatsappDirName))
147 require.NoError(t, err)
148 require.Equal(t, os.FileMode(0o700), info.Mode().Perm())
150 info, err = os.Stat(filepath.Join(dir, whatsappDirName, whatsappSessionFileName))
151 require.NoError(t, err)
152 require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
155 func TestWhatsAppPairingCodeIsATelegramMessageAlone(t *testing.T) {
156 whatsapp, fake, _ := whatsappStartTest(t, t.TempDir(), newTestClock())
157 telegram := newTelegramFake(t)
158 bot, _ := telegramStartTestBot(t, telegram)
159 daemonStartTestWithWhatsApp(t, newModelFake(t), bot, whatsapp)
161 telegram.Add(TelegramUpdate{
163 Message: telegramOwnerMessage("/whatsapp pair 15550001111"),
166 items := <-fake.pairings
167 items <- whatsmeow.QRChannelItem{
168 Event: whatsmeow.QRChannelEventCode,
171 telegramWaitForSent(t, telegram, whatsappPairSteps, whatsappTestCode)
174 func TestWhatsAppPairingEnds(t *testing.T) {
175 testCases := []struct {
177 end func(items chan whatsmeow.QRChannelItem)
181 name: "the code expires",
182 end: func(items chan whatsmeow.QRChannelItem) {
183 items <- whatsmeow.QRChannelTimeout
185 answer: whatsappPairExpired,
188 name: "the library closes the pairing",
189 end: func(items chan whatsmeow.QRChannelItem) {
192 answer: whatsappPairExpired,
195 name: "the pairing fails",
196 end: func(items chan whatsmeow.QRChannelItem) {
197 items <- whatsmeow.QRChannelItem{
198 Event: whatsmeow.QRChannelEventError,
199 Error: errors.New("bad handshake"),
202 answer: "The pairing failed: bad handshake. Run /whatsapp pair PHONE again.",
206 for _, tc := range testCases {
207 t.Run(tc.name, func(t *testing.T) {
208 whatsapp, fake, _ := whatsappStartTest(t, t.TempDir(), newTestClock())
209 socket, _ := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
210 client := terminalStartTestClient(t, socket)
211 client.Type("/whatsapp pair 15550001111")
213 items := <-fake.pairings
214 items <- whatsmeow.QRChannelItem{
215 Event: whatsmeow.QRChannelEventCode,
218 client.WaitFor(t, whatsappTestCode)
220 client.WaitFor(t, tc.answer)
221 require.Equal(t, "WhatsApp is not linked. Run /whatsapp pair PHONE to link it.", whatsapp.Status())
226 func TestWhatsAppNewPairingEndsTheOneBefore(t *testing.T) {
227 whatsapp, fake, _ := whatsappStartTest(t, t.TempDir(), newTestClock())
228 socket, _ := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
229 client := terminalStartTestClient(t, socket)
231 client.Type("/whatsapp pair 15550001111")
233 client.Type("/whatsapp pair 15550002222")
234 items := <-fake.pairings
235 items <- whatsmeow.QRChannelItem{
236 Event: whatsmeow.QRChannelEventCode,
239 client.WaitFor(t, whatsappTestCode)
240 require.Equal(t, []string{"15550002222"}, fake.Phones())
241 require.NotContains(t, client.Output(), "did not start the pairing")
244 func TestWhatsAppPairWhenLinked(t *testing.T) {
246 whatsappTestSession(t, dir, true)
247 whatsapp, _, _ := whatsappStartTest(t, dir, newTestClock())
248 socket, _ := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
249 client := terminalStartTestClient(t, socket)
251 client.Type("/whatsapp pair 15550001111")
252 client.WaitFor(t, "WhatsApp is linked already. Run /whatsapp unlink first.")
255 func TestWhatsAppUnlink(t *testing.T) {
256 testCases := []struct {
262 name: "with a logout",
263 answer: "WhatsApp is unlinked.",
266 name: "with no logout",
267 logoutErr: errors.New("not connected"),
268 answer: "WhatsApp is unlinked here, but the phone did not get the logout. In WhatsApp on your phone, open Linked devices and remove the device.",
272 for _, tc := range testCases {
273 t.Run(tc.name, func(t *testing.T) {
275 whatsappTestSession(t, dir, true)
276 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
277 fake.logoutErr = tc.logoutErr
278 socket, memory := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
279 client := terminalStartTestClient(t, socket)
282 client.Type("/whatsapp unlink")
283 client.WaitFor(t, tc.answer)
284 require.Equal(t, 1, fake.Logouts())
285 require.NoFileExists(t, filepath.Join(dir, whatsappDirName, whatsappSessionFileName))
286 require.Equal(t, "WhatsApp is not linked. Run /whatsapp pair PHONE to link it.", whatsapp.Status())
287 require.Equal(t, []string{"WhatsApp is unlinked."}, whatsappTestLogTexts(t, memory))
289 client.Type("/whatsapp unlink")
290 client.WaitFor(t, "WhatsApp is not linked.\n")
295 func TestWhatsAppLogoutFromThePhone(t *testing.T) {
297 whatsappTestSession(t, dir, true)
298 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
299 telegram := newTelegramFake(t)
300 bot, _ := telegramStartTestBot(t, telegram)
301 socket, memory := daemonStartTestWithWhatsApp(t, newModelFake(t), bot, whatsapp)
302 client := terminalStartTestClient(t, socket)
303 client.WaitFor(t, "[connected to OneChat]")
306 require.True(t, fake.Emit(&events.LoggedOut{}))
307 notice := "WhatsApp was unlinked from the phone. Run /whatsapp pair PHONE to link it again."
308 client.WaitFor(t, notice)
309 telegramWaitForSent(t, telegram, notice)
310 require.NoFileExists(t, filepath.Join(dir, whatsappDirName, whatsappSessionFileName))
311 require.Equal(t, "WhatsApp is not linked. Run /whatsapp pair PHONE to link it.", whatsapp.Status())
312 require.Equal(t, []string{"WhatsApp was unlinked from the phone."}, whatsappTestLogTexts(t, memory))
315 func TestWhatsAppLongDisconnectionGivesOneNotice(t *testing.T) {
317 whatsappTestSession(t, dir, true)
318 clock := newTestClock()
319 whatsapp, fake, _ := whatsappStartTest(t, dir, clock)
320 whatsapp.noticeDelay = 50 * time.Millisecond
321 socket, _ := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
322 client := terminalStartTestClient(t, socket)
323 client.WaitFor(t, "[connected to OneChat]")
326 require.True(t, fake.Emit(&events.Connected{}))
327 require.True(t, fake.Emit(&events.Disconnected{}))
328 require.True(t, fake.Emit(&events.Connected{}))
329 time.Sleep(100 * time.Millisecond)
330 require.Equal(t, "[connected to OneChat]\n", client.Output())
332 clock.now = clock.now.Add(time.Hour)
333 require.True(t, fake.Emit(&events.Disconnected{}))
334 require.True(t, fake.Emit(&events.Disconnected{}))
335 client.WaitFor(t, "WhatsApp is disconnected since 2026-01-01 13:00. OneChat tries again.\n")
336 require.True(t, fake.Emit(&events.Disconnected{}))
337 time.Sleep(100 * time.Millisecond)
339 require.True(t, fake.Emit(&events.Connected{}))
340 client.WaitFor(t, "WhatsApp is connected again.\n")
341 require.Equal(t, 1, strings.Count(client.Output(), "WhatsApp is disconnected since"))
344 func TestWhatsAppDisconnectionWithNoRetryGivesANoticeAtOnce(t *testing.T) {
346 whatsappTestSession(t, dir, true)
347 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
348 socket, _ := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
349 client := terminalStartTestClient(t, socket)
350 client.WaitFor(t, "[connected to OneChat]")
353 require.True(t, fake.Emit(&events.StreamReplaced{}))
354 client.WaitFor(t, "WhatsApp is disconnected and does not connect again by itself: stream replaced. Restart the daemon to try again.")
357 func TestWhatsAppLogsNoOwnNumber(t *testing.T) {
359 whatsappTestSession(t, dir, true)
360 whatsapp, fake, logs := whatsappStartTest(t, dir, newTestClock())
361 fake.connectErr = errors.New("no route for " + whatsappTestNumber + "@s.whatsapp.net and " + whatsappTestLID + "@lid")
362 daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
365 require.Eventually(t, func() bool {
366 return logs.String() != ""
367 }, 10*time.Second, time.Millisecond)
368 require.Equal(t, "connect: no route for OWN@s.whatsapp.net and OWN@lid\n", logs.String())
371 func TestWhatsAppPhone(t *testing.T) {
372 testCases := []struct {
378 name: "a number with a plus and dashes",
379 phone: "+1-555-000-1111",
383 name: "a number with no country code",
384 phone: "0115550001111",
388 name: "a short number",
393 name: "a long number",
394 phone: "1234567890123456",
398 name: "a number with letters",
399 phone: "1555ABC1111",
404 for _, tc := range testCases {
405 t.Run(tc.name, func(t *testing.T) {
406 _, valid := whatsappPhone(tc.phone)
407 require.Equal(t, tc.valid, valid)
412 func TestWhatsAppCommandArgs(t *testing.T) {
413 testCases := []struct {
420 name: "a command with its arguments",
421 line: " /whatsapp pair +1 555 ",
426 name: "the command alone",
432 name: "a word that starts with the command",
433 line: "/whatsappstatus",
438 name: "a message that names the command",
439 line: "what does /whatsapp status do?",
445 for _, tc := range testCases {
446 t.Run(tc.name, func(t *testing.T) {
447 args, found := whatsappCommandArgs(tc.line)
448 require.Equal(t, tc.args, args)
449 require.Equal(t, tc.found, found)
454 // whatsappTestLogTexts returns the texts of the messages in the log of memory,
455 // after the log holds at least one message.
456 func whatsappTestLogTexts(t *testing.T, memory *Memory) []string {
459 require.Eventually(t, func() bool {
460 return memory.Len() > 0
461 }, 10*time.Second, time.Millisecond)
464 for id := range memory.Len() {
465 message, err := memory.Message(id)
466 require.NoError(t, err)
468 texts = append(texts, message.Text)
474 func TestWhatsAppPrivateBatchStartsATurnWithAnEmptyReply(t *testing.T) {
475 whatsapp, fake := whatsappStartTestLinked(t)
476 require.NoError(t, whatsapp.device.Contacts.PutContactName(t.Context(), whatsappTestAna, "Ana", "Ana Souza"))
478 server := newModelFake(t)
479 server.reply = func(w http.ResponseWriter, call ModelFakeCall) {
480 modelWriteEvents(w, modelCompletedEvent([]any{modelMessageItem("")}, 100, 0, 5))
483 telegram := newTelegramFake(t)
484 bot, _ := telegramStartTestBot(t, telegram)
485 socket, memory := daemonStartTestWithWhatsApp(t, server, bot, whatsapp)
486 client := terminalStartTestClient(t, socket)
487 client.WaitFor(t, "[connected to OneChat]")
488 whatsappTestConnect(t, fake)
490 at := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
491 require.True(t, fake.Emit(whatsappTestMessage(whatsappTestAna, whatsappTestAna, "m1", "Are you at home?", at)))
492 require.True(t, fake.Emit(whatsappTestMessage(whatsappTestAna, whatsappTestOwn, "m2", "Yes", at)))
494 text := "[WhatsApp] Ana Souza, private chat 15550002222@s.whatsapp.net, Thursday 2026-01-01\n12:00 Ana Souza: Are you at home?\n12:00 You: Yes\n" + whatsappBatchEmptyReply
495 client.WaitFor(t, text+"\n[working]\n[done]\n")
496 require.Equal(t, 1, server.Calls())
497 require.Contains(t, string(server.Call(0).raw), "Are you at home?")
498 require.Equal(t, []string{text}, whatsappTestLogTexts(t, memory))
499 require.Empty(t, telegram.Sent())
500 require.Eventually(t, func() bool {
501 return len(whatsapp.batches.Closed()) == 0
502 }, 10*time.Second, time.Millisecond)
505 func TestWhatsAppGroupTurns(t *testing.T) {
506 testCases := []struct {
508 message func(event *events.Message)
514 message: func(event *events.Message) {
516 line: "12:00 Bruno Lima: Who brings the cake?",
520 message: func(event *events.Message) {
521 event.Message = &waE2E.Message{
522 ExtendedTextMessage: &waE2E.ExtendedTextMessage{
523 Text: new("@" + whatsappTestLID + " can you bring the cake? Call " + whatsappTestNumber + "."),
524 ContextInfo: &waE2E.ContextInfo{
525 MentionedJID: []string{whatsappTestLID + "@lid"},
530 line: "12:00 Bruno Lima (mentions you): @you can you bring the cake? Call OWN.",
535 message: func(event *events.Message) {
536 event.Message = &waE2E.Message{
537 ExtendedTextMessage: &waE2E.ExtendedTextMessage{
538 Text: new("Who brings the cake?"),
539 ContextInfo: &waE2E.ContextInfo{
540 Participant: new(whatsappTestOwn.String()),
545 line: "12:00 Bruno Lima (replies to you): Who brings the cake?",
550 for _, tc := range testCases {
551 t.Run(tc.name, func(t *testing.T) {
552 whatsapp, fake := whatsappStartTestLinked(t)
553 fake.groups = []*types.GroupInfo{
555 JID: whatsappTestFamily,
556 GroupName: types.GroupName{
562 server := newModelFake(t)
563 socket, memory := daemonStartTestWithWhatsApp(t, server, nil, whatsapp)
564 client := terminalStartTestClient(t, socket)
565 client.WaitFor(t, "[connected to OneChat]")
566 whatsappTestConnect(t, fake)
568 event := whatsappTestMessage(whatsappTestFamily, whatsappTestBruno, "m1", "Who brings the cake?", time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC))
569 event.Info.PushName = "Bruno Lima"
571 require.True(t, fake.Emit(event))
573 text := "[WhatsApp] Family, group 120363000000000001@g.us, Thursday 2026-01-01\n" + tc.line
575 text += "\n" + whatsappBatchEmptyReply
576 client.WaitFor(t, "[done]")
577 require.Equal(t, 1, server.Calls())
579 client.WaitFor(t, text)
580 time.Sleep(100 * time.Millisecond)
581 require.Zero(t, server.Calls())
584 require.Equal(t, text, whatsappTestLogTexts(t, memory)[0])
589 func TestWhatsAppTakesOnlyText(t *testing.T) {
590 at := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC)
591 testCases := []struct {
593 change func(event *events.Message)
597 change: func(event *events.Message) {
603 change: func(event *events.Message) {
604 event.SourceWebMsg = &waWeb.WebMessageInfo{}
608 name: "a view-once message",
609 change: func(event *events.Message) {
610 event.IsViewOnce = true
614 name: "a disappearing message",
615 change: func(event *events.Message) {
616 event.IsEphemeral = true
620 name: "the chat of the user with itself",
621 change: func(event *events.Message) {
622 event.Info.Chat = whatsappTestOwn
626 name: "a status broadcast",
627 change: func(event *events.Message) {
628 event.Info.Chat = types.StatusBroadcastJID
632 name: "a photo with a caption",
633 change: func(event *events.Message) {
634 event.Message = &waE2E.Message{
635 ImageMessage: &waE2E.ImageMessage{
636 Caption: new("look"),
642 name: "a text of spaces",
643 change: func(event *events.Message) {
644 event.Message.Conversation = new(" ")
649 for _, tc := range testCases {
650 t.Run(tc.name, func(t *testing.T) {
651 whatsapp, _ := whatsappStartTestLinked(t)
652 event := whatsappTestMessage(whatsappTestAna, whatsappTestAna, "m1", "hi", at)
654 require.True(t, whatsapp.handle(event))
655 require.NoError(t, whatsapp.batches.CloseAll())
656 require.Empty(t, whatsapp.batches.Closed())
661 // whatsappStartTestLinked opens a WhatsApp with a linked device in a temporary
662 // directory, with a quiet time of 50 ms for its batches.
663 func whatsappStartTestLinked(t *testing.T) (*WhatsApp, *WhatsAppFake) {
667 whatsappTestSession(t, dir, true)
668 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
669 whatsapp.batches.quiet = 50 * time.Millisecond
671 return whatsapp, fake
674 func TestWhatsAppCatchupGivesOneTurn(t *testing.T) {
675 whatsapp, fake := whatsappStartTestLinked(t)
676 server := newModelFake(t)
677 socket, memory := daemonStartTestWithWhatsApp(t, server, nil, whatsapp)
678 client := terminalStartTestClient(t, socket)
679 client.WaitFor(t, "[connected to OneChat]")
682 require.True(t, fake.Emit(&events.Connected{}))
683 at := time.Date(2026, 1, 1, 8, 0, 0, 0, time.UTC)
684 for index, event := range []*events.Message{
685 whatsappTestMessage(whatsappTestAna, whatsappTestAna, "m1", "Good morning", at),
686 whatsappTestMessage(whatsappTestBruno, whatsappTestBruno, "m2", "Call me", at.Add(time.Hour)),
687 whatsappTestMessage(whatsappTestAna, whatsappTestAna, "m3", "Are you there?", at.Add(2*time.Hour)),
689 event.Info.PushName = []string{"Ana", "Bruno", "Ana"}[index]
690 require.True(t, fake.Emit(event))
693 time.Sleep(100 * time.Millisecond)
694 require.Zero(t, memory.Len())
696 require.True(t, fake.Emit(&events.OfflineSyncCompleted{}))
697 client.WaitFor(t, "[done]")
698 require.Equal(t, []string{
699 "[WhatsApp] Ana, private chat 15550002222@s.whatsapp.net, Thursday 2026-01-01\n08:00 Ana: Good morning\n" + whatsappBatchEmptyReply,
700 "[WhatsApp] Bruno, private chat 15550003333@s.whatsapp.net, Thursday 2026-01-01\n09:00 Bruno: Call me\n" + whatsappBatchEmptyReply,
701 "[WhatsApp] Ana, private chat 15550002222@s.whatsapp.net, Thursday 2026-01-01\n10:00 Ana: Are you there?\n" + whatsappBatchEmptyReply,
702 }, whatsappTestLogTexts(t, memory)[:3])
704 time.Sleep(100 * time.Millisecond)
705 require.Equal(t, 1, server.Calls())
706 for _, text := range []string{"Good morning", "Call me", "Are you there?"} {
707 require.Contains(t, string(server.Call(0).raw), text)
711 func TestWhatsAppCatchupEndsAfterItsLimit(t *testing.T) {
712 whatsapp, fake := whatsappStartTestLinked(t)
713 whatsapp.catchupLimit = 100 * time.Millisecond
714 socket, memory := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
715 client := terminalStartTestClient(t, socket)
716 client.WaitFor(t, "[connected to OneChat]")
719 require.True(t, fake.Emit(&events.Connected{}))
720 require.True(t, fake.Emit(whatsappTestMessage(whatsappTestAna, whatsappTestOwn, "m1", "On my way.", time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC))))
721 client.WaitFor(t, "12:00 You: On my way.")
722 require.Len(t, whatsappTestLogTexts(t, memory), 1)
725 func TestWhatsAppBatchesOfTheStateFileWaitForTheCatchup(t *testing.T) {
727 whatsappTestSession(t, dir, true)
728 before, _, _ := whatsappStartTest(t, dir, newTestClock())
729 _, err := before.batches.Add(whatsappTestChat, WhatsAppMessage{
732 Time: time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC),
733 Text: "Did you see my message?",
734 }, time.Now().Add(-time.Hour))
735 require.NoError(t, err)
737 whatsapp, fake, _ := whatsappStartTest(t, dir, newTestClock())
738 socket, memory := daemonStartTestWithWhatsApp(t, newModelFake(t), nil, whatsapp)
739 client := terminalStartTestClient(t, socket)
740 client.WaitFor(t, "[connected to OneChat]")
743 time.Sleep(100 * time.Millisecond)
744 require.Zero(t, memory.Len())
746 require.True(t, fake.Emit(&events.Connected{}))
747 require.True(t, fake.Emit(&events.OfflineSyncCompleted{}))
748 client.WaitFor(t, "12:00 Ana Souza: Did you see my message?")
749 client.WaitFor(t, "[done]")
752 // whatsappTestConnect waits for the connection of the daemon and gives it the
753 // events of a connection with an empty catch-up.
754 func whatsappTestConnect(t *testing.T, fake *WhatsAppFake) {
758 require.True(t, fake.Emit(&events.Connected{}))
759 require.True(t, fake.Emit(&events.OfflineSyncCompleted{}))