13 "github.com/stretchr/testify/require"
15 waAdv "go.mau.fi/whatsmeow/proto/waAdv"
16 "go.mau.fi/whatsmeow/proto/waE2E"
17 "go.mau.fi/whatsmeow/store"
18 "go.mau.fi/whatsmeow/store/sqlstore"
19 "go.mau.fi/whatsmeow/types"
20 "go.mau.fi/whatsmeow/types/events"
21 waLog "go.mau.fi/whatsmeow/util/log"
25 // whatsappTestNumber is the phone number of the own account in the
26 // tests. It is a number for fiction.
27 whatsappTestNumber = "15550001111"
29 // whatsappTestLID is the LID of the own account in the tests.
30 whatsappTestLID = "987654321012345"
32 // whatsappTestCode is the pairing code of the fake.
33 whatsappTestCode = "ABCD-EFGH"
37 // whatsappTestOwn is the own account in the tests.
38 whatsappTestOwn = types.NewJID(whatsappTestNumber, types.DefaultUserServer)
40 // whatsappTestAna is a contact in the tests.
41 whatsappTestAna = types.NewJID("15550002222", types.DefaultUserServer)
43 // whatsappTestBruno is a member of the test group.
44 whatsappTestBruno = types.NewJID("15550003333", types.DefaultUserServer)
46 // whatsappTestFamily is the test group.
47 whatsappTestFamily = types.NewJID("120363000000000001", types.GroupServer)
49 // whatsappTestChat is the private chat with whatsappTestAna.
50 whatsappTestChat = WhatsAppChat{
51 ID: whatsappTestAna.String(),
55 // whatsappTestGroup is the chat of whatsappTestFamily.
56 whatsappTestGroup = WhatsAppChat{
57 ID: whatsappTestFamily.String(),
63 // WhatsAppFake is a fake WhatsApp client. It keeps the event handler that the
64 // daemon gives it, and Emit gives events to the handler, as the library does.
65 // Each connection sends to connects, and each pairing sends its channel to
66 // pairings, so that the test gives the items of the pairing. A connection fails
67 // with connectErr, and a logout with logoutErr. groups are the groups of the
69 type WhatsAppFake struct {
70 connects chan struct{}
71 pairings chan chan whatsmeow.QRChannelItem
74 handle func(event any) bool
77 groups []*types.GroupInfo
82 // ConnectContext tells the test that the daemon connected.
83 func (f *WhatsAppFake) ConnectContext(ctx context.Context) error {
84 f.connects <- struct{}{}
87 defer f.mutex.Unlock()
92 // Disconnect does nothing.
93 func (f *WhatsAppFake) Disconnect() {
96 // GetQRChannel gives a new channel of pairing items to the test.
97 func (f *WhatsAppFake) GetQRChannel(ctx context.Context) (<-chan whatsmeow.QRChannelItem, error) {
98 items := make(chan whatsmeow.QRChannelItem, 8)
104 // PairPhone keeps phone and returns whatsappTestCode.
105 func (f *WhatsAppFake) PairPhone(ctx context.Context, phone string, showPushNotification bool, clientType whatsmeow.PairClientType, clientDisplayName string) (string, error) {
107 defer f.mutex.Unlock()
109 f.phones = append(f.phones, phone)
111 return whatsappTestCode, nil
114 // Logout counts the logouts and returns logoutErr.
115 func (f *WhatsAppFake) Logout(ctx context.Context) error {
117 defer f.mutex.Unlock()
124 // GetJoinedGroups returns groups.
125 func (f *WhatsAppFake) GetJoinedGroups(ctx context.Context) ([]*types.GroupInfo, error) {
127 defer f.mutex.Unlock()
132 // Phones returns the phone numbers of the pairings.
133 func (f *WhatsAppFake) Phones() []string {
135 defer f.mutex.Unlock()
137 return append([]string{}, f.phones...)
140 // Logouts returns the number of logouts.
141 func (f *WhatsAppFake) Logouts() int {
143 defer f.mutex.Unlock()
148 // Emit gives event to the handler of the daemon and returns its answer.
149 func (f *WhatsAppFake) Emit(event any) bool {
157 // newClient keeps handle and returns the fake as the client of device.
158 func (f *WhatsAppFake) newClient(device *store.Device, handle func(event any) bool) WhatsAppClient {
160 defer f.mutex.Unlock()
167 // whatsappStartTest opens the WhatsApp of the data directory dir with a fake
168 // client and the test clock, and closes it at the end of the test. The daemon
169 // log goes to the returned buffer.
170 func whatsappStartTest(t *testing.T, dir string, clock *TestClock) (*WhatsApp, *WhatsAppFake, *SyncBuffer) {
173 fake := &WhatsAppFake{
174 connects: make(chan struct{}, 16),
175 pairings: make(chan chan whatsmeow.QRChannelItem, 16),
178 logs := &SyncBuffer{}
179 whatsapp, err := openWhatsApp(t.Context(), dir, clock.Now, log.New(logs, "", 0), fake.newClient)
180 require.NoError(t, err)
183 require.NoError(t, whatsapp.Close())
186 return whatsapp, fake, logs
189 // whatsappTestSession writes a session database with mode 0600 to the data
190 // directory dir. When linked is true, it holds a device linked to
191 // whatsappTestNumber and whatsappTestLID, and else no device.
192 func whatsappTestSession(t *testing.T, dir string, linked bool) {
195 path := filepath.Join(dir, whatsappDirName, whatsappSessionFileName)
196 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o700))
197 require.NoError(t, os.WriteFile(path, nil, 0o600))
199 address, err := whatsappSessionAddress(path)
200 require.NoError(t, err)
202 container, err := sqlstore.New(t.Context(), "sqlite", address, waLog.Noop)
203 require.NoError(t, err)
206 device := container.NewDevice()
207 jid := types.NewADJID(whatsappTestNumber, 0, 1)
209 device.LID = types.NewADJID(whatsappTestLID, 0, 1)
210 device.LID.Server = types.HiddenUserServer
211 device.Account = &waAdv.ADVSignedDeviceIdentity{
213 AccountSignature: make([]byte, 64),
214 AccountSignatureKey: make([]byte, 32),
215 DeviceSignature: make([]byte, 64),
217 require.NoError(t, device.Save(t.Context()))
220 require.NoError(t, container.Close())
223 // whatsappDiscardLog is a logger for a WhatsApp whose log a test does not read.
224 func whatsappDiscardLog() *log.Logger {
225 return log.New(io.Discard, "", 0)
228 // whatsappTestMessage returns the event of the text message id of sender in
229 // chat at the time at. A sender with whatsappTestNumber is the user.
230 func whatsappTestMessage(chat, sender types.JID, id, text string, at time.Time) *events.Message {
231 return &events.Message{
232 Info: types.MessageInfo{
233 MessageSource: types.MessageSource{
236 IsFromMe: sender.User == whatsappTestNumber,
237 IsGroup: chat.Server == types.GroupServer,
242 Message: &waE2E.Message{
243 Conversation: new(text),