12 "github.com/stretchr/testify/require"
14 waAdv "go.mau.fi/whatsmeow/proto/waAdv"
15 "go.mau.fi/whatsmeow/store"
16 "go.mau.fi/whatsmeow/store/sqlstore"
17 "go.mau.fi/whatsmeow/types"
18 waLog "go.mau.fi/whatsmeow/util/log"
22 // whatsappTestNumber is the phone number of the own account in the
23 // tests. It is a number for fiction.
24 whatsappTestNumber = "15550001111"
26 // whatsappTestLID is the LID of the own account in the tests.
27 whatsappTestLID = "987654321012345"
29 // whatsappTestCode is the pairing code of the fake.
30 whatsappTestCode = "ABCD-EFGH"
33 // WhatsAppFake is a fake WhatsApp client. It keeps the event handler that the
34 // daemon gives it, and Emit gives events to the handler, as the library does.
35 // Each connection sends to connects, and each pairing sends its channel to
36 // pairings, so that the test gives the items of the pairing. A connection
37 // fails with connectErr, and a logout with logoutErr.
38 type WhatsAppFake struct {
39 connects chan struct{}
40 pairings chan chan whatsmeow.QRChannelItem
43 handle func(event any) bool
50 // ConnectContext tells the test that the daemon connected.
51 func (f *WhatsAppFake) ConnectContext(ctx context.Context) error {
52 f.connects <- struct{}{}
55 defer f.mutex.Unlock()
60 // Disconnect does nothing.
61 func (f *WhatsAppFake) Disconnect() {
64 // GetQRChannel gives a new channel of pairing items to the test.
65 func (f *WhatsAppFake) GetQRChannel(ctx context.Context) (<-chan whatsmeow.QRChannelItem, error) {
66 items := make(chan whatsmeow.QRChannelItem, 8)
72 // PairPhone keeps phone and returns whatsappTestCode.
73 func (f *WhatsAppFake) PairPhone(ctx context.Context, phone string, showPushNotification bool, clientType whatsmeow.PairClientType, clientDisplayName string) (string, error) {
75 defer f.mutex.Unlock()
77 f.phones = append(f.phones, phone)
79 return whatsappTestCode, nil
82 // Logout counts the logouts and returns logoutErr.
83 func (f *WhatsAppFake) Logout(ctx context.Context) error {
85 defer f.mutex.Unlock()
92 // Phones returns the phone numbers of the pairings.
93 func (f *WhatsAppFake) Phones() []string {
95 defer f.mutex.Unlock()
97 return append([]string{}, f.phones...)
100 // Logouts returns the number of logouts.
101 func (f *WhatsAppFake) Logouts() int {
103 defer f.mutex.Unlock()
108 // Emit gives event to the handler of the daemon and returns its answer.
109 func (f *WhatsAppFake) Emit(event any) bool {
117 // newClient keeps handle and returns the fake as the client of device.
118 func (f *WhatsAppFake) newClient(device *store.Device, handle func(event any) bool) WhatsAppClient {
120 defer f.mutex.Unlock()
127 // whatsappStartTest opens the WhatsApp of the data directory dir with a fake
128 // client and the test clock, and closes it at the end of the test. The daemon
129 // log goes to the returned buffer.
130 func whatsappStartTest(t *testing.T, dir string, clock *TestClock) (*WhatsApp, *WhatsAppFake, *SyncBuffer) {
133 fake := &WhatsAppFake{
134 connects: make(chan struct{}, 16),
135 pairings: make(chan chan whatsmeow.QRChannelItem, 16),
138 logs := &SyncBuffer{}
139 whatsapp, err := openWhatsApp(t.Context(), dir, clock.Now, log.New(logs, "", 0), fake.newClient)
140 require.NoError(t, err)
143 require.NoError(t, whatsapp.Close())
146 return whatsapp, fake, logs
149 // whatsappTestSession writes a session database with mode 0600 to the data
150 // directory dir. When linked is true, it holds a device linked to
151 // whatsappTestNumber and whatsappTestLID, and else no device.
152 func whatsappTestSession(t *testing.T, dir string, linked bool) {
155 path := filepath.Join(dir, whatsappDirName, whatsappSessionFileName)
156 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o700))
157 require.NoError(t, os.WriteFile(path, nil, 0o600))
159 address, err := whatsappSessionAddress(path)
160 require.NoError(t, err)
162 container, err := sqlstore.New(t.Context(), "sqlite", address, waLog.Noop)
163 require.NoError(t, err)
166 device := container.NewDevice()
167 jid := types.NewADJID(whatsappTestNumber, 0, 1)
169 device.LID = types.NewADJID(whatsappTestLID, 0, 1)
170 device.LID.Server = types.HiddenUserServer
171 device.Account = &waAdv.ADVSignedDeviceIdentity{
173 AccountSignature: make([]byte, 64),
174 AccountSignatureKey: make([]byte, 32),
175 DeviceSignature: make([]byte, 64),
177 require.NoError(t, device.Save(t.Context()))
180 require.NoError(t, container.Close())
183 // whatsappDiscardLog is a logger for a WhatsApp whose log a test does not
185 func whatsappDiscardLog() *log.Logger {
186 return log.New(io.Discard, "", 0)