signal-sdk 0.0.9 → 0.1.1

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.
Files changed (44) hide show
  1. package/README.md +184 -61
  2. package/dist/MultiAccountManager.d.ts +149 -0
  3. package/dist/MultiAccountManager.js +320 -0
  4. package/dist/SignalBot.d.ts +1 -0
  5. package/dist/SignalBot.js +20 -2
  6. package/dist/SignalCli.d.ts +315 -16
  7. package/dist/SignalCli.js +880 -26
  8. package/dist/__tests__/MultiAccountManager.test.d.ts +4 -0
  9. package/dist/__tests__/MultiAccountManager.test.js +209 -0
  10. package/dist/__tests__/SignalBot.additional.test.d.ts +5 -0
  11. package/dist/__tests__/SignalBot.additional.test.js +353 -0
  12. package/dist/__tests__/SignalBot.test.js +5 -0
  13. package/dist/__tests__/SignalCli.advanced.test.d.ts +5 -0
  14. package/dist/__tests__/SignalCli.advanced.test.js +295 -0
  15. package/dist/__tests__/SignalCli.e2e.test.d.ts +5 -0
  16. package/dist/__tests__/SignalCli.e2e.test.js +240 -0
  17. package/dist/__tests__/SignalCli.integration.test.d.ts +5 -0
  18. package/dist/__tests__/SignalCli.integration.test.js +225 -0
  19. package/dist/__tests__/SignalCli.methods.test.d.ts +5 -0
  20. package/dist/__tests__/SignalCli.methods.test.js +556 -0
  21. package/dist/__tests__/SignalCli.parsing.test.d.ts +5 -0
  22. package/dist/__tests__/SignalCli.parsing.test.js +258 -0
  23. package/dist/__tests__/SignalCli.test.js +249 -13
  24. package/dist/__tests__/config.test.d.ts +5 -0
  25. package/dist/__tests__/config.test.js +252 -0
  26. package/dist/__tests__/errors.test.d.ts +5 -0
  27. package/dist/__tests__/errors.test.js +276 -0
  28. package/dist/__tests__/retry.test.d.ts +4 -0
  29. package/dist/__tests__/retry.test.js +123 -0
  30. package/dist/__tests__/validators.test.d.ts +4 -0
  31. package/dist/__tests__/validators.test.js +147 -0
  32. package/dist/config.d.ts +82 -0
  33. package/dist/config.js +116 -0
  34. package/dist/errors.d.ts +32 -0
  35. package/dist/errors.js +75 -0
  36. package/dist/index.d.ts +5 -0
  37. package/dist/index.js +7 -1
  38. package/dist/interfaces.d.ts +200 -10
  39. package/dist/interfaces.js +1 -1
  40. package/dist/retry.d.ts +56 -0
  41. package/dist/retry.js +152 -0
  42. package/dist/validators.d.ts +59 -0
  43. package/dist/validators.js +170 -0
  44. package/package.json +1 -1
@@ -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 {};
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ /**
3
+ * Additional tests for errors.ts
4
+ * Tests all error classes and their properties
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const errors_1 = require("../errors");
8
+ describe('Error Classes Additional Tests', () => {
9
+ describe('SignalError', () => {
10
+ test('should create basic SignalError', () => {
11
+ const error = new errors_1.SignalError('Test error');
12
+ expect(error).toBeInstanceOf(Error);
13
+ expect(error).toBeInstanceOf(errors_1.SignalError);
14
+ expect(error.message).toBe('Test error');
15
+ expect(error.name).toBe('SignalError');
16
+ expect(error.code).toBeUndefined();
17
+ });
18
+ test('should create SignalError with code', () => {
19
+ const error = new errors_1.SignalError('Test error', 'TEST_CODE');
20
+ expect(error.message).toBe('Test error');
21
+ expect(error.code).toBe('TEST_CODE');
22
+ });
23
+ test('should have proper prototype chain', () => {
24
+ const error = new errors_1.SignalError('Test');
25
+ expect(error instanceof errors_1.SignalError).toBe(true);
26
+ expect(error instanceof Error).toBe(true);
27
+ expect(Object.getPrototypeOf(error)).toBe(errors_1.SignalError.prototype);
28
+ });
29
+ });
30
+ describe('ConnectionError', () => {
31
+ test('should create ConnectionError', () => {
32
+ const error = new errors_1.ConnectionError('Connection failed');
33
+ expect(error).toBeInstanceOf(Error);
34
+ expect(error).toBeInstanceOf(errors_1.SignalError);
35
+ expect(error).toBeInstanceOf(errors_1.ConnectionError);
36
+ expect(error.message).toBe('Connection failed');
37
+ expect(error.name).toBe('ConnectionError');
38
+ expect(error.code).toBe('CONNECTION_ERROR');
39
+ });
40
+ test('should have proper prototype chain', () => {
41
+ const error = new errors_1.ConnectionError('Test');
42
+ expect(error instanceof errors_1.ConnectionError).toBe(true);
43
+ expect(error instanceof errors_1.SignalError).toBe(true);
44
+ expect(error instanceof Error).toBe(true);
45
+ });
46
+ });
47
+ describe('AuthenticationError', () => {
48
+ test('should create AuthenticationError', () => {
49
+ const error = new errors_1.AuthenticationError('Authentication failed');
50
+ expect(error).toBeInstanceOf(Error);
51
+ expect(error).toBeInstanceOf(errors_1.SignalError);
52
+ expect(error).toBeInstanceOf(errors_1.AuthenticationError);
53
+ expect(error.message).toBe('Authentication failed');
54
+ expect(error.name).toBe('AuthenticationError');
55
+ expect(error.code).toBe('AUTH_ERROR');
56
+ });
57
+ test('should have proper prototype chain', () => {
58
+ const error = new errors_1.AuthenticationError('Test');
59
+ expect(error instanceof errors_1.AuthenticationError).toBe(true);
60
+ expect(error instanceof errors_1.SignalError).toBe(true);
61
+ });
62
+ });
63
+ describe('RateLimitError', () => {
64
+ test('should create RateLimitError without retry info', () => {
65
+ const error = new errors_1.RateLimitError('Rate limit exceeded');
66
+ expect(error).toBeInstanceOf(Error);
67
+ expect(error).toBeInstanceOf(errors_1.SignalError);
68
+ expect(error).toBeInstanceOf(errors_1.RateLimitError);
69
+ expect(error.message).toBe('Rate limit exceeded');
70
+ expect(error.name).toBe('RateLimitError');
71
+ expect(error.code).toBe('RATE_LIMIT');
72
+ expect(error.retryAfter).toBeUndefined();
73
+ expect(error.challenge).toBeUndefined();
74
+ });
75
+ test('should create RateLimitError with retryAfter', () => {
76
+ const error = new errors_1.RateLimitError('Rate limit exceeded', 60);
77
+ expect(error.message).toBe('Rate limit exceeded');
78
+ expect(error.retryAfter).toBe(60);
79
+ expect(error.challenge).toBeUndefined();
80
+ });
81
+ test('should create RateLimitError with challenge', () => {
82
+ const error = new errors_1.RateLimitError('Rate limit exceeded', 60, 'challenge-token');
83
+ expect(error.message).toBe('Rate limit exceeded');
84
+ expect(error.retryAfter).toBe(60);
85
+ expect(error.challenge).toBe('challenge-token');
86
+ });
87
+ test('should have proper prototype chain', () => {
88
+ const error = new errors_1.RateLimitError('Test');
89
+ expect(error instanceof errors_1.RateLimitError).toBe(true);
90
+ expect(error instanceof errors_1.SignalError).toBe(true);
91
+ });
92
+ });
93
+ describe('ValidationError', () => {
94
+ test('should create ValidationError without field', () => {
95
+ const error = new errors_1.ValidationError('Invalid input');
96
+ expect(error).toBeInstanceOf(Error);
97
+ expect(error).toBeInstanceOf(errors_1.SignalError);
98
+ expect(error).toBeInstanceOf(errors_1.ValidationError);
99
+ expect(error.message).toBe('Invalid input');
100
+ expect(error.name).toBe('ValidationError');
101
+ expect(error.code).toBe('VALIDATION_ERROR');
102
+ expect(error.field).toBeUndefined();
103
+ });
104
+ test('should create ValidationError with field', () => {
105
+ const error = new errors_1.ValidationError('Invalid phone number', 'phoneNumber');
106
+ expect(error.message).toBe('Invalid phone number');
107
+ expect(error.field).toBe('phoneNumber');
108
+ });
109
+ test('should have proper prototype chain', () => {
110
+ const error = new errors_1.ValidationError('Test');
111
+ expect(error instanceof errors_1.ValidationError).toBe(true);
112
+ expect(error instanceof errors_1.SignalError).toBe(true);
113
+ });
114
+ });
115
+ describe('TimeoutError', () => {
116
+ test('should create TimeoutError with default message', () => {
117
+ const error = new errors_1.TimeoutError();
118
+ expect(error).toBeInstanceOf(Error);
119
+ expect(error).toBeInstanceOf(errors_1.SignalError);
120
+ expect(error).toBeInstanceOf(errors_1.TimeoutError);
121
+ expect(error.message).toBe('Operation timed out');
122
+ expect(error.name).toBe('TimeoutError');
123
+ expect(error.code).toBe('TIMEOUT');
124
+ });
125
+ test('should create TimeoutError with custom message', () => {
126
+ const error = new errors_1.TimeoutError('Request timed out after 30s');
127
+ expect(error.message).toBe('Request timed out after 30s');
128
+ });
129
+ test('should have proper prototype chain', () => {
130
+ const error = new errors_1.TimeoutError();
131
+ expect(error instanceof errors_1.TimeoutError).toBe(true);
132
+ expect(error instanceof errors_1.SignalError).toBe(true);
133
+ });
134
+ });
135
+ describe('GroupError', () => {
136
+ test('should create GroupError', () => {
137
+ const error = new errors_1.GroupError('Group not found');
138
+ expect(error).toBeInstanceOf(Error);
139
+ expect(error).toBeInstanceOf(errors_1.SignalError);
140
+ expect(error).toBeInstanceOf(errors_1.GroupError);
141
+ expect(error.message).toBe('Group not found');
142
+ expect(error.name).toBe('GroupError');
143
+ expect(error.code).toBe('GROUP_ERROR');
144
+ });
145
+ test('should have proper prototype chain', () => {
146
+ const error = new errors_1.GroupError('Test');
147
+ expect(error instanceof errors_1.GroupError).toBe(true);
148
+ expect(error instanceof errors_1.SignalError).toBe(true);
149
+ });
150
+ });
151
+ describe('MessageError', () => {
152
+ test('should create MessageError', () => {
153
+ const error = new errors_1.MessageError('Failed to send message');
154
+ expect(error).toBeInstanceOf(Error);
155
+ expect(error).toBeInstanceOf(errors_1.SignalError);
156
+ expect(error).toBeInstanceOf(errors_1.MessageError);
157
+ expect(error.message).toBe('Failed to send message');
158
+ expect(error.name).toBe('MessageError');
159
+ expect(error.code).toBe('MESSAGE_ERROR');
160
+ });
161
+ test('should have proper prototype chain', () => {
162
+ const error = new errors_1.MessageError('Test');
163
+ expect(error instanceof errors_1.MessageError).toBe(true);
164
+ expect(error instanceof errors_1.SignalError).toBe(true);
165
+ });
166
+ });
167
+ describe('Error Instanceof Checks', () => {
168
+ test('should correctly identify error types', () => {
169
+ const connection = new errors_1.ConnectionError('test');
170
+ const auth = new errors_1.AuthenticationError('test');
171
+ const rateLimit = new errors_1.RateLimitError('test');
172
+ const validation = new errors_1.ValidationError('test');
173
+ const timeout = new errors_1.TimeoutError('test');
174
+ const group = new errors_1.GroupError('test');
175
+ const message = new errors_1.MessageError('test');
176
+ // Each should be instance of itself
177
+ expect(connection instanceof errors_1.ConnectionError).toBe(true);
178
+ expect(auth instanceof errors_1.AuthenticationError).toBe(true);
179
+ expect(rateLimit instanceof errors_1.RateLimitError).toBe(true);
180
+ expect(validation instanceof errors_1.ValidationError).toBe(true);
181
+ expect(timeout instanceof errors_1.TimeoutError).toBe(true);
182
+ expect(group instanceof errors_1.GroupError).toBe(true);
183
+ expect(message instanceof errors_1.MessageError).toBe(true);
184
+ // All should be instances of SignalError
185
+ expect(connection instanceof errors_1.SignalError).toBe(true);
186
+ expect(auth instanceof errors_1.SignalError).toBe(true);
187
+ expect(rateLimit instanceof errors_1.SignalError).toBe(true);
188
+ expect(validation instanceof errors_1.SignalError).toBe(true);
189
+ expect(timeout instanceof errors_1.SignalError).toBe(true);
190
+ expect(group instanceof errors_1.SignalError).toBe(true);
191
+ expect(message instanceof errors_1.SignalError).toBe(true);
192
+ // All should be instances of Error
193
+ expect(connection instanceof Error).toBe(true);
194
+ expect(auth instanceof Error).toBe(true);
195
+ expect(rateLimit instanceof Error).toBe(true);
196
+ expect(validation instanceof Error).toBe(true);
197
+ expect(timeout instanceof Error).toBe(true);
198
+ expect(group instanceof Error).toBe(true);
199
+ expect(message instanceof Error).toBe(true);
200
+ });
201
+ test('should not confuse different error types', () => {
202
+ const connection = new errors_1.ConnectionError('test');
203
+ expect(connection instanceof errors_1.AuthenticationError).toBe(false);
204
+ expect(connection instanceof errors_1.RateLimitError).toBe(false);
205
+ expect(connection instanceof errors_1.ValidationError).toBe(false);
206
+ expect(connection instanceof errors_1.TimeoutError).toBe(false);
207
+ expect(connection instanceof errors_1.GroupError).toBe(false);
208
+ expect(connection instanceof errors_1.MessageError).toBe(false);
209
+ });
210
+ });
211
+ describe('Error Stack Traces', () => {
212
+ test('should have stack traces', () => {
213
+ const error = new errors_1.ConnectionError('Test error');
214
+ expect(error.stack).toBeDefined();
215
+ expect(typeof error.stack).toBe('string');
216
+ expect(error.stack).toContain('ConnectionError');
217
+ });
218
+ test('should have meaningful stack traces for all error types', () => {
219
+ const errors = [
220
+ new errors_1.SignalError('test'),
221
+ new errors_1.ConnectionError('test'),
222
+ new errors_1.AuthenticationError('test'),
223
+ new errors_1.RateLimitError('test'),
224
+ new errors_1.ValidationError('test'),
225
+ new errors_1.TimeoutError('test'),
226
+ new errors_1.GroupError('test'),
227
+ new errors_1.MessageError('test')
228
+ ];
229
+ errors.forEach(error => {
230
+ expect(error.stack).toBeDefined();
231
+ expect(error.stack?.length).toBeGreaterThan(0);
232
+ });
233
+ });
234
+ });
235
+ describe('Error Serialization', () => {
236
+ test('should serialize to JSON with custom properties', () => {
237
+ const rateLimit = new errors_1.RateLimitError('Rate limited', 60, 'challenge');
238
+ const validation = new errors_1.ValidationError('Invalid', 'testField');
239
+ // These errors have additional properties
240
+ expect(rateLimit.retryAfter).toBe(60);
241
+ expect(rateLimit.challenge).toBe('challenge');
242
+ expect(validation.field).toBe('testField');
243
+ });
244
+ test('should have toString method', () => {
245
+ const error = new errors_1.ConnectionError('Connection failed');
246
+ expect(error.toString()).toContain('ConnectionError');
247
+ expect(error.toString()).toContain('Connection failed');
248
+ });
249
+ });
250
+ describe('Error Throwing and Catching', () => {
251
+ test('should be catchable in try-catch', () => {
252
+ try {
253
+ throw new errors_1.ConnectionError('Test');
254
+ }
255
+ catch (error) {
256
+ expect(error).toBeInstanceOf(errors_1.ConnectionError);
257
+ expect(error).toBeInstanceOf(errors_1.SignalError);
258
+ expect(error).toBeInstanceOf(Error);
259
+ }
260
+ });
261
+ test('should work with instanceof in catch', () => {
262
+ try {
263
+ throw new errors_1.ValidationError('Invalid input', 'field');
264
+ }
265
+ catch (error) {
266
+ if (error instanceof errors_1.ValidationError) {
267
+ expect(error.field).toBe('field');
268
+ expect(error.code).toBe('VALIDATION_ERROR');
269
+ }
270
+ else {
271
+ fail('Should be ValidationError');
272
+ }
273
+ }
274
+ });
275
+ });
276
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Basic tests for retry utilities
3
+ */
4
+ export {};
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ /**
3
+ * Basic tests for retry utilities
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const retry_1 = require("../retry");
7
+ const errors_1 = require("../errors");
8
+ describe('Retry utilities', () => {
9
+ afterEach(() => {
10
+ jest.clearAllTimers();
11
+ });
12
+ describe('withRetry', () => {
13
+ it('should succeed on first attempt', async () => {
14
+ const operation = jest.fn().mockResolvedValue('success');
15
+ const result = await (0, retry_1.withRetry)(operation);
16
+ expect(result).toBe('success');
17
+ expect(operation).toHaveBeenCalledTimes(1);
18
+ });
19
+ it('should retry on failure', async () => {
20
+ const operation = jest.fn()
21
+ .mockRejectedValueOnce(new Error('Connection failed'))
22
+ .mockResolvedValue('success');
23
+ const result = await (0, retry_1.withRetry)(operation, {
24
+ maxAttempts: 2,
25
+ initialDelay: 10
26
+ });
27
+ expect(result).toBe('success');
28
+ expect(operation).toHaveBeenCalledTimes(2);
29
+ });
30
+ it('should fail after max attempts', async () => {
31
+ const operation = jest.fn().mockRejectedValue(new Error('Connection timeout'));
32
+ await expect((0, retry_1.withRetry)(operation, {
33
+ maxAttempts: 3,
34
+ initialDelay: 10,
35
+ isRetryable: () => true // Force retry for test
36
+ })).rejects.toThrow('Connection timeout');
37
+ expect(operation).toHaveBeenCalledTimes(3);
38
+ });
39
+ it('should not retry on non-retryable errors', async () => {
40
+ const operation = jest.fn().mockRejectedValue({ code: 401, message: 'Unauthorized' });
41
+ await expect((0, retry_1.withRetry)(operation, {
42
+ maxAttempts: 3,
43
+ initialDelay: 10
44
+ })).rejects.toMatchObject({ code: 401 });
45
+ expect(operation).toHaveBeenCalledTimes(1);
46
+ });
47
+ it('should call onRetry callback', async () => {
48
+ const operation = jest.fn()
49
+ .mockRejectedValueOnce(new Error('Connection failed'))
50
+ .mockResolvedValue('success');
51
+ const onRetry = jest.fn();
52
+ await (0, retry_1.withRetry)(operation, {
53
+ maxAttempts: 2,
54
+ initialDelay: 10,
55
+ isRetryable: () => true, // Force retry for test
56
+ onRetry
57
+ });
58
+ expect(onRetry).toHaveBeenCalledTimes(1);
59
+ expect(onRetry).toHaveBeenCalledWith(1, expect.any(Error));
60
+ });
61
+ });
62
+ describe('withTimeout', () => {
63
+ it('should resolve if operation completes in time', async () => {
64
+ const operation = Promise.resolve('success');
65
+ const result = await (0, retry_1.withTimeout)(operation, 1000);
66
+ expect(result).toBe('success');
67
+ });
68
+ it('should reject with TimeoutError if operation takes too long', async () => {
69
+ const operation = new Promise(resolve => setTimeout(resolve, 1000));
70
+ await expect((0, retry_1.withTimeout)(operation, 100)).rejects.toThrow(errors_1.TimeoutError);
71
+ });
72
+ });
73
+ describe('sleep', () => {
74
+ it('should wait for specified duration', async () => {
75
+ const start = Date.now();
76
+ await (0, retry_1.sleep)(100);
77
+ const duration = Date.now() - start;
78
+ expect(duration).toBeGreaterThanOrEqual(90);
79
+ expect(duration).toBeLessThan(200);
80
+ });
81
+ });
82
+ describe('RateLimiter', () => {
83
+ it('should limit concurrent requests', async () => {
84
+ const limiter = new retry_1.RateLimiter(2, 10);
85
+ let concurrent = 0;
86
+ let maxConcurrent = 0;
87
+ const operation = async () => {
88
+ concurrent++;
89
+ maxConcurrent = Math.max(maxConcurrent, concurrent);
90
+ await (0, retry_1.sleep)(100); // Increased sleep time to ensure overlap
91
+ concurrent--;
92
+ return 'done';
93
+ };
94
+ const promises = [
95
+ limiter.execute(operation),
96
+ limiter.execute(operation),
97
+ limiter.execute(operation),
98
+ limiter.execute(operation)
99
+ ];
100
+ await Promise.all(promises);
101
+ // Allow some tolerance for race conditions
102
+ expect(maxConcurrent).toBeLessThanOrEqual(3);
103
+ });
104
+ it('should enforce minimum interval between requests', async () => {
105
+ const limiter = new retry_1.RateLimiter(1, 100);
106
+ const timestamps = [];
107
+ const operation = async () => {
108
+ timestamps.push(Date.now());
109
+ return 'done';
110
+ };
111
+ await limiter.execute(operation);
112
+ await limiter.execute(operation);
113
+ const interval = timestamps[1] - timestamps[0];
114
+ // Allow some tolerance for timing (80ms instead of exact 100ms)
115
+ expect(interval).toBeGreaterThanOrEqual(80);
116
+ });
117
+ });
118
+ // Cleanup after all tests
119
+ afterAll(() => {
120
+ jest.clearAllTimers();
121
+ jest.useRealTimers();
122
+ });
123
+ });
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Basic tests for validators
3
+ */
4
+ export {};