File size: 7,369 Bytes
46252cd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { describe, expect, it } from 'vitest';
import { OpenWAClient } from '../src';
import { MockTransport } from './helpers';

function client(t: MockTransport): OpenWAClient {
  return new OpenWAClient({ baseUrl: 'http://x', apiKey: 'k', fetch: t.asFetch() });
}

describe('MessagesResource — exact paths', () => {
  it('sendText posts to /messages/send-text (NOT /messages/text)', async () => {
    const t = new MockTransport().on('POST', /send-text$/, { body: { messageId: 'm1', timestamp: 1 } });
    await client(t).messages.sendText('s1', { chatId: 'a@c.us', text: 'hi' });
    expect(t.lastCall!.url).toBe('http://x/api/sessions/s1/messages/send-text');
    expect(t.lastCall!.body).toEqual({ chatId: 'a@c.us', text: 'hi' });
  });

  it('sendImage posts to /messages/send-image', async () => {
    const t = new MockTransport().on('POST', /send-image$/, { body: { messageId: 'm', timestamp: 2 } });
    await client(t).messages.sendImage('s', { chatId: 'a@c.us', url: 'http://img' });
    expect(t.lastCall!.url).toContain('/messages/send-image');
  });

  it('sendVideo / sendAudio / sendDocument / sendSticker use correct segments', async () => {
    const cases: Array<[string, (c: OpenWAClient) => Promise<unknown>]> = [
      ['send-video', c => c.messages.sendVideo('s', { chatId: 'a@c.us', url: 'u' })],
      ['send-audio', c => c.messages.sendAudio('s', { chatId: 'a@c.us', url: 'u' })],
      ['send-document', c => c.messages.sendDocument('s', { chatId: 'a@c.us', filename: 'f.pdf' })],
      ['send-sticker', c => c.messages.sendSticker('s', { chatId: 'a@c.us', url: 'u' })],
    ];
    for (const [segment, fn] of cases) {
      const t = new MockTransport().on('POST', new RegExp(`${segment}$`), { body: { messageId: 'm', timestamp: 3 } });
      await fn(client(t));
      expect(t.lastCall!.url).toContain(`/messages/${segment}`);
    }
  });

  it('sendLocation / sendContact / sendTemplate', async () => {
    const t = new MockTransport()
      .on('POST', /send-location/, { body: { messageId: 'm', timestamp: 1 } })
      .on('POST', /send-contact/, { body: { messageId: 'm', timestamp: 1 } })
      .on('POST', /send-template/, { body: { messageId: 'm', timestamp: 1 } });
    const c = client(t);
    await c.messages.sendLocation('s', { chatId: 'a@c.us', latitude: -6.2, longitude: 106.8 });
    expect(t.lastCall!.url).toContain('/messages/send-location');
    await c.messages.sendContact('s', { chatId: 'a@c.us', contactName: 'A', contactNumber: '628' });
    expect(t.lastCall!.url).toContain('/messages/send-contact');
    await c.messages.sendTemplate('s', { chatId: 'a@c.us', templateId: 't1', vars: { name: 'Sam' } });
    expect(t.lastCall!.url).toContain('/messages/send-template');
    // Server DTO field is `vars` (NOT `variables`) — body must forward verbatim.
    expect(t.lastCall!.body).toEqual({ chatId: 'a@c.us', templateId: 't1', vars: { name: 'Sam' } });
  });

  it('sendTemplate accepts templateName as the alternative to templateId', async () => {
    const t = new MockTransport().on('POST', /send-template/, { body: { messageId: 'm', timestamp: 1 } });
    await client(t).messages.sendTemplate('s', { chatId: 'a@c.us', templateName: 'welcome', vars: { x: '1' } });
    expect(t.lastCall!.body).toEqual({ chatId: 'a@c.us', templateName: 'welcome', vars: { x: '1' } });
  });

  it('list returns the {messages,total} wrapper the server actually sends', async () => {
    const t = new MockTransport().on('GET', /\/messages$/, {
      body: {
        messages: [
          { id: '1', sessionId: 's', chatId: 'a@c.us', from: 'a@c.us', to: 's', type: 'text', direction: 'incoming', status: 'delivered', createdAt: '' },
        ],
        total: 1,
      },
    });
    const res = await client(t).messages.list('s', { chatId: 'a@c.us' });
    expect(res.total).toBe(1);
    expect(res.messages).toHaveLength(1);
  });

  it('reply / forward / react / delete', async () => {
    const t = new MockTransport()
      .on('POST', /\/messages\/reply$/, { body: { messageId: 'm', timestamp: 1 } })
      .on('POST', /\/messages\/forward$/, { body: { messageId: 'm', timestamp: 1 } })
      .on('POST', /\/messages\/react$/, { body: { success: true } })
      .on('POST', /\/messages\/delete$/, { body: { success: true } });
    const c = client(t);
    await c.messages.reply('s', { chatId: 'a@c.us', quotedMessageId: 'q', text: 'r' });
    expect(t.lastCall!.url).toContain('/messages/reply');
    await c.messages.forward('s', { fromChatId: 'a@c.us', toChatId: 'b@c.us', messageId: 'm' });
    expect(t.lastCall!.url).toContain('/messages/forward');
    await c.messages.react('s', { chatId: 'a@c.us', messageId: 'm', emoji: '👍' });
    expect(t.lastCall!.url).toContain('/messages/react');
    await c.messages.delete('s', { chatId: 'a@c.us', messageId: 'm' });
    expect(t.lastCall!.url).toContain('/messages/delete');
  });

  it('editMessage posts to /messages/edit and returns the MessageResponse shape', async () => {
    const t = new MockTransport().on('POST', /\/messages\/edit$/, { body: { messageId: 'm1', timestamp: 4 } });
    const res = await client(t).messages.editMessage('s', { chatId: 'a@c.us', messageId: 'm1', body: 'edited' });
    expect(t.lastCall!.url).toBe('http://x/api/sessions/s/messages/edit');
    expect(t.lastCall!.body).toEqual({ chatId: 'a@c.us', messageId: 'm1', body: 'edited' });
    expect(res.messageId).toBe('m1');
    expect(res.timestamp).toBe(4);
  });

  it('history puts chatId in the path', async () => {
    const t = new MockTransport().on('GET', /\/messages\/[^/]+\/history$/, { body: [] });
    await client(t).messages.history('s', 'a@c.us', { limit: 5 });
    expect(t.lastCall!.url).toContain('/messages/a@c.us/history');
    expect(t.lastCall!.url).toContain('limit=5');
  });

  it('reactions puts chatId and messageId in the path', async () => {
    const t = new MockTransport().on('GET', /\/reactions$/, { body: [] });
    await client(t).messages.reactions('s', 'a@c.us', 'm1');
    expect(t.lastCall!.url).toContain('/a@c.us/m1/reactions');
  });

  it('sendBulk + batchStatus + cancelBatch', async () => {
    const t = new MockTransport()
      .on('POST', /send-bulk$/, {
        body: { batchId: 'b', status: 'queued', totalMessages: 1, estimatedCompletionTime: 't', statusUrl: '/u' },
      })
      .on('GET', /\/batch\/b$/, {
        body: {
          batchId: 'b',
          status: 'done',
          progress: { total: 1, sent: 1, failed: 0, pending: 0, cancelled: 0 },
          results: [],
          startedAt: 's',
          completedAt: 'c',
        },
      })
      .on('POST', /\/batch\/b\/cancel$/, {
        body: { batchId: 'b', status: 'cancelled', progress: { total: 1, sent: 0, failed: 0, pending: 0, cancelled: 1 } },
      });
    const c = client(t);
    await c.messages.sendBulk('s', { messages: [{ chatId: 'a@c.us', type: 'text', content: { text: 'x' } }] });
    expect(t.lastCall!.url).toContain('/messages/send-bulk');
    const status = await c.messages.batchStatus('s', 'b');
    expect(status.progress?.sent).toBe(1);
    expect(t.lastCall!.url).toContain('/messages/batch/b');
    const cancelled = await c.messages.cancelBatch('s', 'b');
    expect(cancelled.status).toBe('cancelled');
    expect(t.lastCall!.url).toContain('/messages/batch/b/cancel');
    expect(t.lastCall!.method).toBe('POST');
  });
});