When the session holds a linked device, the daemon connects at its
start. If the first connection fails, it tries again after 2 seconds,
then doubles the wait up to 5 minutes. The inbox and commands stay
-active during each wait. An unlink, logout, or pair command stops these
-retries. After a network failure of an open connection, the library
-connects again by itself. When a disconnection lasts 10 minutes, all
-clients get one notice, and one more when the connection comes back.
-When WhatsApp ends the connection for good, for example because another
-program uses the same session, all clients get a notice at once. When
-the user removes the device in the phone, the daemon deletes the session
-and tells all clients one time.
+active during each wait. An unlink or a logout stops these retries.
+After a network failure of an open connection, the library connects
+again by itself. When a disconnection lasts 10 minutes, all clients get
+one notice, and one more when the connection comes back. When WhatsApp
+ends the connection for good, for example because another program uses
+the same session, all clients get a notice at once. When the user
+removes the device in the phone, the daemon deletes the session and
+tells all clients one time.
***********
Schedules
// failed first connection starts the same outage notice as a lost connection. A
// timer in the select retries it with a doubled wait from 2 seconds to 5
// minutes, without a retry goroutine or a wait that blocks commands or the
-// inbox. A connection, unlink, logout, pair command, or ctx end stops retries.
-// The library owns retries after a connection opens. Run returns the error of a
-// write of the log or of the state file, which stops the daemon, because then
-// messages would be lost.
+// inbox. A connection, unlink, logout, or ctx end stops retries. The library
+// owns retries after a connection opens. Run returns the error of a write of
+// the log or of the state file, which stops the daemon, because then messages
+// would be lost.
func (w *WhatsApp) Run(ctx context.Context, l *TurnLoop, notify func(Event)) error {
w.mutex.Lock()
w.notify = notify
return err
}
case request := <-w.requests:
- stopRetry()
cancel()
pairing.Wait()
cancel = func() {}
switch {
case request.unlink:
+ stopRetry()
err = w.unlink(ctx, l, request.answer)
if err != nil {
return err
})
}
+func TestWhatsAppLinkedPairRequestKeepsFirstConnectionRetries(t *testing.T) {
+ synctest.Test(t, func(t *testing.T) {
+ test := whatsappRetryTest(t)
+ test.fake.connectErr = errors.New("network unavailable")
+ test.start(t)
+ synctest.Wait()
+
+ answers := make(chan string, 1)
+ test.whatsapp.Command("pair 15550001111", func(text string) {
+ answers <- text
+ })
+ require.Equal(t, "WhatsApp is linked already. Run /whatsapp unlink first.", <-answers)
+ synctest.Wait()
+ test.fake.mutex.Lock()
+ test.fake.connectErr = nil
+ test.fake.mutex.Unlock()
+ time.Sleep(whatsappRetryDelay)
+ synctest.Wait()
+ require.Len(t, test.fake.Connections(), 2)
+ require.True(t, test.fake.Emit(&events.Connected{}))
+ require.True(t, test.fake.Emit(&events.OfflineSyncCompleted{}))
+ require.Contains(t, test.whatsapp.Status(), "linked and connected since")
+ time.Sleep(whatsappRetryLimit)
+ synctest.Wait()
+ require.Len(t, test.fake.Connections(), 2)
+ require.Equal(t, "first connect failed, retrying: network unavailable\nconnected again after 2s\n", test.logs.String())
+ })
+}
+
func TestWhatsAppFirstConnectionRetriesStopForLinkChanges(t *testing.T) {
testCases := []struct {
name string
test.fake.Emit(&events.LoggedOut{})
},
},
- {
- name: "pair command",
- stop: func(test *WhatsAppRetryTest) {
- test.whatsapp.Command("pair 15550001111", func(text string) {})
- },
- },
{
name: "permanent disconnect",
stop: func(test *WhatsAppRetryTest) {