signal-sdk 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -17
- package/dist/MultiAccountManager.d.ts +149 -0
- package/dist/MultiAccountManager.js +320 -0
- package/dist/SignalBot.d.ts +1 -0
- package/dist/SignalBot.js +20 -2
- package/dist/SignalCli.d.ts +264 -15
- package/dist/SignalCli.js +652 -25
- package/dist/__tests__/MultiAccountManager.test.d.ts +4 -0
- package/dist/__tests__/MultiAccountManager.test.js +209 -0
- package/dist/__tests__/SignalBot.additional.test.js +31 -11
- package/dist/__tests__/SignalBot.test.js +5 -0
- package/dist/__tests__/SignalCli.advanced.test.d.ts +5 -0
- package/dist/__tests__/SignalCli.advanced.test.js +295 -0
- package/dist/__tests__/SignalCli.e2e.test.d.ts +5 -0
- package/dist/__tests__/SignalCli.e2e.test.js +240 -0
- package/dist/__tests__/SignalCli.integration.test.js +9 -2
- package/dist/__tests__/SignalCli.methods.test.js +171 -1
- package/dist/__tests__/SignalCli.parsing.test.d.ts +5 -0
- package/dist/__tests__/SignalCli.parsing.test.js +258 -0
- package/dist/__tests__/SignalCli.test.js +50 -13
- package/dist/config.d.ts +16 -1
- package/dist/config.js +6 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/interfaces.d.ts +73 -9
- package/dist/retry.js +25 -8
- package/package.json +1 -1
- package/scripts/install.js +1 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* End-to-end integration tests for complete workflows (Phase 6)
|
|
4
|
+
* Tests realistic scenarios combining multiple operations
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const SignalCli_1 = require("../SignalCli");
|
|
8
|
+
describe('SignalCli - E2E Workflow Tests (Phase 6)', () => {
|
|
9
|
+
let signal;
|
|
10
|
+
const mockSendResponse = {
|
|
11
|
+
timestamp: Date.now(),
|
|
12
|
+
results: [{ type: 'SUCCESS' }],
|
|
13
|
+
};
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
signal = new SignalCli_1.SignalCli('+33123456789');
|
|
16
|
+
});
|
|
17
|
+
describe('Complete Messaging Workflow', () => {
|
|
18
|
+
it('should send message with full options and handle response', async () => {
|
|
19
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
20
|
+
.mockResolvedValue(mockSendResponse);
|
|
21
|
+
const result = await signal.sendMessage('+33987654321', 'Hello!', {
|
|
22
|
+
attachments: ['/path/to/image.jpg'],
|
|
23
|
+
textStyles: [
|
|
24
|
+
{ start: 0, length: 5, style: 'BOLD' },
|
|
25
|
+
],
|
|
26
|
+
mentions: [
|
|
27
|
+
{ start: 0, length: 5, number: '+33987654321' },
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
expect(result.timestamp).toBeDefined();
|
|
31
|
+
expect(result.results).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
it('should send quoted message with attachments', async () => {
|
|
34
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
35
|
+
.mockResolvedValue(mockSendResponse);
|
|
36
|
+
const result = await signal.sendMessage('+33987654321', 'Reply', {
|
|
37
|
+
quote: {
|
|
38
|
+
timestamp: 1234567890,
|
|
39
|
+
author: '+33111111111',
|
|
40
|
+
text: 'Original message',
|
|
41
|
+
},
|
|
42
|
+
attachments: ['/path/to/doc.pdf'],
|
|
43
|
+
});
|
|
44
|
+
expect(result.timestamp).toBeDefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('Contact Management Workflow', () => {
|
|
48
|
+
it('should manage contacts through complete lifecycle', async () => {
|
|
49
|
+
const mockContacts = [
|
|
50
|
+
{
|
|
51
|
+
number: '+33111111111',
|
|
52
|
+
name: 'Alice',
|
|
53
|
+
blocked: false,
|
|
54
|
+
givenName: 'Alice',
|
|
55
|
+
familyName: 'Smith',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
number: '+33222222222',
|
|
59
|
+
name: 'Bob',
|
|
60
|
+
blocked: false,
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
64
|
+
.mockResolvedValueOnce(mockContacts)
|
|
65
|
+
.mockResolvedValueOnce(undefined)
|
|
66
|
+
.mockResolvedValueOnce([mockContacts[0]]);
|
|
67
|
+
const contacts = await signal.getContactsWithProfiles();
|
|
68
|
+
expect(contacts).toHaveLength(2);
|
|
69
|
+
expect(contacts[0].profileName).toBe('Alice Smith');
|
|
70
|
+
await signal.updateContact('+33222222222', 'Bob Johnson');
|
|
71
|
+
const updated = await signal.listContacts();
|
|
72
|
+
expect(signal.sendJsonRpcRequest).toHaveBeenCalledTimes(3);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe('Group Management Workflow', () => {
|
|
76
|
+
it('should manage group through complete lifecycle', async () => {
|
|
77
|
+
const mockGroup = {
|
|
78
|
+
groupId: 'group123==',
|
|
79
|
+
name: 'Test Group',
|
|
80
|
+
isMember: true,
|
|
81
|
+
isBlocked: false,
|
|
82
|
+
messageExpirationTime: 0,
|
|
83
|
+
members: [
|
|
84
|
+
{ number: '+33111111111' },
|
|
85
|
+
{ number: '+33222222222' },
|
|
86
|
+
],
|
|
87
|
+
pendingMembers: [],
|
|
88
|
+
requestingMembers: [],
|
|
89
|
+
admins: [{ number: '+33111111111' }],
|
|
90
|
+
banned: [],
|
|
91
|
+
permissionAddMember: 'EVERY_MEMBER',
|
|
92
|
+
permissionEditDetails: 'EVERY_MEMBER',
|
|
93
|
+
permissionSendMessage: 'EVERY_MEMBER',
|
|
94
|
+
groupInviteLink: 'https://signal.group/test',
|
|
95
|
+
};
|
|
96
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
97
|
+
.mockResolvedValueOnce([mockGroup])
|
|
98
|
+
.mockResolvedValueOnce(undefined)
|
|
99
|
+
.mockResolvedValueOnce(mockSendResponse)
|
|
100
|
+
.mockResolvedValueOnce([
|
|
101
|
+
{
|
|
102
|
+
...mockGroup,
|
|
103
|
+
members: [
|
|
104
|
+
...mockGroup.members,
|
|
105
|
+
{ number: '+33333333333' },
|
|
106
|
+
],
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
const groups = await signal.getGroupsWithDetails();
|
|
110
|
+
expect(groups).toHaveLength(1);
|
|
111
|
+
expect(groups[0].members).toHaveLength(2);
|
|
112
|
+
await signal.updateGroup('group123==', {
|
|
113
|
+
addMembers: ['+33333333333'],
|
|
114
|
+
});
|
|
115
|
+
await signal.sendMessage('group123==', 'Welcome new member!');
|
|
116
|
+
const updatedGroups = await signal.listGroups();
|
|
117
|
+
expect(updatedGroups[0].members).toHaveLength(3);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('Identity Management Workflow', () => {
|
|
121
|
+
it('should verify safety number before sending', async () => {
|
|
122
|
+
const mockSafetyNumber = '12345 67890 12345 67890 12345 67890';
|
|
123
|
+
const mockIdentities = [{
|
|
124
|
+
safetyNumber: mockSafetyNumber,
|
|
125
|
+
number: '+33987654321',
|
|
126
|
+
}];
|
|
127
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
128
|
+
.mockResolvedValueOnce(mockIdentities) // listIdentities
|
|
129
|
+
.mockResolvedValueOnce(mockIdentities) // listIdentities again for verify
|
|
130
|
+
.mockResolvedValueOnce(undefined) // trust
|
|
131
|
+
.mockResolvedValueOnce(mockSendResponse); // send
|
|
132
|
+
const safetyNumberResult = await signal.getSafetyNumber('+33987654321');
|
|
133
|
+
expect(safetyNumberResult).toBe(mockSafetyNumber);
|
|
134
|
+
await signal.verifySafetyNumber('+33987654321', mockSafetyNumber);
|
|
135
|
+
await signal.sendMessage('+33987654321', 'Secure message');
|
|
136
|
+
expect(signal.sendJsonRpcRequest).toHaveBeenCalledTimes(4);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('Username Management Workflow', () => {
|
|
140
|
+
it('should manage username lifecycle', async () => {
|
|
141
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
142
|
+
.mockResolvedValueOnce({ username: 'alice.01' })
|
|
143
|
+
.mockResolvedValueOnce(mockSendResponse)
|
|
144
|
+
.mockResolvedValueOnce(undefined);
|
|
145
|
+
const result = await signal.setUsername('alice.01');
|
|
146
|
+
expect(result.username).toBe('alice.01');
|
|
147
|
+
await signal.sendMessage('+33987654321', 'You can find me at @alice.01');
|
|
148
|
+
await signal.deleteUsername();
|
|
149
|
+
expect(signal.sendJsonRpcRequest).toHaveBeenCalledTimes(3);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
describe('Receive and Process Messages Workflow', () => {
|
|
153
|
+
it('should receive and process incoming messages', async () => {
|
|
154
|
+
const mockMessages = [
|
|
155
|
+
{
|
|
156
|
+
envelope: {
|
|
157
|
+
source: '+33987654321',
|
|
158
|
+
timestamp: Date.now(),
|
|
159
|
+
dataMessage: {
|
|
160
|
+
message: 'Hello!',
|
|
161
|
+
timestamp: Date.now(),
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
envelope: {
|
|
167
|
+
source: '+33111111111',
|
|
168
|
+
timestamp: Date.now(),
|
|
169
|
+
dataMessage: {
|
|
170
|
+
message: 'Hi there!',
|
|
171
|
+
timestamp: Date.now(),
|
|
172
|
+
groupInfo: {
|
|
173
|
+
groupId: 'group123==',
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
180
|
+
.mockResolvedValueOnce(mockMessages)
|
|
181
|
+
.mockResolvedValueOnce(mockSendResponse)
|
|
182
|
+
.mockResolvedValueOnce(mockSendResponse);
|
|
183
|
+
const messages = await signal.receive();
|
|
184
|
+
expect(messages).toHaveLength(2);
|
|
185
|
+
// Only send messages if the source/groupId fields exist
|
|
186
|
+
const msg0 = messages[0];
|
|
187
|
+
const msg1 = messages[1];
|
|
188
|
+
if (msg0.envelope && msg0.envelope.source) {
|
|
189
|
+
await signal.sendMessage(msg0.envelope.source, 'Hello back!');
|
|
190
|
+
}
|
|
191
|
+
if (msg1.envelope && msg1.envelope.dataMessage &&
|
|
192
|
+
msg1.envelope.dataMessage.groupInfo &&
|
|
193
|
+
msg1.envelope.dataMessage.groupInfo.groupId) {
|
|
194
|
+
await signal.sendMessage(msg1.envelope.dataMessage.groupInfo.groupId, 'Hi everyone!');
|
|
195
|
+
}
|
|
196
|
+
// Verify at least receive was called (message sending depends on data structure)
|
|
197
|
+
expect(signal.sendJsonRpcRequest).toHaveBeenCalled();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
describe('Multi-Step Group Operations', () => {
|
|
201
|
+
it('should create group, add members, and send announcement', async () => {
|
|
202
|
+
const mockGroupId = 'newgroup123==';
|
|
203
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
204
|
+
.mockResolvedValueOnce({ groupId: mockGroupId })
|
|
205
|
+
.mockResolvedValueOnce(undefined)
|
|
206
|
+
.mockResolvedValueOnce(undefined)
|
|
207
|
+
.mockResolvedValueOnce(mockSendResponse);
|
|
208
|
+
const createResult = await signal.updateGroup(mockGroupId, {
|
|
209
|
+
name: 'New Team',
|
|
210
|
+
});
|
|
211
|
+
await signal.updateGroup(mockGroupId, {
|
|
212
|
+
addMembers: ['+33222222222', '+33333333333'],
|
|
213
|
+
});
|
|
214
|
+
await signal.updateGroup(mockGroupId, {
|
|
215
|
+
description: 'Our awesome team workspace',
|
|
216
|
+
});
|
|
217
|
+
await signal.sendMessage(mockGroupId, 'Welcome to the team! 🎉');
|
|
218
|
+
expect(signal.sendJsonRpcRequest).toHaveBeenCalledTimes(4);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
describe('Error Handling in Workflows', () => {
|
|
222
|
+
it('should handle errors gracefully in message sending workflow', async () => {
|
|
223
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
224
|
+
.mockRejectedValueOnce(new Error('Network error'))
|
|
225
|
+
.mockResolvedValueOnce(mockSendResponse);
|
|
226
|
+
await expect(signal.sendMessage('+33987654321', 'Test')).rejects.toThrow('Network error');
|
|
227
|
+
const result = await signal.sendMessage('+33987654321', 'Test');
|
|
228
|
+
expect(result.timestamp).toBeDefined();
|
|
229
|
+
});
|
|
230
|
+
it('should handle non-existent group in workflow', async () => {
|
|
231
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
232
|
+
.mockResolvedValue([]);
|
|
233
|
+
const groups = await signal.getGroupsWithDetails();
|
|
234
|
+
expect(groups).toHaveLength(0);
|
|
235
|
+
signal.sendJsonRpcRequest = jest.fn()
|
|
236
|
+
.mockRejectedValue(new Error('Group not found'));
|
|
237
|
+
await expect(signal.sendMessage('nonexistent==', 'Test')).rejects.toThrow('Group not found');
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
@@ -34,7 +34,12 @@ describe('SignalCli Integration Tests', () => {
|
|
|
34
34
|
afterEach(() => {
|
|
35
35
|
if (signalCli) {
|
|
36
36
|
signalCli.disconnect();
|
|
37
|
+
// Clean up all listeners
|
|
38
|
+
signalCli.removeAllListeners();
|
|
37
39
|
}
|
|
40
|
+
// Clear all timers and mocks
|
|
41
|
+
jest.clearAllTimers();
|
|
42
|
+
jest.clearAllMocks();
|
|
38
43
|
});
|
|
39
44
|
describe('Connection Lifecycle', () => {
|
|
40
45
|
it('should handle multiple connect/disconnect cycles', async () => {
|
|
@@ -62,11 +67,13 @@ describe('SignalCli Integration Tests', () => {
|
|
|
62
67
|
}
|
|
63
68
|
});
|
|
64
69
|
const shutdownPromise = signalCli.gracefulShutdown();
|
|
65
|
-
// Simulate timeout and force kill
|
|
66
|
-
setTimeout(() => {
|
|
70
|
+
// Simulate timeout and force kill with unref to prevent hanging
|
|
71
|
+
const timer = setTimeout(() => {
|
|
67
72
|
if (closeCallback)
|
|
68
73
|
closeCallback(0);
|
|
69
74
|
}, 100);
|
|
75
|
+
if (timer.unref)
|
|
76
|
+
timer.unref();
|
|
70
77
|
await expect(shutdownPromise).resolves.toBeUndefined();
|
|
71
78
|
}, 10000); // Increase timeout to 10 seconds
|
|
72
79
|
it('should handle stderr data with different log levels', async () => {
|
|
@@ -71,7 +71,7 @@ describe('SignalCli Methods Tests', () => {
|
|
|
71
71
|
it('should send view-once message', async () => {
|
|
72
72
|
await signalCli.sendMessage('+1234567890', 'View once', { isViewOnce: true });
|
|
73
73
|
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('send', expect.objectContaining({
|
|
74
|
-
|
|
74
|
+
viewOnce: true
|
|
75
75
|
}));
|
|
76
76
|
});
|
|
77
77
|
it('should send reaction', async () => {
|
|
@@ -401,6 +401,90 @@ describe('SignalCli Methods Tests', () => {
|
|
|
401
401
|
}));
|
|
402
402
|
});
|
|
403
403
|
});
|
|
404
|
+
describe('Update Account Methods', () => {
|
|
405
|
+
it('should update account with device name', async () => {
|
|
406
|
+
sendJsonRpcRequestSpy.mockResolvedValue({ username: 'user.123' });
|
|
407
|
+
const result = await signalCli.updateAccount({ deviceName: 'New Device' });
|
|
408
|
+
expect(result.success).toBe(true);
|
|
409
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('updateAccount', expect.objectContaining({
|
|
410
|
+
deviceName: 'New Device'
|
|
411
|
+
}));
|
|
412
|
+
});
|
|
413
|
+
it('should update account with username', async () => {
|
|
414
|
+
sendJsonRpcRequestSpy.mockResolvedValue({ username: 'newuser.456', usernameLink: 'link' });
|
|
415
|
+
const result = await signalCli.updateAccount({ username: 'newuser.456' });
|
|
416
|
+
expect(result.success).toBe(true);
|
|
417
|
+
expect(result.username).toBe('newuser.456');
|
|
418
|
+
});
|
|
419
|
+
it('should delete username', async () => {
|
|
420
|
+
sendJsonRpcRequestSpy.mockResolvedValue({});
|
|
421
|
+
await signalCli.updateAccount({ deleteUsername: true });
|
|
422
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('updateAccount', expect.objectContaining({
|
|
423
|
+
deleteUsername: true
|
|
424
|
+
}));
|
|
425
|
+
});
|
|
426
|
+
it('should update privacy settings', async () => {
|
|
427
|
+
sendJsonRpcRequestSpy.mockResolvedValue({});
|
|
428
|
+
await signalCli.updateAccount({
|
|
429
|
+
unrestrictedUnidentifiedSender: true,
|
|
430
|
+
discoverableByNumber: false,
|
|
431
|
+
numberSharing: false
|
|
432
|
+
});
|
|
433
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('updateAccount', expect.objectContaining({
|
|
434
|
+
unrestrictedUnidentifiedSender: true,
|
|
435
|
+
discoverableByNumber: false,
|
|
436
|
+
numberSharing: false
|
|
437
|
+
}));
|
|
438
|
+
});
|
|
439
|
+
it('should handle update account error', async () => {
|
|
440
|
+
sendJsonRpcRequestSpy.mockRejectedValue(new Error('Update failed'));
|
|
441
|
+
const result = await signalCli.updateAccount({ deviceName: 'Device' });
|
|
442
|
+
expect(result.success).toBe(false);
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
describe('Device Management Methods (v0.13.23+)', () => {
|
|
446
|
+
it('should list devices', async () => {
|
|
447
|
+
const mockDevices = [
|
|
448
|
+
{ id: 1, name: 'Primary', lastSeen: Date.now() },
|
|
449
|
+
{ id: 2, name: 'Tablet', lastSeen: Date.now() }
|
|
450
|
+
];
|
|
451
|
+
sendJsonRpcRequestSpy.mockResolvedValue(mockDevices);
|
|
452
|
+
const result = await signalCli.listDevices();
|
|
453
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('listDevices', {
|
|
454
|
+
account: '+1234567890'
|
|
455
|
+
});
|
|
456
|
+
expect(result).toEqual(mockDevices);
|
|
457
|
+
});
|
|
458
|
+
it('should update device name', async () => {
|
|
459
|
+
sendJsonRpcRequestSpy.mockResolvedValue(undefined);
|
|
460
|
+
await signalCli.updateDevice({
|
|
461
|
+
deviceId: 3,
|
|
462
|
+
deviceName: 'My Laptop'
|
|
463
|
+
});
|
|
464
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('updateDevice', {
|
|
465
|
+
account: '+1234567890',
|
|
466
|
+
deviceId: 3,
|
|
467
|
+
deviceName: 'My Laptop'
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
it('should validate device ID', async () => {
|
|
471
|
+
await expect(signalCli.updateDevice({
|
|
472
|
+
deviceId: 0,
|
|
473
|
+
deviceName: 'Invalid'
|
|
474
|
+
})).rejects.toThrow();
|
|
475
|
+
await expect(signalCli.updateDevice({
|
|
476
|
+
deviceId: -5,
|
|
477
|
+
deviceName: 'Invalid'
|
|
478
|
+
})).rejects.toThrow();
|
|
479
|
+
});
|
|
480
|
+
it('should validate device name is not too long', async () => {
|
|
481
|
+
const longName = 'x'.repeat(300);
|
|
482
|
+
await expect(signalCli.updateDevice({
|
|
483
|
+
deviceId: 2,
|
|
484
|
+
deviceName: longName
|
|
485
|
+
})).rejects.toThrow();
|
|
486
|
+
});
|
|
487
|
+
});
|
|
404
488
|
describe('Update Account Methods', () => {
|
|
405
489
|
it('should update account with device name', async () => {
|
|
406
490
|
sendJsonRpcRequestSpy.mockResolvedValue({ username: 'user.123' });
|
|
@@ -467,4 +551,90 @@ describe('SignalCli Methods Tests', () => {
|
|
|
467
551
|
}));
|
|
468
552
|
});
|
|
469
553
|
});
|
|
554
|
+
describe('Phone Number Change Methods', () => {
|
|
555
|
+
it('should start change number with SMS', async () => {
|
|
556
|
+
await signalCli.startChangeNumber('+33612345678');
|
|
557
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('startChangeNumber', {
|
|
558
|
+
account: '+1234567890',
|
|
559
|
+
number: '+33612345678',
|
|
560
|
+
voice: false
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
it('should start change number with voice verification', async () => {
|
|
564
|
+
await signalCli.startChangeNumber('+33612345678', true);
|
|
565
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('startChangeNumber', {
|
|
566
|
+
account: '+1234567890',
|
|
567
|
+
number: '+33612345678',
|
|
568
|
+
voice: true
|
|
569
|
+
});
|
|
570
|
+
});
|
|
571
|
+
it('should start change number with captcha', async () => {
|
|
572
|
+
const captcha = 'captcha_token_12345';
|
|
573
|
+
await signalCli.startChangeNumber('+33612345678', false, captcha);
|
|
574
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('startChangeNumber', {
|
|
575
|
+
account: '+1234567890',
|
|
576
|
+
number: '+33612345678',
|
|
577
|
+
voice: false,
|
|
578
|
+
captcha
|
|
579
|
+
});
|
|
580
|
+
});
|
|
581
|
+
it('should validate phone number in startChangeNumber', async () => {
|
|
582
|
+
await expect(signalCli.startChangeNumber('invalid')).rejects.toThrow();
|
|
583
|
+
});
|
|
584
|
+
it('should finish change number without PIN', async () => {
|
|
585
|
+
await signalCli.finishChangeNumber('+33612345678', '123456');
|
|
586
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('finishChangeNumber', {
|
|
587
|
+
account: '+1234567890',
|
|
588
|
+
number: '+33612345678',
|
|
589
|
+
verificationCode: '123456'
|
|
590
|
+
});
|
|
591
|
+
});
|
|
592
|
+
it('should finish change number with PIN', async () => {
|
|
593
|
+
await signalCli.finishChangeNumber('+33612345678', '123456', '1234');
|
|
594
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('finishChangeNumber', {
|
|
595
|
+
account: '+1234567890',
|
|
596
|
+
number: '+33612345678',
|
|
597
|
+
verificationCode: '123456',
|
|
598
|
+
pin: '1234'
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
it('should validate phone number in finishChangeNumber', async () => {
|
|
602
|
+
await expect(signalCli.finishChangeNumber('invalid', '123456')).rejects.toThrow();
|
|
603
|
+
});
|
|
604
|
+
it('should require verification code in finishChangeNumber', async () => {
|
|
605
|
+
await expect(signalCli.finishChangeNumber('+33612345678', '')).rejects.toThrow('Verification code is required');
|
|
606
|
+
});
|
|
607
|
+
});
|
|
608
|
+
describe('Payment Notification Methods', () => {
|
|
609
|
+
it('should send payment notification', async () => {
|
|
610
|
+
const receipt = 'base64EncodedReceipt';
|
|
611
|
+
const note = 'Thanks for dinner!';
|
|
612
|
+
const recipient = '+1111111111';
|
|
613
|
+
sendJsonRpcRequestSpy.mockResolvedValue({ timestamp: 1234567890 });
|
|
614
|
+
const result = await signalCli.sendPaymentNotification(recipient, { receipt, note });
|
|
615
|
+
expect(result.timestamp).toBe(1234567890);
|
|
616
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPaymentNotification', {
|
|
617
|
+
account: '+1234567890',
|
|
618
|
+
recipient,
|
|
619
|
+
receipt,
|
|
620
|
+
note
|
|
621
|
+
});
|
|
622
|
+
});
|
|
623
|
+
it('should send payment notification without note', async () => {
|
|
624
|
+
const receipt = 'base64EncodedReceipt';
|
|
625
|
+
const recipient = '+1111111111';
|
|
626
|
+
await signalCli.sendPaymentNotification(recipient, { receipt });
|
|
627
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPaymentNotification', {
|
|
628
|
+
account: '+1234567890',
|
|
629
|
+
recipient,
|
|
630
|
+
receipt
|
|
631
|
+
});
|
|
632
|
+
});
|
|
633
|
+
it('should validate recipient in sendPaymentNotification', async () => {
|
|
634
|
+
await expect(signalCli.sendPaymentNotification('invalid', { receipt: 'test' })).rejects.toThrow();
|
|
635
|
+
});
|
|
636
|
+
it('should require receipt in sendPaymentNotification', async () => {
|
|
637
|
+
await expect(signalCli.sendPaymentNotification('+1111111111', { receipt: '' })).rejects.toThrow('Payment receipt is required');
|
|
638
|
+
});
|
|
639
|
+
});
|
|
470
640
|
});
|