@massalabs/gossip-sdk 0.0.2-dev.20260128094509 → 0.0.2-dev.20260128111120
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/dist/api/messageProtocol/index.d.ts +19 -0
- package/dist/api/messageProtocol/index.js +26 -0
- package/dist/api/messageProtocol/mock.d.ts +12 -0
- package/{src/api/messageProtocol/mock.ts → dist/api/messageProtocol/mock.js} +2 -3
- package/dist/api/messageProtocol/rest.d.ts +22 -0
- package/dist/api/messageProtocol/rest.js +161 -0
- package/dist/api/messageProtocol/types.d.ts +61 -0
- package/dist/api/messageProtocol/types.js +6 -0
- package/dist/assets/generated/wasm/README.md +281 -0
- package/dist/assets/generated/wasm/gossip_wasm.d.ts +498 -0
- package/dist/assets/generated/wasm/gossip_wasm.js +1399 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm +0 -0
- package/dist/assets/generated/wasm/gossip_wasm_bg.wasm.d.ts +68 -0
- package/dist/assets/generated/wasm/package.json +15 -0
- package/dist/config/protocol.d.ts +36 -0
- package/dist/config/protocol.js +77 -0
- package/dist/config/sdk.d.ts +82 -0
- package/dist/config/sdk.js +55 -0
- package/{src/contacts.ts → dist/contacts.d.ts} +10 -94
- package/dist/contacts.js +166 -0
- package/dist/core/SdkEventEmitter.d.ts +36 -0
- package/dist/core/SdkEventEmitter.js +59 -0
- package/dist/core/SdkPolling.d.ts +35 -0
- package/dist/core/SdkPolling.js +100 -0
- package/{src/core/index.ts → dist/core/index.d.ts} +0 -2
- package/dist/core/index.js +5 -0
- package/dist/crypto/bip39.d.ts +34 -0
- package/dist/crypto/bip39.js +62 -0
- package/dist/crypto/encryption.d.ts +37 -0
- package/dist/crypto/encryption.js +46 -0
- package/dist/db.d.ts +190 -0
- package/dist/db.js +311 -0
- package/dist/gossipSdk.d.ts +274 -0
- package/dist/gossipSdk.js +690 -0
- package/dist/index.d.ts +73 -0
- package/dist/index.js +77 -0
- package/dist/services/announcement.d.ts +43 -0
- package/dist/services/announcement.js +491 -0
- package/dist/services/auth.d.ts +37 -0
- package/dist/services/auth.js +76 -0
- package/dist/services/discussion.d.ts +63 -0
- package/dist/services/discussion.js +297 -0
- package/dist/services/message.d.ts +74 -0
- package/dist/services/message.js +826 -0
- package/dist/services/refresh.d.ts +41 -0
- package/dist/services/refresh.js +205 -0
- package/{src/sw.ts → dist/sw.d.ts} +1 -8
- package/dist/sw.js +10 -0
- package/dist/types/events.d.ts +80 -0
- package/dist/types/events.js +7 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.js +7 -0
- package/dist/utils/base64.d.ts +10 -0
- package/dist/utils/base64.js +30 -0
- package/dist/utils/contacts.d.ts +42 -0
- package/dist/utils/contacts.js +113 -0
- package/dist/utils/discussions.d.ts +24 -0
- package/dist/utils/discussions.js +38 -0
- package/dist/utils/logs.d.ts +19 -0
- package/dist/utils/logs.js +89 -0
- package/dist/utils/messageSerialization.d.ts +64 -0
- package/dist/utils/messageSerialization.js +184 -0
- package/dist/utils/queue.d.ts +50 -0
- package/dist/utils/queue.js +110 -0
- package/dist/utils/type.d.ts +10 -0
- package/dist/utils/type.js +4 -0
- package/dist/utils/userId.d.ts +40 -0
- package/dist/utils/userId.js +90 -0
- package/dist/utils/validation.d.ts +50 -0
- package/dist/utils/validation.js +112 -0
- package/dist/utils.d.ts +30 -0
- package/{src/utils.ts → dist/utils.js} +9 -19
- package/dist/wasm/encryption.d.ts +56 -0
- package/{src/wasm/encryption.ts → dist/wasm/encryption.js} +22 -51
- package/dist/wasm/index.d.ts +10 -0
- package/{src/wasm/index.ts → dist/wasm/index.js} +1 -8
- package/dist/wasm/loader.d.ts +21 -0
- package/dist/wasm/loader.js +103 -0
- package/dist/wasm/session.d.ts +85 -0
- package/dist/wasm/session.js +226 -0
- package/dist/wasm/userKeys.d.ts +17 -0
- package/{src/wasm/userKeys.ts → dist/wasm/userKeys.js} +6 -13
- package/package.json +5 -1
- package/src/api/messageProtocol/index.ts +0 -53
- package/src/api/messageProtocol/rest.ts +0 -209
- package/src/api/messageProtocol/types.ts +0 -70
- package/src/config/protocol.ts +0 -97
- package/src/config/sdk.ts +0 -131
- package/src/core/SdkEventEmitter.ts +0 -91
- package/src/core/SdkPolling.ts +0 -134
- package/src/crypto/bip39.ts +0 -84
- package/src/crypto/encryption.ts +0 -77
- package/src/db.ts +0 -465
- package/src/gossipSdk.ts +0 -994
- package/src/index.ts +0 -211
- package/src/services/announcement.ts +0 -653
- package/src/services/auth.ts +0 -95
- package/src/services/discussion.ts +0 -380
- package/src/services/message.ts +0 -1055
- package/src/services/refresh.ts +0 -234
- package/src/types/events.ts +0 -108
- package/src/types.ts +0 -70
- package/src/utils/base64.ts +0 -39
- package/src/utils/contacts.ts +0 -161
- package/src/utils/discussions.ts +0 -55
- package/src/utils/logs.ts +0 -86
- package/src/utils/messageSerialization.ts +0 -257
- package/src/utils/queue.ts +0 -106
- package/src/utils/type.ts +0 -7
- package/src/utils/userId.ts +0 -114
- package/src/utils/validation.ts +0 -144
- package/src/wasm/loader.ts +0 -123
- package/src/wasm/session.ts +0 -276
- package/test/config/protocol.spec.ts +0 -31
- package/test/config/sdk.spec.ts +0 -163
- package/test/db/helpers.spec.ts +0 -142
- package/test/db/operations.spec.ts +0 -128
- package/test/db/states.spec.ts +0 -535
- package/test/integration/discussion-flow.spec.ts +0 -422
- package/test/integration/messaging-flow.spec.ts +0 -708
- package/test/integration/sdk-lifecycle.spec.ts +0 -325
- package/test/mocks/index.ts +0 -9
- package/test/mocks/mockMessageProtocol.ts +0 -100
- package/test/services/auth.spec.ts +0 -311
- package/test/services/discussion.spec.ts +0 -279
- package/test/services/message-deduplication.spec.ts +0 -299
- package/test/services/message-startup.spec.ts +0 -331
- package/test/services/message.spec.ts +0 -817
- package/test/services/refresh.spec.ts +0 -199
- package/test/services/session-status.spec.ts +0 -349
- package/test/session/wasm.spec.ts +0 -227
- package/test/setup.ts +0 -52
- package/test/utils/contacts.spec.ts +0 -156
- package/test/utils/discussions.spec.ts +0 -66
- package/test/utils/queue.spec.ts +0 -52
- package/test/utils/serialization.spec.ts +0 -120
- package/test/utils/userId.spec.ts +0 -120
- package/test/utils/validation.spec.ts +0 -223
- package/test/utils.ts +0 -212
- package/tsconfig.json +0 -26
- package/tsconfig.tsbuildinfo +0 -1
- package/vitest.config.ts +0 -28
|
@@ -1,708 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Messaging e2e-style tests
|
|
3
|
-
*
|
|
4
|
-
* Uses real WASM SessionModule with real crypto.
|
|
5
|
-
* MockMessageProtocol provides in-memory message storage (no network).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
9
|
-
import { AnnouncementService } from '../../src/services/announcement';
|
|
10
|
-
import { DiscussionService } from '../../src/services/discussion';
|
|
11
|
-
import { MessageService } from '../../src/services/message';
|
|
12
|
-
import {
|
|
13
|
-
db,
|
|
14
|
-
DiscussionStatus,
|
|
15
|
-
DiscussionDirection,
|
|
16
|
-
MessageStatus,
|
|
17
|
-
MessageDirection,
|
|
18
|
-
MessageType,
|
|
19
|
-
} from '../../src/db';
|
|
20
|
-
import { SessionStatus } from '../../src/assets/generated/wasm/gossip_wasm';
|
|
21
|
-
import { MockMessageProtocol } from '../mocks';
|
|
22
|
-
import {
|
|
23
|
-
createTestSession,
|
|
24
|
-
cleanupTestSession,
|
|
25
|
-
TestSessionData,
|
|
26
|
-
} from '../utils';
|
|
27
|
-
import type { GossipSdkEvents } from '../../src/types/events';
|
|
28
|
-
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// Session renewal resends unacknowledged messages
|
|
31
|
-
// ============================================================================
|
|
32
|
-
|
|
33
|
-
describe('Session renewal resends unacknowledged messages', () => {
|
|
34
|
-
let alice: TestSessionData;
|
|
35
|
-
let bob: TestSessionData;
|
|
36
|
-
let mockProtocol: MockMessageProtocol;
|
|
37
|
-
let events: GossipSdkEvents;
|
|
38
|
-
let messageService: MessageService;
|
|
39
|
-
let discussionService: DiscussionService;
|
|
40
|
-
let announcementService: AnnouncementService;
|
|
41
|
-
|
|
42
|
-
beforeEach(async () => {
|
|
43
|
-
if (!db.isOpen()) {
|
|
44
|
-
await db.open();
|
|
45
|
-
}
|
|
46
|
-
await Promise.all(db.tables.map(table => table.clear()));
|
|
47
|
-
|
|
48
|
-
// Create real WASM sessions
|
|
49
|
-
alice = await createTestSession(`alice-renew-${Date.now()}`);
|
|
50
|
-
bob = await createTestSession(`bob-renew-${Date.now()}`);
|
|
51
|
-
|
|
52
|
-
mockProtocol = new MockMessageProtocol();
|
|
53
|
-
events = {};
|
|
54
|
-
|
|
55
|
-
// Add Bob as Alice's contact
|
|
56
|
-
await db.contacts.add({
|
|
57
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
58
|
-
userId: bob.session.userIdEncoded,
|
|
59
|
-
name: 'Bob',
|
|
60
|
-
publicKeys: bob.session.ourPk.to_bytes(),
|
|
61
|
-
isOnline: false,
|
|
62
|
-
lastSeen: new Date(),
|
|
63
|
-
createdAt: new Date(),
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Set up services
|
|
67
|
-
announcementService = new AnnouncementService(
|
|
68
|
-
db,
|
|
69
|
-
mockProtocol,
|
|
70
|
-
alice.session,
|
|
71
|
-
events
|
|
72
|
-
);
|
|
73
|
-
discussionService = new DiscussionService(
|
|
74
|
-
db,
|
|
75
|
-
announcementService,
|
|
76
|
-
alice.session
|
|
77
|
-
);
|
|
78
|
-
messageService = new MessageService(
|
|
79
|
-
db,
|
|
80
|
-
mockProtocol,
|
|
81
|
-
alice.session,
|
|
82
|
-
discussionService,
|
|
83
|
-
events
|
|
84
|
-
);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
afterEach(() => {
|
|
88
|
-
cleanupTestSession(alice);
|
|
89
|
-
cleanupTestSession(bob);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Helper to reset unacknowledged messages to WAITING_SESSION
|
|
94
|
-
* (simulates what happens when session needs renewal)
|
|
95
|
-
*/
|
|
96
|
-
async function simulateRenewReset(
|
|
97
|
-
ownerUserId: string,
|
|
98
|
-
contactUserId: string
|
|
99
|
-
): Promise<number> {
|
|
100
|
-
return await db.messages
|
|
101
|
-
.where('[ownerUserId+contactUserId]')
|
|
102
|
-
.equals([ownerUserId, contactUserId])
|
|
103
|
-
.and(
|
|
104
|
-
message =>
|
|
105
|
-
message.direction === MessageDirection.OUTGOING &&
|
|
106
|
-
(message.status === MessageStatus.SENDING ||
|
|
107
|
-
message.status === MessageStatus.FAILED ||
|
|
108
|
-
message.status === MessageStatus.SENT)
|
|
109
|
-
)
|
|
110
|
-
.modify({
|
|
111
|
-
status: MessageStatus.WAITING_SESSION,
|
|
112
|
-
encryptedMessage: undefined,
|
|
113
|
-
seeker: undefined,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
describe('Manual renewal flow', () => {
|
|
118
|
-
it('should reset SENT messages to WAITING_SESSION and resend when session becomes active', async () => {
|
|
119
|
-
// Establish a real session between Alice and Bob
|
|
120
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
121
|
-
bob.session.ourPk
|
|
122
|
-
);
|
|
123
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
124
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
125
|
-
alice.session.ourPk
|
|
126
|
-
);
|
|
127
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
128
|
-
|
|
129
|
-
// Verify session is active
|
|
130
|
-
expect(alice.session.peerSessionStatus(bob.session.userId)).toBe(
|
|
131
|
-
SessionStatus.Active
|
|
132
|
-
);
|
|
133
|
-
|
|
134
|
-
// Create active discussion
|
|
135
|
-
await db.discussions.add({
|
|
136
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
137
|
-
contactUserId: bob.session.userIdEncoded,
|
|
138
|
-
direction: DiscussionDirection.INITIATED,
|
|
139
|
-
status: DiscussionStatus.ACTIVE,
|
|
140
|
-
unreadCount: 0,
|
|
141
|
-
createdAt: new Date(),
|
|
142
|
-
updatedAt: new Date(),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// Add a SENT message (simulates message sent but not acknowledged)
|
|
146
|
-
const sentMessageId = await db.messages.add({
|
|
147
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
148
|
-
contactUserId: bob.session.userIdEncoded,
|
|
149
|
-
content: 'Hello Bob! This was sent but not delivered.',
|
|
150
|
-
type: MessageType.TEXT,
|
|
151
|
-
direction: MessageDirection.OUTGOING,
|
|
152
|
-
status: MessageStatus.SENT,
|
|
153
|
-
timestamp: new Date(),
|
|
154
|
-
seeker: new Uint8Array(32).fill(1),
|
|
155
|
-
encryptedMessage: new Uint8Array(64).fill(2),
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// Add a DELIVERED message (should not be reset)
|
|
159
|
-
const deliveredMessageId = await db.messages.add({
|
|
160
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
161
|
-
contactUserId: bob.session.userIdEncoded,
|
|
162
|
-
content: 'This was delivered already.',
|
|
163
|
-
type: MessageType.TEXT,
|
|
164
|
-
direction: MessageDirection.OUTGOING,
|
|
165
|
-
status: MessageStatus.DELIVERED,
|
|
166
|
-
timestamp: new Date(),
|
|
167
|
-
seeker: new Uint8Array(32).fill(3),
|
|
168
|
-
encryptedMessage: new Uint8Array(64).fill(4),
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
// Verify initial state
|
|
172
|
-
let sentMessage = await db.messages.get(sentMessageId);
|
|
173
|
-
expect(sentMessage?.status).toBe(MessageStatus.SENT);
|
|
174
|
-
expect(sentMessage?.seeker).toBeDefined();
|
|
175
|
-
|
|
176
|
-
// Simulate renewal reset
|
|
177
|
-
const resetCount = await simulateRenewReset(
|
|
178
|
-
alice.session.userIdEncoded,
|
|
179
|
-
bob.session.userIdEncoded
|
|
180
|
-
);
|
|
181
|
-
expect(resetCount).toBe(1);
|
|
182
|
-
|
|
183
|
-
// Verify reset state
|
|
184
|
-
sentMessage = await db.messages.get(sentMessageId);
|
|
185
|
-
expect(sentMessage?.status).toBe(MessageStatus.WAITING_SESSION);
|
|
186
|
-
expect(sentMessage?.seeker).toBeUndefined();
|
|
187
|
-
|
|
188
|
-
// DELIVERED should be unchanged
|
|
189
|
-
const deliveredMessage = await db.messages.get(deliveredMessageId);
|
|
190
|
-
expect(deliveredMessage?.status).toBe(MessageStatus.DELIVERED);
|
|
191
|
-
expect(deliveredMessage?.seeker).toBeDefined();
|
|
192
|
-
|
|
193
|
-
// Process waiting messages (session is still active)
|
|
194
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
195
|
-
bob.session.userIdEncoded
|
|
196
|
-
);
|
|
197
|
-
|
|
198
|
-
expect(sentCount).toBe(1);
|
|
199
|
-
sentMessage = await db.messages.get(sentMessageId);
|
|
200
|
-
expect(sentMessage?.status).toBe(MessageStatus.SENT);
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('should reset multiple unacknowledged messages (SENT, SENDING, FAILED) on renew', async () => {
|
|
204
|
-
// Establish session
|
|
205
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
206
|
-
bob.session.ourPk
|
|
207
|
-
);
|
|
208
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
209
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
210
|
-
alice.session.ourPk
|
|
211
|
-
);
|
|
212
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
213
|
-
|
|
214
|
-
await db.discussions.add({
|
|
215
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
216
|
-
contactUserId: bob.session.userIdEncoded,
|
|
217
|
-
direction: DiscussionDirection.INITIATED,
|
|
218
|
-
status: DiscussionStatus.ACTIVE,
|
|
219
|
-
unreadCount: 0,
|
|
220
|
-
createdAt: new Date(),
|
|
221
|
-
updatedAt: new Date(),
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
// Add messages with different statuses
|
|
225
|
-
const sentId = await db.messages.add({
|
|
226
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
227
|
-
contactUserId: bob.session.userIdEncoded,
|
|
228
|
-
content: 'SENT message',
|
|
229
|
-
type: MessageType.TEXT,
|
|
230
|
-
direction: MessageDirection.OUTGOING,
|
|
231
|
-
status: MessageStatus.SENT,
|
|
232
|
-
timestamp: new Date(Date.now() - 3000),
|
|
233
|
-
seeker: new Uint8Array(32).fill(1),
|
|
234
|
-
encryptedMessage: new Uint8Array(64).fill(1),
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const sendingId = await db.messages.add({
|
|
238
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
239
|
-
contactUserId: bob.session.userIdEncoded,
|
|
240
|
-
content: 'SENDING message (interrupted)',
|
|
241
|
-
type: MessageType.TEXT,
|
|
242
|
-
direction: MessageDirection.OUTGOING,
|
|
243
|
-
status: MessageStatus.SENDING,
|
|
244
|
-
timestamp: new Date(Date.now() - 2000),
|
|
245
|
-
seeker: new Uint8Array(32).fill(2),
|
|
246
|
-
encryptedMessage: new Uint8Array(64).fill(2),
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
const failedId = await db.messages.add({
|
|
250
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
251
|
-
contactUserId: bob.session.userIdEncoded,
|
|
252
|
-
content: 'FAILED message',
|
|
253
|
-
type: MessageType.TEXT,
|
|
254
|
-
direction: MessageDirection.OUTGOING,
|
|
255
|
-
status: MessageStatus.FAILED,
|
|
256
|
-
timestamp: new Date(Date.now() - 1000),
|
|
257
|
-
seeker: new Uint8Array(32).fill(3),
|
|
258
|
-
encryptedMessage: new Uint8Array(64).fill(3),
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
const deliveredId = await db.messages.add({
|
|
262
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
263
|
-
contactUserId: bob.session.userIdEncoded,
|
|
264
|
-
content: 'DELIVERED message',
|
|
265
|
-
type: MessageType.TEXT,
|
|
266
|
-
direction: MessageDirection.OUTGOING,
|
|
267
|
-
status: MessageStatus.DELIVERED,
|
|
268
|
-
timestamp: new Date(Date.now() - 5000),
|
|
269
|
-
seeker: new Uint8Array(32).fill(4),
|
|
270
|
-
encryptedMessage: new Uint8Array(64).fill(4),
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Simulate renewal
|
|
274
|
-
const resetCount = await simulateRenewReset(
|
|
275
|
-
alice.session.userIdEncoded,
|
|
276
|
-
bob.session.userIdEncoded
|
|
277
|
-
);
|
|
278
|
-
expect(resetCount).toBe(3); // SENT, SENDING, FAILED
|
|
279
|
-
|
|
280
|
-
// Verify reset
|
|
281
|
-
expect((await db.messages.get(sentId))?.status).toBe(
|
|
282
|
-
MessageStatus.WAITING_SESSION
|
|
283
|
-
);
|
|
284
|
-
expect((await db.messages.get(sendingId))?.status).toBe(
|
|
285
|
-
MessageStatus.WAITING_SESSION
|
|
286
|
-
);
|
|
287
|
-
expect((await db.messages.get(failedId))?.status).toBe(
|
|
288
|
-
MessageStatus.WAITING_SESSION
|
|
289
|
-
);
|
|
290
|
-
expect((await db.messages.get(deliveredId))?.status).toBe(
|
|
291
|
-
MessageStatus.DELIVERED
|
|
292
|
-
);
|
|
293
|
-
|
|
294
|
-
// Process waiting messages
|
|
295
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
296
|
-
bob.session.userIdEncoded
|
|
297
|
-
);
|
|
298
|
-
expect(sentCount).toBe(3);
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
describe('Edge cases', () => {
|
|
303
|
-
it('should preserve message order when resending multiple messages', async () => {
|
|
304
|
-
// Establish session
|
|
305
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
306
|
-
bob.session.ourPk
|
|
307
|
-
);
|
|
308
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
309
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
310
|
-
alice.session.ourPk
|
|
311
|
-
);
|
|
312
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
313
|
-
|
|
314
|
-
await db.discussions.add({
|
|
315
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
316
|
-
contactUserId: bob.session.userIdEncoded,
|
|
317
|
-
direction: DiscussionDirection.INITIATED,
|
|
318
|
-
status: DiscussionStatus.ACTIVE,
|
|
319
|
-
unreadCount: 0,
|
|
320
|
-
createdAt: new Date(),
|
|
321
|
-
updatedAt: new Date(),
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
// Add 3 messages in order
|
|
325
|
-
const msg1Id = await db.messages.add({
|
|
326
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
327
|
-
contactUserId: bob.session.userIdEncoded,
|
|
328
|
-
content: 'Message 1',
|
|
329
|
-
type: MessageType.TEXT,
|
|
330
|
-
direction: MessageDirection.OUTGOING,
|
|
331
|
-
status: MessageStatus.SENT,
|
|
332
|
-
timestamp: new Date(Date.now() - 3000),
|
|
333
|
-
seeker: new Uint8Array(32).fill(1),
|
|
334
|
-
encryptedMessage: new Uint8Array(64).fill(1),
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
const msg2Id = await db.messages.add({
|
|
338
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
339
|
-
contactUserId: bob.session.userIdEncoded,
|
|
340
|
-
content: 'Message 2',
|
|
341
|
-
type: MessageType.TEXT,
|
|
342
|
-
direction: MessageDirection.OUTGOING,
|
|
343
|
-
status: MessageStatus.SENT,
|
|
344
|
-
timestamp: new Date(Date.now() - 2000),
|
|
345
|
-
seeker: new Uint8Array(32).fill(2),
|
|
346
|
-
encryptedMessage: new Uint8Array(64).fill(2),
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
const msg3Id = await db.messages.add({
|
|
350
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
351
|
-
contactUserId: bob.session.userIdEncoded,
|
|
352
|
-
content: 'Message 3',
|
|
353
|
-
type: MessageType.TEXT,
|
|
354
|
-
direction: MessageDirection.OUTGOING,
|
|
355
|
-
status: MessageStatus.SENT,
|
|
356
|
-
timestamp: new Date(Date.now() - 1000),
|
|
357
|
-
seeker: new Uint8Array(32).fill(3),
|
|
358
|
-
encryptedMessage: new Uint8Array(64).fill(3),
|
|
359
|
-
});
|
|
360
|
-
|
|
361
|
-
await simulateRenewReset(
|
|
362
|
-
alice.session.userIdEncoded,
|
|
363
|
-
bob.session.userIdEncoded
|
|
364
|
-
);
|
|
365
|
-
|
|
366
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
367
|
-
bob.session.userIdEncoded
|
|
368
|
-
);
|
|
369
|
-
expect(sentCount).toBe(3);
|
|
370
|
-
|
|
371
|
-
// All should be SENT now
|
|
372
|
-
expect((await db.messages.get(msg1Id))?.status).toBe(MessageStatus.SENT);
|
|
373
|
-
expect((await db.messages.get(msg2Id))?.status).toBe(MessageStatus.SENT);
|
|
374
|
-
expect((await db.messages.get(msg3Id))?.status).toBe(MessageStatus.SENT);
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
it('should not resend incoming messages on renewal', async () => {
|
|
378
|
-
// Establish session
|
|
379
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
380
|
-
bob.session.ourPk
|
|
381
|
-
);
|
|
382
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
383
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
384
|
-
alice.session.ourPk
|
|
385
|
-
);
|
|
386
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
387
|
-
|
|
388
|
-
await db.discussions.add({
|
|
389
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
390
|
-
contactUserId: bob.session.userIdEncoded,
|
|
391
|
-
direction: DiscussionDirection.INITIATED,
|
|
392
|
-
status: DiscussionStatus.ACTIVE,
|
|
393
|
-
unreadCount: 0,
|
|
394
|
-
createdAt: new Date(),
|
|
395
|
-
updatedAt: new Date(),
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
const outgoingId = await db.messages.add({
|
|
399
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
400
|
-
contactUserId: bob.session.userIdEncoded,
|
|
401
|
-
content: 'Outgoing message',
|
|
402
|
-
type: MessageType.TEXT,
|
|
403
|
-
direction: MessageDirection.OUTGOING,
|
|
404
|
-
status: MessageStatus.SENT,
|
|
405
|
-
timestamp: new Date(),
|
|
406
|
-
seeker: new Uint8Array(32).fill(1),
|
|
407
|
-
encryptedMessage: new Uint8Array(64).fill(1),
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
const incomingId = await db.messages.add({
|
|
411
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
412
|
-
contactUserId: bob.session.userIdEncoded,
|
|
413
|
-
content: 'Incoming message from Bob',
|
|
414
|
-
type: MessageType.TEXT,
|
|
415
|
-
direction: MessageDirection.INCOMING,
|
|
416
|
-
status: MessageStatus.DELIVERED,
|
|
417
|
-
timestamp: new Date(),
|
|
418
|
-
seeker: new Uint8Array(32).fill(2),
|
|
419
|
-
encryptedMessage: new Uint8Array(64).fill(2),
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
const resetCount = await simulateRenewReset(
|
|
423
|
-
alice.session.userIdEncoded,
|
|
424
|
-
bob.session.userIdEncoded
|
|
425
|
-
);
|
|
426
|
-
expect(resetCount).toBe(1); // Only outgoing
|
|
427
|
-
|
|
428
|
-
expect((await db.messages.get(outgoingId))?.status).toBe(
|
|
429
|
-
MessageStatus.WAITING_SESSION
|
|
430
|
-
);
|
|
431
|
-
expect((await db.messages.get(incomingId))?.status).toBe(
|
|
432
|
-
MessageStatus.DELIVERED
|
|
433
|
-
);
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
// ============================================================================
|
|
439
|
-
// WAITING_SESSION after accept
|
|
440
|
-
// ============================================================================
|
|
441
|
-
|
|
442
|
-
describe('WAITING_SESSION messages after peer acceptance', () => {
|
|
443
|
-
let alice: TestSessionData;
|
|
444
|
-
let bob: TestSessionData;
|
|
445
|
-
let mockProtocol: MockMessageProtocol;
|
|
446
|
-
let events: GossipSdkEvents;
|
|
447
|
-
let messageService: MessageService;
|
|
448
|
-
let discussionService: DiscussionService;
|
|
449
|
-
let announcementService: AnnouncementService;
|
|
450
|
-
|
|
451
|
-
function createUserProfile(userId: string) {
|
|
452
|
-
return {
|
|
453
|
-
userId,
|
|
454
|
-
username: 'test',
|
|
455
|
-
security: {
|
|
456
|
-
encKeySalt: new Uint8Array(),
|
|
457
|
-
authMethod: 'password' as const,
|
|
458
|
-
mnemonicBackup: {
|
|
459
|
-
encryptedMnemonic: new Uint8Array(),
|
|
460
|
-
createdAt: new Date(),
|
|
461
|
-
backedUp: false,
|
|
462
|
-
},
|
|
463
|
-
},
|
|
464
|
-
session: new Uint8Array(),
|
|
465
|
-
status: 'online' as const,
|
|
466
|
-
lastSeen: new Date(),
|
|
467
|
-
createdAt: new Date(),
|
|
468
|
-
updatedAt: new Date(),
|
|
469
|
-
};
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
beforeEach(async () => {
|
|
473
|
-
if (!db.isOpen()) {
|
|
474
|
-
await db.open();
|
|
475
|
-
}
|
|
476
|
-
await Promise.all(db.tables.map(table => table.clear()));
|
|
477
|
-
|
|
478
|
-
alice = await createTestSession(`alice-waiting-${Date.now()}`);
|
|
479
|
-
bob = await createTestSession(`bob-waiting-${Date.now()}`);
|
|
480
|
-
|
|
481
|
-
mockProtocol = new MockMessageProtocol();
|
|
482
|
-
events = {};
|
|
483
|
-
|
|
484
|
-
await db.contacts.add({
|
|
485
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
486
|
-
userId: bob.session.userIdEncoded,
|
|
487
|
-
name: 'Bob',
|
|
488
|
-
publicKeys: bob.session.ourPk.to_bytes(),
|
|
489
|
-
isOnline: false,
|
|
490
|
-
lastSeen: new Date(),
|
|
491
|
-
createdAt: new Date(),
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
await db.userProfile.put(createUserProfile(alice.session.userIdEncoded));
|
|
495
|
-
|
|
496
|
-
announcementService = new AnnouncementService(
|
|
497
|
-
db,
|
|
498
|
-
mockProtocol,
|
|
499
|
-
alice.session,
|
|
500
|
-
events
|
|
501
|
-
);
|
|
502
|
-
discussionService = new DiscussionService(
|
|
503
|
-
db,
|
|
504
|
-
announcementService,
|
|
505
|
-
alice.session
|
|
506
|
-
);
|
|
507
|
-
messageService = new MessageService(
|
|
508
|
-
db,
|
|
509
|
-
mockProtocol,
|
|
510
|
-
alice.session,
|
|
511
|
-
discussionService,
|
|
512
|
-
events
|
|
513
|
-
);
|
|
514
|
-
});
|
|
515
|
-
|
|
516
|
-
afterEach(() => {
|
|
517
|
-
cleanupTestSession(alice);
|
|
518
|
-
cleanupTestSession(bob);
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
it('messages queued before acceptance should send after session becomes Active', async () => {
|
|
522
|
-
// Alice initiates session to Bob (SelfRequested state)
|
|
523
|
-
await alice.session.establishOutgoingSession(bob.session.ourPk);
|
|
524
|
-
|
|
525
|
-
expect(alice.session.peerSessionStatus(bob.session.userId)).toBe(
|
|
526
|
-
SessionStatus.SelfRequested
|
|
527
|
-
);
|
|
528
|
-
|
|
529
|
-
// Create pending discussion
|
|
530
|
-
await db.discussions.add({
|
|
531
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
532
|
-
contactUserId: bob.session.userIdEncoded,
|
|
533
|
-
direction: DiscussionDirection.INITIATED,
|
|
534
|
-
status: DiscussionStatus.PENDING,
|
|
535
|
-
unreadCount: 0,
|
|
536
|
-
createdAt: new Date(),
|
|
537
|
-
updatedAt: new Date(),
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
// Alice tries to send a message before Bob accepts
|
|
541
|
-
const message = {
|
|
542
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
543
|
-
contactUserId: bob.session.userIdEncoded,
|
|
544
|
-
content: 'Hello Bob! (sent before you accepted)',
|
|
545
|
-
type: MessageType.TEXT,
|
|
546
|
-
direction: MessageDirection.OUTGOING,
|
|
547
|
-
status: MessageStatus.SENDING,
|
|
548
|
-
timestamp: new Date(),
|
|
549
|
-
};
|
|
550
|
-
|
|
551
|
-
const sendResult = await messageService.sendMessage(message);
|
|
552
|
-
|
|
553
|
-
// Message should be queued as WAITING_SESSION
|
|
554
|
-
expect(sendResult.success).toBe(true);
|
|
555
|
-
expect(sendResult.message?.status).toBe(MessageStatus.WAITING_SESSION);
|
|
556
|
-
|
|
557
|
-
const queuedMessageId = sendResult.message!.id!;
|
|
558
|
-
let dbMessage = await db.messages.get(queuedMessageId);
|
|
559
|
-
expect(dbMessage?.status).toBe(MessageStatus.WAITING_SESSION);
|
|
560
|
-
|
|
561
|
-
// Now Bob accepts - establish the full handshake
|
|
562
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
563
|
-
bob.session.ourPk
|
|
564
|
-
);
|
|
565
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
566
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
567
|
-
alice.session.ourPk
|
|
568
|
-
);
|
|
569
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
570
|
-
|
|
571
|
-
// Session should now be active
|
|
572
|
-
expect(alice.session.peerSessionStatus(bob.session.userId)).toBe(
|
|
573
|
-
SessionStatus.Active
|
|
574
|
-
);
|
|
575
|
-
|
|
576
|
-
// Update discussion to active
|
|
577
|
-
const discussion = await db.getDiscussionByOwnerAndContact(
|
|
578
|
-
alice.session.userIdEncoded,
|
|
579
|
-
bob.session.userIdEncoded
|
|
580
|
-
);
|
|
581
|
-
await db.discussions.update(discussion!.id!, {
|
|
582
|
-
status: DiscussionStatus.ACTIVE,
|
|
583
|
-
updatedAt: new Date(),
|
|
584
|
-
});
|
|
585
|
-
|
|
586
|
-
// Process waiting messages
|
|
587
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
588
|
-
bob.session.userIdEncoded
|
|
589
|
-
);
|
|
590
|
-
|
|
591
|
-
expect(sentCount).toBe(1);
|
|
592
|
-
dbMessage = await db.messages.get(queuedMessageId);
|
|
593
|
-
expect(dbMessage?.status).toBe(MessageStatus.SENT);
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
it('processWaitingMessages correctly sends messages when called manually', async () => {
|
|
597
|
-
// Establish full session
|
|
598
|
-
const aliceAnnouncement = await alice.session.establishOutgoingSession(
|
|
599
|
-
bob.session.ourPk
|
|
600
|
-
);
|
|
601
|
-
await bob.session.feedIncomingAnnouncement(aliceAnnouncement);
|
|
602
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
603
|
-
alice.session.ourPk
|
|
604
|
-
);
|
|
605
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
606
|
-
|
|
607
|
-
await db.discussions.add({
|
|
608
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
609
|
-
contactUserId: bob.session.userIdEncoded,
|
|
610
|
-
direction: DiscussionDirection.INITIATED,
|
|
611
|
-
status: DiscussionStatus.ACTIVE,
|
|
612
|
-
unreadCount: 0,
|
|
613
|
-
createdAt: new Date(),
|
|
614
|
-
updatedAt: new Date(),
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
// Add a WAITING_SESSION message directly
|
|
618
|
-
const messageId = await db.messages.add({
|
|
619
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
620
|
-
contactUserId: bob.session.userIdEncoded,
|
|
621
|
-
content: 'Stuck message',
|
|
622
|
-
type: MessageType.TEXT,
|
|
623
|
-
direction: MessageDirection.OUTGOING,
|
|
624
|
-
status: MessageStatus.WAITING_SESSION,
|
|
625
|
-
timestamp: new Date(),
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
let dbMessage = await db.messages.get(messageId);
|
|
629
|
-
expect(dbMessage?.status).toBe(MessageStatus.WAITING_SESSION);
|
|
630
|
-
|
|
631
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
632
|
-
bob.session.userIdEncoded
|
|
633
|
-
);
|
|
634
|
-
|
|
635
|
-
expect(sentCount).toBe(1);
|
|
636
|
-
dbMessage = await db.messages.get(messageId);
|
|
637
|
-
expect(dbMessage?.status).toBe(MessageStatus.SENT);
|
|
638
|
-
});
|
|
639
|
-
|
|
640
|
-
it('full flow: Alice initiates, sends message, Bob accepts, message delivered', async () => {
|
|
641
|
-
// Create pending discussion
|
|
642
|
-
const aliceBobContact = {
|
|
643
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
644
|
-
userId: bob.session.userIdEncoded,
|
|
645
|
-
name: 'Bob',
|
|
646
|
-
publicKeys: bob.session.ourPk.to_bytes(),
|
|
647
|
-
isOnline: false,
|
|
648
|
-
lastSeen: new Date(),
|
|
649
|
-
createdAt: new Date(),
|
|
650
|
-
};
|
|
651
|
-
|
|
652
|
-
// Alice initiates discussion
|
|
653
|
-
const { discussionId } =
|
|
654
|
-
await discussionService.initialize(aliceBobContact);
|
|
655
|
-
|
|
656
|
-
const discussion = await db.discussions.get(discussionId);
|
|
657
|
-
expect(discussion?.status).toBe(DiscussionStatus.PENDING);
|
|
658
|
-
|
|
659
|
-
// Alice sends a message (will be queued)
|
|
660
|
-
const sendResult = await messageService.sendMessage({
|
|
661
|
-
ownerUserId: alice.session.userIdEncoded,
|
|
662
|
-
contactUserId: bob.session.userIdEncoded,
|
|
663
|
-
content: 'Hello Bob!',
|
|
664
|
-
type: MessageType.TEXT,
|
|
665
|
-
direction: MessageDirection.OUTGOING,
|
|
666
|
-
status: MessageStatus.SENDING,
|
|
667
|
-
timestamp: new Date(),
|
|
668
|
-
});
|
|
669
|
-
|
|
670
|
-
expect(sendResult.success).toBe(true);
|
|
671
|
-
expect(sendResult.message?.status).toBe(MessageStatus.WAITING_SESSION);
|
|
672
|
-
|
|
673
|
-
// Bob receives Alice's announcement and accepts
|
|
674
|
-
const storedAnnouncements = mockProtocol.getStoredAnnouncements();
|
|
675
|
-
expect(storedAnnouncements.length).toBe(1);
|
|
676
|
-
|
|
677
|
-
// Bob processes the announcement
|
|
678
|
-
await bob.session.feedIncomingAnnouncement(storedAnnouncements[0].data);
|
|
679
|
-
|
|
680
|
-
// Bob sends acceptance
|
|
681
|
-
const bobAnnouncement = await bob.session.establishOutgoingSession(
|
|
682
|
-
alice.session.ourPk
|
|
683
|
-
);
|
|
684
|
-
|
|
685
|
-
// Alice receives Bob's acceptance
|
|
686
|
-
await alice.session.feedIncomingAnnouncement(bobAnnouncement);
|
|
687
|
-
|
|
688
|
-
// Session should now be active
|
|
689
|
-
expect(alice.session.peerSessionStatus(bob.session.userId)).toBe(
|
|
690
|
-
SessionStatus.Active
|
|
691
|
-
);
|
|
692
|
-
|
|
693
|
-
// Update discussion status
|
|
694
|
-
await db.discussions.update(discussionId, {
|
|
695
|
-
status: DiscussionStatus.ACTIVE,
|
|
696
|
-
});
|
|
697
|
-
|
|
698
|
-
// Process waiting messages
|
|
699
|
-
const sentCount = await messageService.processWaitingMessages(
|
|
700
|
-
bob.session.userIdEncoded
|
|
701
|
-
);
|
|
702
|
-
|
|
703
|
-
expect(sentCount).toBe(1);
|
|
704
|
-
|
|
705
|
-
const finalMessage = await db.messages.get(sendResult.message!.id!);
|
|
706
|
-
expect(finalMessage?.status).toBe(MessageStatus.SENT);
|
|
707
|
-
});
|
|
708
|
-
});
|