signal-sdk 0.0.9 → 0.1.0

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.
@@ -232,4 +232,248 @@ describe('SignalCli', () => {
232
232
  Object.defineProperty(process, 'platform', { value: originalPlatform });
233
233
  });
234
234
  });
235
+ // Test v0.1.0 features: Polls
236
+ describe('Poll Management', () => {
237
+ beforeEach(() => {
238
+ jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
239
+ });
240
+ it('should create a poll', async () => {
241
+ const mockResponse = { timestamp: 123456789 };
242
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
243
+ .mockResolvedValue(mockResponse);
244
+ const result = await signalCli.sendPollCreate({
245
+ recipients: ['+1234567890'],
246
+ question: 'What is your favorite color?',
247
+ options: ['Red', 'Blue', 'Green']
248
+ });
249
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPollCreate', {
250
+ account: '+1234567890',
251
+ recipients: ['+1234567890'],
252
+ question: 'What is your favorite color?',
253
+ options: ['Red', 'Blue', 'Green']
254
+ });
255
+ expect(result).toEqual(mockResponse);
256
+ });
257
+ it('should create a group poll', async () => {
258
+ const mockResponse = { timestamp: 123456789 };
259
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
260
+ .mockResolvedValue(mockResponse);
261
+ const result = await signalCli.sendPollCreate({
262
+ groupId: 'group-123',
263
+ question: 'Best meeting time?',
264
+ options: ['9 AM', '2 PM', '4 PM']
265
+ });
266
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPollCreate', {
267
+ account: '+1234567890',
268
+ groupId: 'group-123',
269
+ question: 'Best meeting time?',
270
+ options: ['9 AM', '2 PM', '4 PM']
271
+ });
272
+ expect(result).toEqual(mockResponse);
273
+ });
274
+ it('should vote on a poll', async () => {
275
+ const mockResponse = { timestamp: 123456790 };
276
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
277
+ .mockResolvedValue(mockResponse);
278
+ const result = await signalCli.sendPollVote('+1234567890', {
279
+ pollAuthor: '+9876543210',
280
+ pollTimestamp: 123456789,
281
+ optionIndexes: [0, 1]
282
+ });
283
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPollVote', {
284
+ account: '+1234567890',
285
+ recipient: '+1234567890',
286
+ pollAuthor: '+9876543210',
287
+ pollTimestamp: 123456789,
288
+ options: [0, 1]
289
+ });
290
+ expect(result).toEqual(mockResponse);
291
+ });
292
+ it('should terminate a poll', async () => {
293
+ const mockResponse = { timestamp: 123456791 };
294
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
295
+ .mockResolvedValue(mockResponse);
296
+ const result = await signalCli.sendPollTerminate('+1234567890', {
297
+ pollTimestamp: 123456789
298
+ });
299
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendPollTerminate', {
300
+ account: '+1234567890',
301
+ recipient: '+1234567890',
302
+ pollTimestamp: 123456789
303
+ });
304
+ expect(result).toEqual(mockResponse);
305
+ });
306
+ });
307
+ // Test v0.1.0 features: Attachment Retrieval
308
+ describe('Attachment Retrieval', () => {
309
+ beforeEach(() => {
310
+ jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
311
+ });
312
+ it('should get attachment by ID', async () => {
313
+ const mockBase64 = 'base64-encoded-data';
314
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
315
+ .mockResolvedValue(mockBase64);
316
+ const result = await signalCli.getAttachment({
317
+ id: 'attachment-123'
318
+ });
319
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getAttachment', {
320
+ account: '+1234567890',
321
+ id: 'attachment-123'
322
+ });
323
+ expect(result).toBe(mockBase64);
324
+ });
325
+ it('should get contact avatar', async () => {
326
+ const mockBase64 = 'base64-avatar-data';
327
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
328
+ .mockResolvedValue(mockBase64);
329
+ const result = await signalCli.getAvatar({
330
+ contact: '+9876543210'
331
+ });
332
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getAvatar', {
333
+ account: '+1234567890',
334
+ contact: '+9876543210'
335
+ });
336
+ expect(result).toBe(mockBase64);
337
+ });
338
+ it('should get profile avatar', async () => {
339
+ const mockBase64 = 'base64-profile-avatar';
340
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
341
+ .mockResolvedValue(mockBase64);
342
+ const result = await signalCli.getAvatar({
343
+ profile: '+1234567890'
344
+ });
345
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getAvatar', {
346
+ account: '+1234567890',
347
+ profile: '+1234567890'
348
+ });
349
+ expect(result).toBe(mockBase64);
350
+ });
351
+ it('should get group avatar', async () => {
352
+ const mockBase64 = 'base64-group-avatar';
353
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
354
+ .mockResolvedValue(mockBase64);
355
+ const result = await signalCli.getAvatar({
356
+ groupId: 'group-123'
357
+ });
358
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getAvatar', {
359
+ account: '+1234567890',
360
+ groupId: 'group-123'
361
+ });
362
+ expect(result).toBe(mockBase64);
363
+ });
364
+ it('should get sticker data', async () => {
365
+ const mockBase64 = 'base64-sticker-data';
366
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
367
+ .mockResolvedValue(mockBase64);
368
+ const result = await signalCli.getSticker({
369
+ packId: 'pack-123',
370
+ stickerId: 5
371
+ });
372
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('getSticker', {
373
+ account: '+1234567890',
374
+ packId: 'pack-123',
375
+ stickerId: 5
376
+ });
377
+ expect(result).toBe(mockBase64);
378
+ });
379
+ });
380
+ // Test v0.1.0 features: Account Management
381
+ describe('Account Management', () => {
382
+ beforeEach(() => {
383
+ jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
384
+ });
385
+ it('should update account settings', async () => {
386
+ const mockResponse = {
387
+ username: 'myusername',
388
+ usernameLink: 'https://signal.me/#myusername'
389
+ };
390
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
391
+ .mockResolvedValue(mockResponse);
392
+ const result = await signalCli.updateAccount({
393
+ deviceName: 'My Device',
394
+ username: 'myusername'
395
+ });
396
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('updateAccount', {
397
+ account: '+1234567890',
398
+ deviceName: 'My Device',
399
+ username: 'myusername'
400
+ });
401
+ expect(result.success).toBe(true);
402
+ expect(result.username).toBe('myusername');
403
+ });
404
+ it('should list accounts with details', async () => {
405
+ const mockResponse = {
406
+ accounts: [
407
+ { number: '+1234567890', name: 'Account 1', uuid: 'uuid-1' },
408
+ { number: '+9876543210', name: 'Account 2', uuid: 'uuid-2' }
409
+ ]
410
+ };
411
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
412
+ .mockResolvedValue(mockResponse);
413
+ const result = await signalCli.listAccountsDetailed();
414
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('listAccounts');
415
+ expect(result).toEqual(mockResponse.accounts);
416
+ });
417
+ });
418
+ // Test v0.1.0 features: Synchronization
419
+ describe('Synchronization', () => {
420
+ beforeEach(() => {
421
+ jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
422
+ });
423
+ it('should send contacts to linked devices', async () => {
424
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
425
+ .mockResolvedValue({});
426
+ await signalCli.sendContacts();
427
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('sendContacts', {
428
+ account: '+1234567890'
429
+ });
430
+ });
431
+ it('should list groups with detailed information', async () => {
432
+ const mockGroups = [
433
+ {
434
+ id: 'group-1',
435
+ name: 'Test Group 1',
436
+ members: ['+1111111111', '+2222222222'],
437
+ groupType: 'GROUP_V2'
438
+ },
439
+ {
440
+ id: 'group-2',
441
+ name: 'Test Group 2',
442
+ members: ['+3333333333'],
443
+ groupType: 'GROUP_V2'
444
+ }
445
+ ];
446
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
447
+ .mockResolvedValue(mockGroups);
448
+ const result = await signalCli.listGroupsDetailed({
449
+ detailed: true
450
+ });
451
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('listGroups', {
452
+ account: '+1234567890',
453
+ detailed: true
454
+ });
455
+ expect(result).toEqual(mockGroups);
456
+ });
457
+ it('should filter groups by ID', async () => {
458
+ const mockGroups = [
459
+ {
460
+ id: 'group-1',
461
+ name: 'Specific Group',
462
+ members: ['+1111111111']
463
+ }
464
+ ];
465
+ const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
466
+ .mockResolvedValue(mockGroups);
467
+ const result = await signalCli.listGroupsDetailed({
468
+ groupIds: ['group-1'],
469
+ detailed: true
470
+ });
471
+ expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('listGroups', {
472
+ account: '+1234567890',
473
+ detailed: true,
474
+ groupId: ['group-1']
475
+ });
476
+ expect(result).toEqual(mockGroups);
477
+ });
478
+ });
235
479
  });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Additional tests for config.ts
3
+ * Tests configuration validation and logger functionality
4
+ */
5
+ export {};
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ /**
3
+ * Additional tests for config.ts
4
+ * Tests configuration validation and logger functionality
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const config_1 = require("../config");
8
+ describe('Config Additional Tests', () => {
9
+ describe('validateConfig', () => {
10
+ test('should return default config when no user config provided', () => {
11
+ const config = (0, config_1.validateConfig)();
12
+ expect(config).toEqual(config_1.DEFAULT_CONFIG);
13
+ });
14
+ test('should merge user config with defaults', () => {
15
+ const userConfig = {
16
+ verbose: true,
17
+ maxRetries: 5
18
+ };
19
+ const config = (0, config_1.validateConfig)(userConfig);
20
+ expect(config.verbose).toBe(true);
21
+ expect(config.maxRetries).toBe(5);
22
+ expect(config.connectionTimeout).toBe(config_1.DEFAULT_CONFIG.connectionTimeout);
23
+ });
24
+ test('should throw error for negative connectionTimeout', () => {
25
+ expect(() => (0, config_1.validateConfig)({ connectionTimeout: -1 }))
26
+ .toThrow('connectionTimeout must be non-negative');
27
+ });
28
+ test('should throw error for negative requestTimeout', () => {
29
+ expect(() => (0, config_1.validateConfig)({ requestTimeout: -100 }))
30
+ .toThrow('requestTimeout must be non-negative');
31
+ });
32
+ test('should throw error for negative maxRetries', () => {
33
+ expect(() => (0, config_1.validateConfig)({ maxRetries: -5 }))
34
+ .toThrow('maxRetries must be non-negative');
35
+ });
36
+ test('should throw error for negative retryDelay', () => {
37
+ expect(() => (0, config_1.validateConfig)({ retryDelay: -1000 }))
38
+ .toThrow('retryDelay must be non-negative');
39
+ });
40
+ test('should throw error for maxConcurrentRequests less than 1', () => {
41
+ expect(() => (0, config_1.validateConfig)({ maxConcurrentRequests: 0 }))
42
+ .toThrow('maxConcurrentRequests must be at least 1');
43
+ expect(() => (0, config_1.validateConfig)({ maxConcurrentRequests: -1 }))
44
+ .toThrow('maxConcurrentRequests must be at least 1');
45
+ });
46
+ test('should throw error for negative minRequestInterval', () => {
47
+ expect(() => (0, config_1.validateConfig)({ minRequestInterval: -50 }))
48
+ .toThrow('minRequestInterval must be non-negative');
49
+ });
50
+ test('should accept all valid trustNewIdentities values', () => {
51
+ expect(() => (0, config_1.validateConfig)({ trustNewIdentities: 'on-first-use' }))
52
+ .not.toThrow();
53
+ expect(() => (0, config_1.validateConfig)({ trustNewIdentities: 'always' }))
54
+ .not.toThrow();
55
+ expect(() => (0, config_1.validateConfig)({ trustNewIdentities: 'never' }))
56
+ .not.toThrow();
57
+ });
58
+ test('should accept zero values for valid fields', () => {
59
+ const config = (0, config_1.validateConfig)({
60
+ connectionTimeout: 0,
61
+ requestTimeout: 0,
62
+ maxRetries: 0,
63
+ retryDelay: 0,
64
+ minRequestInterval: 0
65
+ });
66
+ expect(config.connectionTimeout).toBe(0);
67
+ expect(config.requestTimeout).toBe(0);
68
+ expect(config.maxRetries).toBe(0);
69
+ expect(config.retryDelay).toBe(0);
70
+ expect(config.minRequestInterval).toBe(0);
71
+ });
72
+ test('should handle all configuration options', () => {
73
+ const fullConfig = {
74
+ signalCliPath: '/custom/path/signal-cli',
75
+ account: '+1234567890',
76
+ connectionTimeout: 60000,
77
+ requestTimeout: 120000,
78
+ enableRetry: false,
79
+ maxRetries: 10,
80
+ retryDelay: 2000,
81
+ verbose: true,
82
+ logFile: '/var/log/signal.log',
83
+ maxConcurrentRequests: 10,
84
+ minRequestInterval: 200,
85
+ autoReconnect: false,
86
+ trustNewIdentities: 'never',
87
+ disableSendLog: true
88
+ };
89
+ const config = (0, config_1.validateConfig)(fullConfig);
90
+ expect(config).toMatchObject(fullConfig);
91
+ });
92
+ });
93
+ describe('Logger', () => {
94
+ let logger;
95
+ beforeEach(() => {
96
+ logger = new config_1.Logger({
97
+ level: 'debug',
98
+ enableConsole: true,
99
+ enableFile: false,
100
+ includeTimestamp: true,
101
+ includeLevel: true
102
+ });
103
+ });
104
+ test('should create logger with default config', () => {
105
+ const defaultLogger = new config_1.Logger();
106
+ expect(defaultLogger).toBeDefined();
107
+ });
108
+ test('should log debug messages', () => {
109
+ const consoleSpy = jest.spyOn(console, 'debug').mockImplementation();
110
+ logger.debug('Test debug message');
111
+ expect(consoleSpy).toHaveBeenCalled();
112
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Test debug message'));
113
+ consoleSpy.mockRestore();
114
+ });
115
+ test('should log info messages', () => {
116
+ const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
117
+ logger.info('Test info message');
118
+ expect(consoleSpy).toHaveBeenCalled();
119
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Test info message'));
120
+ consoleSpy.mockRestore();
121
+ });
122
+ test('should log warn messages', () => {
123
+ const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
124
+ logger.warn('Test warning message');
125
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('WARN'));
126
+ consoleSpy.mockRestore();
127
+ });
128
+ test('should log error messages', () => {
129
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation();
130
+ logger.error('Test error message');
131
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('ERROR'));
132
+ consoleSpy.mockRestore();
133
+ });
134
+ test('should include timestamp when configured', () => {
135
+ const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
136
+ logger.info('Timestamped message');
137
+ expect(consoleSpy).toHaveBeenCalled();
138
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringMatching(/\[\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z\]/));
139
+ consoleSpy.mockRestore();
140
+ });
141
+ test('should not log when console is disabled', () => {
142
+ const consoleLogger = new config_1.Logger({
143
+ level: 'info',
144
+ enableConsole: false,
145
+ enableFile: false,
146
+ includeTimestamp: true,
147
+ includeLevel: true
148
+ });
149
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
150
+ consoleLogger.info('Should not appear in console');
151
+ expect(consoleSpy).not.toHaveBeenCalled();
152
+ consoleSpy.mockRestore();
153
+ });
154
+ test('should respect log level', () => {
155
+ const infoLogger = new config_1.Logger({
156
+ level: 'info',
157
+ enableConsole: true,
158
+ enableFile: false,
159
+ includeTimestamp: false,
160
+ includeLevel: true
161
+ });
162
+ const consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation();
163
+ const consoleInfoSpy = jest.spyOn(console, 'info').mockImplementation();
164
+ infoLogger.debug('Debug message'); // Should not log
165
+ infoLogger.info('Info message'); // Should log
166
+ expect(consoleDebugSpy).not.toHaveBeenCalled();
167
+ expect(consoleInfoSpy).toHaveBeenCalledTimes(1);
168
+ expect(consoleInfoSpy).toHaveBeenCalledWith(expect.stringContaining('Info message'));
169
+ consoleDebugSpy.mockRestore();
170
+ consoleInfoSpy.mockRestore();
171
+ });
172
+ test('should log with additional data', () => {
173
+ const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
174
+ logger.info('Message with data', { key: 'value', number: 42 });
175
+ expect(consoleSpy).toHaveBeenCalled();
176
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('key'));
177
+ consoleSpy.mockRestore();
178
+ });
179
+ test('should handle error log level hierarchy', () => {
180
+ const errorLogger = new config_1.Logger({
181
+ level: 'error',
182
+ enableConsole: true,
183
+ enableFile: false,
184
+ includeTimestamp: false,
185
+ includeLevel: true
186
+ });
187
+ const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
188
+ const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
189
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
190
+ errorLogger.debug('Debug'); // Should not log
191
+ errorLogger.info('Info'); // Should not log
192
+ errorLogger.warn('Warn'); // Should not log
193
+ errorLogger.error('Error'); // Should log
194
+ expect(consoleLogSpy).not.toHaveBeenCalled();
195
+ expect(consoleWarnSpy).not.toHaveBeenCalled();
196
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
197
+ consoleLogSpy.mockRestore();
198
+ consoleWarnSpy.mockRestore();
199
+ consoleErrorSpy.mockRestore();
200
+ });
201
+ test('should format messages without timestamp', () => {
202
+ const noTimestampLogger = new config_1.Logger({
203
+ level: 'info',
204
+ enableConsole: true,
205
+ enableFile: false,
206
+ includeTimestamp: false,
207
+ includeLevel: true
208
+ });
209
+ const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
210
+ noTimestampLogger.info('No timestamp');
211
+ expect(consoleSpy).toHaveBeenCalled();
212
+ const calls = consoleSpy.mock.calls[0][0];
213
+ expect(calls).not.toMatch(/\[\d{4}-\d{2}-\d{2}T/);
214
+ consoleSpy.mockRestore();
215
+ });
216
+ test('should format messages without level', () => {
217
+ const noLevelLogger = new config_1.Logger({
218
+ level: 'info',
219
+ enableConsole: true,
220
+ enableFile: false,
221
+ includeTimestamp: false,
222
+ includeLevel: false
223
+ });
224
+ const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
225
+ noLevelLogger.info('No level');
226
+ expect(consoleSpy).toHaveBeenCalled();
227
+ const calls = consoleSpy.mock.calls[0][0];
228
+ expect(calls).not.toContain('[INFO]');
229
+ consoleSpy.mockRestore();
230
+ });
231
+ });
232
+ describe('DEFAULT_CONFIG', () => {
233
+ test('should have sensible defaults', () => {
234
+ expect(config_1.DEFAULT_CONFIG.connectionTimeout).toBe(30000);
235
+ expect(config_1.DEFAULT_CONFIG.requestTimeout).toBe(60000);
236
+ expect(config_1.DEFAULT_CONFIG.enableRetry).toBe(true);
237
+ expect(config_1.DEFAULT_CONFIG.maxRetries).toBe(3);
238
+ expect(config_1.DEFAULT_CONFIG.retryDelay).toBe(1000);
239
+ expect(config_1.DEFAULT_CONFIG.verbose).toBe(false);
240
+ expect(config_1.DEFAULT_CONFIG.maxConcurrentRequests).toBe(5);
241
+ expect(config_1.DEFAULT_CONFIG.minRequestInterval).toBe(100);
242
+ expect(config_1.DEFAULT_CONFIG.autoReconnect).toBe(true);
243
+ expect(config_1.DEFAULT_CONFIG.trustNewIdentities).toBe('on-first-use');
244
+ expect(config_1.DEFAULT_CONFIG.disableSendLog).toBe(false);
245
+ });
246
+ test('should have empty strings for optional paths', () => {
247
+ expect(config_1.DEFAULT_CONFIG.signalCliPath).toBe('');
248
+ expect(config_1.DEFAULT_CONFIG.account).toBe('');
249
+ expect(config_1.DEFAULT_CONFIG.logFile).toBe('');
250
+ });
251
+ });
252
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Additional tests for errors.ts
3
+ * Tests all error classes and their properties
4
+ */
5
+ export {};