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.
- package/README.md +175 -59
- package/dist/SignalCli.d.ts +72 -2
- package/dist/SignalCli.js +257 -1
- package/dist/__tests__/SignalBot.additional.test.d.ts +5 -0
- package/dist/__tests__/SignalBot.additional.test.js +333 -0
- package/dist/__tests__/SignalCli.integration.test.d.ts +5 -0
- package/dist/__tests__/SignalCli.integration.test.js +218 -0
- package/dist/__tests__/SignalCli.methods.test.d.ts +5 -0
- package/dist/__tests__/SignalCli.methods.test.js +470 -0
- package/dist/__tests__/SignalCli.test.js +244 -0
- package/dist/__tests__/config.test.d.ts +5 -0
- package/dist/__tests__/config.test.js +252 -0
- package/dist/__tests__/errors.test.d.ts +5 -0
- package/dist/__tests__/errors.test.js +276 -0
- package/dist/__tests__/retry.test.d.ts +4 -0
- package/dist/__tests__/retry.test.js +123 -0
- package/dist/__tests__/validators.test.d.ts +4 -0
- package/dist/__tests__/validators.test.js +147 -0
- package/dist/config.d.ts +67 -0
- package/dist/config.js +111 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +75 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/interfaces.d.ts +136 -1
- package/dist/interfaces.js +1 -1
- package/dist/retry.d.ts +56 -0
- package/dist/retry.js +135 -0
- package/dist/validators.d.ts +59 -0
- package/dist/validators.js +170 -0
- package/package.json +1 -1
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Integration tests for SignalCli
|
|
4
|
+
* Tests complex scenarios and edge cases
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const SignalCli_1 = require("../SignalCli");
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
jest.mock('child_process');
|
|
10
|
+
describe('SignalCli Integration Tests', () => {
|
|
11
|
+
let signalCli;
|
|
12
|
+
let mockProcess;
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
mockProcess = {
|
|
15
|
+
stdout: {
|
|
16
|
+
on: jest.fn(),
|
|
17
|
+
once: jest.fn(),
|
|
18
|
+
},
|
|
19
|
+
stderr: {
|
|
20
|
+
on: jest.fn(),
|
|
21
|
+
},
|
|
22
|
+
stdin: {
|
|
23
|
+
write: jest.fn(),
|
|
24
|
+
},
|
|
25
|
+
on: jest.fn(),
|
|
26
|
+
once: jest.fn(),
|
|
27
|
+
kill: jest.fn(),
|
|
28
|
+
killed: false,
|
|
29
|
+
};
|
|
30
|
+
const spawnMock = child_process_1.spawn;
|
|
31
|
+
spawnMock.mockReturnValue(mockProcess);
|
|
32
|
+
signalCli = new SignalCli_1.SignalCli('signal-cli', '+1234567890');
|
|
33
|
+
});
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
if (signalCli) {
|
|
36
|
+
signalCli.disconnect();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
describe('Connection Lifecycle', () => {
|
|
40
|
+
it('should handle multiple connect/disconnect cycles', async () => {
|
|
41
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
42
|
+
if (event === 'data')
|
|
43
|
+
callback();
|
|
44
|
+
});
|
|
45
|
+
await signalCli.connect();
|
|
46
|
+
signalCli.disconnect();
|
|
47
|
+
await signalCli.connect();
|
|
48
|
+
signalCli.disconnect();
|
|
49
|
+
expect(mockProcess.kill).toHaveBeenCalledTimes(2);
|
|
50
|
+
});
|
|
51
|
+
it('should handle graceful shutdown timeout', async () => {
|
|
52
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
53
|
+
if (event === 'data')
|
|
54
|
+
callback();
|
|
55
|
+
});
|
|
56
|
+
await signalCli.connect();
|
|
57
|
+
// Don't emit close event to simulate hanging process
|
|
58
|
+
let closeCallback;
|
|
59
|
+
mockProcess.once.mockImplementation((event, callback) => {
|
|
60
|
+
if (event === 'close') {
|
|
61
|
+
closeCallback = callback;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
const shutdownPromise = signalCli.gracefulShutdown();
|
|
65
|
+
// Simulate timeout and force kill
|
|
66
|
+
setTimeout(() => {
|
|
67
|
+
if (closeCallback)
|
|
68
|
+
closeCallback(0);
|
|
69
|
+
}, 100);
|
|
70
|
+
await expect(shutdownPromise).resolves.toBeUndefined();
|
|
71
|
+
}, 10000); // Increase timeout to 10 seconds
|
|
72
|
+
it('should handle stderr data with different log levels', async () => {
|
|
73
|
+
const handleStderrData = signalCli.handleStderrData.bind(signalCli);
|
|
74
|
+
const errorHandler = jest.fn();
|
|
75
|
+
const logHandler = jest.fn();
|
|
76
|
+
signalCli.on('error', errorHandler);
|
|
77
|
+
signalCli.on('log', logHandler);
|
|
78
|
+
handleStderrData('ERROR Component - Critical error');
|
|
79
|
+
expect(errorHandler).toHaveBeenCalled();
|
|
80
|
+
handleStderrData('WARN Component - Warning message');
|
|
81
|
+
expect(logHandler).toHaveBeenCalledWith(expect.objectContaining({ level: 'warn' }));
|
|
82
|
+
handleStderrData('INFO Component - Info message');
|
|
83
|
+
expect(logHandler).toHaveBeenCalledWith(expect.objectContaining({ level: 'info' }));
|
|
84
|
+
handleStderrData('DEBUG Component - Debug message');
|
|
85
|
+
expect(logHandler).toHaveBeenCalledWith(expect.objectContaining({ level: 'debug' }));
|
|
86
|
+
});
|
|
87
|
+
it('should filter out informational WARN messages', async () => {
|
|
88
|
+
const handleStderrData = signalCli.handleStderrData.bind(signalCli);
|
|
89
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
90
|
+
handleStderrData('WARN Component - Failed to get sender certificate');
|
|
91
|
+
handleStderrData('WARN Component - ignoring: java.lang.InterruptedException');
|
|
92
|
+
handleStderrData('WARN Component - Request was interrupted');
|
|
93
|
+
expect(consoleSpy).not.toHaveBeenCalled();
|
|
94
|
+
handleStderrData('WARN Component - Real warning message');
|
|
95
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
96
|
+
consoleSpy.mockRestore();
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe('JSON-RPC Response Handling', () => {
|
|
100
|
+
it('should handle multi-line JSON-RPC responses', () => {
|
|
101
|
+
const handleRpcResponse = signalCli.handleRpcResponse.bind(signalCli);
|
|
102
|
+
const mockPromise = {
|
|
103
|
+
resolve: jest.fn(),
|
|
104
|
+
reject: jest.fn(),
|
|
105
|
+
};
|
|
106
|
+
signalCli.requestPromises.set('test-id', mockPromise);
|
|
107
|
+
const multiLineData = `{"jsonrpc":"2.0","id":"test-id","result":{"success":true}}\n{"jsonrpc":"2.0","method":"receive","params":{"data":"test"}}`;
|
|
108
|
+
handleRpcResponse(multiLineData);
|
|
109
|
+
expect(mockPromise.resolve).toHaveBeenCalledWith({ success: true });
|
|
110
|
+
});
|
|
111
|
+
it('should handle malformed JSON gracefully', () => {
|
|
112
|
+
const handleRpcResponse = signalCli.handleRpcResponse.bind(signalCli);
|
|
113
|
+
const errorHandler = jest.fn();
|
|
114
|
+
signalCli.on('error', errorHandler);
|
|
115
|
+
handleRpcResponse('not valid json');
|
|
116
|
+
expect(errorHandler).toHaveBeenCalledWith(expect.objectContaining({
|
|
117
|
+
message: expect.stringContaining('Failed to parse JSON-RPC response')
|
|
118
|
+
}));
|
|
119
|
+
});
|
|
120
|
+
it('should emit notifications correctly', () => {
|
|
121
|
+
const handleRpcResponse = signalCli.handleRpcResponse.bind(signalCli);
|
|
122
|
+
const notificationHandler = jest.fn();
|
|
123
|
+
const messageHandler = jest.fn();
|
|
124
|
+
signalCli.on('notification', notificationHandler);
|
|
125
|
+
signalCli.on('message', messageHandler);
|
|
126
|
+
const notification = '{"jsonrpc":"2.0","method":"receive","params":{"envelope":{"dataMessage":"test"}}}';
|
|
127
|
+
handleRpcResponse(notification);
|
|
128
|
+
expect(notificationHandler).toHaveBeenCalled();
|
|
129
|
+
expect(messageHandler).toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
describe('Validation Edge Cases', () => {
|
|
133
|
+
it('should validate recipients (phone, UUID, username)', async () => {
|
|
134
|
+
// Connect first
|
|
135
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
136
|
+
if (event === 'data')
|
|
137
|
+
callback();
|
|
138
|
+
});
|
|
139
|
+
await signalCli.connect();
|
|
140
|
+
jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
|
|
141
|
+
// Valid phone
|
|
142
|
+
await expect(signalCli.sendMessage('+1234567890', 'test')).resolves.toBeDefined();
|
|
143
|
+
// Valid UUID
|
|
144
|
+
await expect(signalCli.sendMessage('12345678-1234-1234-1234-123456789012', 'test')).resolves.toBeDefined();
|
|
145
|
+
// Valid username
|
|
146
|
+
await expect(signalCli.sendMessage('u:username.123', 'test')).resolves.toBeDefined();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
describe('Group Detection', () => {
|
|
150
|
+
it('should correctly identify group IDs', () => {
|
|
151
|
+
const isGroupId = signalCli.isGroupId.bind(signalCli);
|
|
152
|
+
expect(isGroupId('base64groupid==')).toBe(true);
|
|
153
|
+
expect(isGroupId('group/with/slash')).toBe(true);
|
|
154
|
+
expect(isGroupId('multiple+signs+notphone')).toBe(true);
|
|
155
|
+
expect(isGroupId('+1234567890')).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
describe('Deprecated Methods', () => {
|
|
159
|
+
it('should warn about deprecated receiveMessages', async () => {
|
|
160
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
161
|
+
await signalCli.receiveMessages();
|
|
162
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('deprecated'));
|
|
163
|
+
consoleSpy.mockRestore();
|
|
164
|
+
});
|
|
165
|
+
it('should warn about deprecated startDaemon', async () => {
|
|
166
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
167
|
+
mockProcess.stdout.once.mockImplementation((event, callback) => {
|
|
168
|
+
if (event === 'data')
|
|
169
|
+
callback();
|
|
170
|
+
});
|
|
171
|
+
await signalCli.startDaemon();
|
|
172
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('deprecated'));
|
|
173
|
+
consoleSpy.mockRestore();
|
|
174
|
+
});
|
|
175
|
+
it('should warn about deprecated stopDaemon', () => {
|
|
176
|
+
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
|
177
|
+
signalCli.stopDaemon();
|
|
178
|
+
expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('deprecated'));
|
|
179
|
+
consoleSpy.mockRestore();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
describe('Complex Scenarios', () => {
|
|
183
|
+
beforeEach(() => {
|
|
184
|
+
jest.spyOn(signalCli, 'sendJsonRpcRequest').mockResolvedValue({});
|
|
185
|
+
});
|
|
186
|
+
it('should handle group message with options', async () => {
|
|
187
|
+
const sendJsonRpcRequestSpy = jest.spyOn(signalCli, 'sendJsonRpcRequest')
|
|
188
|
+
.mockResolvedValue({ results: [{ type: 'SUCCESS' }], timestamp: 123456 });
|
|
189
|
+
await signalCli.sendMessage('groupId==', 'Group message', {
|
|
190
|
+
mentions: [{ start: 0, length: 4, number: '+1111111111' }]
|
|
191
|
+
});
|
|
192
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalled();
|
|
193
|
+
expect(sendJsonRpcRequestSpy).toHaveBeenCalledWith('send', expect.objectContaining({
|
|
194
|
+
message: 'Group message',
|
|
195
|
+
groupId: 'groupId=='
|
|
196
|
+
}));
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
describe('Error Handling', () => {
|
|
200
|
+
it('should reject when not connected', async () => {
|
|
201
|
+
const disconnectedSignal = new SignalCli_1.SignalCli('signal-cli', '+1234567890');
|
|
202
|
+
await expect(disconnectedSignal.sendMessage('+0987654321', 'test')).rejects.toThrow('Not connected');
|
|
203
|
+
});
|
|
204
|
+
it('should handle JSON-RPC errors correctly', () => {
|
|
205
|
+
const handleRpcResponse = signalCli.handleRpcResponse.bind(signalCli);
|
|
206
|
+
const mockPromise = {
|
|
207
|
+
resolve: jest.fn(),
|
|
208
|
+
reject: jest.fn(),
|
|
209
|
+
};
|
|
210
|
+
signalCli.requestPromises.set('error-id', mockPromise);
|
|
211
|
+
const errorResponse = '{"jsonrpc":"2.0","id":"error-id","error":{"code":-32600,"message":"Invalid Request"}}';
|
|
212
|
+
handleRpcResponse(errorResponse);
|
|
213
|
+
expect(mockPromise.reject).toHaveBeenCalledWith(expect.objectContaining({
|
|
214
|
+
message: expect.stringContaining('Invalid Request')
|
|
215
|
+
}));
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
});
|