@massalabs/gossip-sdk 0.0.1 → 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,199 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RefreshService tests
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
6
|
-
import { RefreshService } from '../../src/services/refresh';
|
|
7
|
-
import { MessageService } from '../../src/services/message';
|
|
8
|
-
import { db, MessageType, DiscussionDirection } from '../../src/db';
|
|
9
|
-
import type { SessionModule } from '../../src/wasm/session';
|
|
10
|
-
import { encodeUserId, decodeUserId } from '../../src/utils/userId';
|
|
11
|
-
import { SessionStatus } from '../../src/assets/generated/wasm/gossip_wasm';
|
|
12
|
-
import { DiscussionStatus } from '../../src/db';
|
|
13
|
-
|
|
14
|
-
const REFRESH_OWNER_USER_ID = encodeUserId(new Uint8Array(32).fill(11));
|
|
15
|
-
const REFRESH_CONTACT_USER_ID = encodeUserId(new Uint8Array(32).fill(12));
|
|
16
|
-
|
|
17
|
-
function createRefreshSession(
|
|
18
|
-
sessionStatus: SessionStatus = SessionStatus.Active
|
|
19
|
-
): SessionModule {
|
|
20
|
-
return {
|
|
21
|
-
peerSessionStatus: vi.fn().mockReturnValue(sessionStatus),
|
|
22
|
-
sendMessage: vi.fn(),
|
|
23
|
-
receiveMessage: vi.fn(),
|
|
24
|
-
refresh: vi.fn().mockResolvedValue([]),
|
|
25
|
-
receiveAnnouncement: vi.fn(),
|
|
26
|
-
establishOutgoingSession: vi.fn(),
|
|
27
|
-
toEncryptedBlob: vi.fn(),
|
|
28
|
-
userIdEncoded: REFRESH_OWNER_USER_ID,
|
|
29
|
-
userIdRaw: new Uint8Array(32).fill(11),
|
|
30
|
-
userId: new Uint8Array(32).fill(11),
|
|
31
|
-
getMessageBoardReadKeys: vi.fn().mockReturnValue([]),
|
|
32
|
-
cleanup: vi.fn(),
|
|
33
|
-
} as unknown as SessionModule;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function createRefreshMessageService(): MessageService {
|
|
37
|
-
return {
|
|
38
|
-
sendMessage: vi.fn().mockResolvedValue({ success: true }),
|
|
39
|
-
} as unknown as MessageService;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe('RefreshService', () => {
|
|
43
|
-
beforeEach(async () => {
|
|
44
|
-
if (!db.isOpen()) {
|
|
45
|
-
await db.open();
|
|
46
|
-
}
|
|
47
|
-
await Promise.all(db.tables.map(table => table.clear()));
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
describe('handleSessionRefresh', () => {
|
|
51
|
-
it('should throw error when active discussion has PeerRequested status', async () => {
|
|
52
|
-
const mockSession = createRefreshSession(SessionStatus.PeerRequested);
|
|
53
|
-
const mockMessageService = createRefreshMessageService();
|
|
54
|
-
const refreshService = new RefreshService(
|
|
55
|
-
db,
|
|
56
|
-
mockMessageService,
|
|
57
|
-
mockSession
|
|
58
|
-
);
|
|
59
|
-
|
|
60
|
-
const activeDiscussion = {
|
|
61
|
-
id: 1,
|
|
62
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
63
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
64
|
-
direction: DiscussionDirection.INITIATED,
|
|
65
|
-
status: DiscussionStatus.ACTIVE,
|
|
66
|
-
unreadCount: 0,
|
|
67
|
-
createdAt: new Date(),
|
|
68
|
-
updatedAt: new Date(),
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
await expect(
|
|
72
|
-
refreshService.handleSessionRefresh([activeDiscussion])
|
|
73
|
-
).rejects.toThrow(
|
|
74
|
-
/Unexpected PeerRequested status for active discussion/
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should trigger onSessionRenewalNeeded when session is Killed', async () => {
|
|
79
|
-
const mockSession = createRefreshSession(SessionStatus.Killed);
|
|
80
|
-
const mockMessageService = createRefreshMessageService();
|
|
81
|
-
const onSessionRenewalNeeded = vi.fn();
|
|
82
|
-
const refreshService = new RefreshService(
|
|
83
|
-
db,
|
|
84
|
-
mockMessageService,
|
|
85
|
-
mockSession,
|
|
86
|
-
{ onSessionRenewalNeeded }
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const activeDiscussion = {
|
|
90
|
-
id: 1,
|
|
91
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
92
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
93
|
-
direction: DiscussionDirection.INITIATED,
|
|
94
|
-
status: DiscussionStatus.ACTIVE,
|
|
95
|
-
unreadCount: 0,
|
|
96
|
-
createdAt: new Date(),
|
|
97
|
-
updatedAt: new Date(),
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
await refreshService.handleSessionRefresh([activeDiscussion]);
|
|
101
|
-
|
|
102
|
-
expect(onSessionRenewalNeeded).toHaveBeenCalledTimes(1);
|
|
103
|
-
expect(onSessionRenewalNeeded).toHaveBeenCalledWith(
|
|
104
|
-
REFRESH_CONTACT_USER_ID
|
|
105
|
-
);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('should trigger onSessionRenewalNeeded when session is NoSession', async () => {
|
|
109
|
-
const mockSession = createRefreshSession(SessionStatus.NoSession);
|
|
110
|
-
const mockMessageService = createRefreshMessageService();
|
|
111
|
-
const onSessionRenewalNeeded = vi.fn();
|
|
112
|
-
const refreshService = new RefreshService(
|
|
113
|
-
db,
|
|
114
|
-
mockMessageService,
|
|
115
|
-
mockSession,
|
|
116
|
-
{ onSessionRenewalNeeded }
|
|
117
|
-
);
|
|
118
|
-
|
|
119
|
-
const activeDiscussion = {
|
|
120
|
-
id: 1,
|
|
121
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
122
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
123
|
-
direction: DiscussionDirection.INITIATED,
|
|
124
|
-
status: DiscussionStatus.ACTIVE,
|
|
125
|
-
unreadCount: 0,
|
|
126
|
-
createdAt: new Date(),
|
|
127
|
-
updatedAt: new Date(),
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
await refreshService.handleSessionRefresh([activeDiscussion]);
|
|
131
|
-
|
|
132
|
-
expect(onSessionRenewalNeeded).toHaveBeenCalledTimes(1);
|
|
133
|
-
expect(onSessionRenewalNeeded).toHaveBeenCalledWith(
|
|
134
|
-
REFRESH_CONTACT_USER_ID
|
|
135
|
-
);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('should send keep-alive message when session is Active and peer needs it', async () => {
|
|
139
|
-
const mockSession = createRefreshSession(SessionStatus.Active);
|
|
140
|
-
mockSession.refresh = vi
|
|
141
|
-
.fn()
|
|
142
|
-
.mockResolvedValue([decodeUserId(REFRESH_CONTACT_USER_ID)]);
|
|
143
|
-
const mockMessageService = createRefreshMessageService();
|
|
144
|
-
const refreshService = new RefreshService(
|
|
145
|
-
db,
|
|
146
|
-
mockMessageService,
|
|
147
|
-
mockSession
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const activeDiscussion = {
|
|
151
|
-
id: 1,
|
|
152
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
153
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
154
|
-
direction: DiscussionDirection.INITIATED,
|
|
155
|
-
status: DiscussionStatus.ACTIVE,
|
|
156
|
-
unreadCount: 0,
|
|
157
|
-
createdAt: new Date(),
|
|
158
|
-
updatedAt: new Date(),
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
await refreshService.handleSessionRefresh([activeDiscussion]);
|
|
162
|
-
|
|
163
|
-
expect(mockMessageService.sendMessage).toHaveBeenCalledTimes(1);
|
|
164
|
-
expect(mockMessageService.sendMessage).toHaveBeenCalledWith(
|
|
165
|
-
expect.objectContaining({
|
|
166
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
167
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
168
|
-
type: MessageType.KEEP_ALIVE,
|
|
169
|
-
})
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should not send keep-alive when session is Active but peer does not need it', async () => {
|
|
174
|
-
const mockSession = createRefreshSession(SessionStatus.Active);
|
|
175
|
-
mockSession.refresh = vi.fn().mockResolvedValue([]);
|
|
176
|
-
const mockMessageService = createRefreshMessageService();
|
|
177
|
-
const refreshService = new RefreshService(
|
|
178
|
-
db,
|
|
179
|
-
mockMessageService,
|
|
180
|
-
mockSession
|
|
181
|
-
);
|
|
182
|
-
|
|
183
|
-
const activeDiscussion = {
|
|
184
|
-
id: 1,
|
|
185
|
-
ownerUserId: REFRESH_OWNER_USER_ID,
|
|
186
|
-
contactUserId: REFRESH_CONTACT_USER_ID,
|
|
187
|
-
direction: DiscussionDirection.INITIATED,
|
|
188
|
-
status: DiscussionStatus.ACTIVE,
|
|
189
|
-
unreadCount: 0,
|
|
190
|
-
createdAt: new Date(),
|
|
191
|
-
updatedAt: new Date(),
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
await refreshService.handleSessionRefresh([activeDiscussion]);
|
|
195
|
-
|
|
196
|
-
expect(mockMessageService.sendMessage).not.toHaveBeenCalled();
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
});
|
|
@@ -1,349 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Session status logic tests
|
|
3
|
-
*
|
|
4
|
-
* Auto-accept logic, discussion status after renewal, reply target fallback,
|
|
5
|
-
* and message FIFO ordering.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
9
|
-
import {
|
|
10
|
-
GossipDatabase,
|
|
11
|
-
MessageType,
|
|
12
|
-
MessageDirection,
|
|
13
|
-
MessageStatus,
|
|
14
|
-
DiscussionStatus,
|
|
15
|
-
DiscussionDirection,
|
|
16
|
-
} from '../../src/db';
|
|
17
|
-
import { encodeUserId } from '../../src/utils/userId';
|
|
18
|
-
import { SessionStatus } from '../../src/assets/generated/wasm/gossip_wasm';
|
|
19
|
-
|
|
20
|
-
// ============================================================================
|
|
21
|
-
// Auto-accept + renew status logic
|
|
22
|
-
// ============================================================================
|
|
23
|
-
|
|
24
|
-
describe('Auto-Accept Logic', () => {
|
|
25
|
-
interface ProcessAnnouncementParams {
|
|
26
|
-
isNewContact: boolean;
|
|
27
|
-
sessionStatus: SessionStatus;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function shouldAutoAccept(params: ProcessAnnouncementParams): boolean {
|
|
31
|
-
return (
|
|
32
|
-
params.sessionStatus === SessionStatus.PeerRequested &&
|
|
33
|
-
!params.isNewContact
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
describe('New contact requests', () => {
|
|
38
|
-
it('should NOT auto-accept for new contact with PeerRequested status', () => {
|
|
39
|
-
const result = shouldAutoAccept({
|
|
40
|
-
isNewContact: true,
|
|
41
|
-
sessionStatus: SessionStatus.PeerRequested,
|
|
42
|
-
});
|
|
43
|
-
expect(result).toBe(false);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should NOT auto-accept for new contact with any status', () => {
|
|
47
|
-
const statuses = [
|
|
48
|
-
SessionStatus.Active,
|
|
49
|
-
SessionStatus.NoSession,
|
|
50
|
-
SessionStatus.SelfRequested,
|
|
51
|
-
SessionStatus.UnknownPeer,
|
|
52
|
-
SessionStatus.Killed,
|
|
53
|
-
SessionStatus.Saturated,
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
for (const status of statuses) {
|
|
57
|
-
const result = shouldAutoAccept({
|
|
58
|
-
isNewContact: true,
|
|
59
|
-
sessionStatus: status,
|
|
60
|
-
});
|
|
61
|
-
expect(result).toBe(false);
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('Existing contact session recovery', () => {
|
|
67
|
-
it('should auto-accept for existing contact with PeerRequested status', () => {
|
|
68
|
-
const result = shouldAutoAccept({
|
|
69
|
-
isNewContact: false,
|
|
70
|
-
sessionStatus: SessionStatus.PeerRequested,
|
|
71
|
-
});
|
|
72
|
-
expect(result).toBe(true);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('should NOT auto-accept for existing contact with non-PeerRequested status', () => {
|
|
76
|
-
const statuses = [
|
|
77
|
-
SessionStatus.Active,
|
|
78
|
-
SessionStatus.NoSession,
|
|
79
|
-
SessionStatus.SelfRequested,
|
|
80
|
-
SessionStatus.UnknownPeer,
|
|
81
|
-
SessionStatus.Killed,
|
|
82
|
-
SessionStatus.Saturated,
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
for (const status of statuses) {
|
|
86
|
-
const result = shouldAutoAccept({
|
|
87
|
-
isNewContact: false,
|
|
88
|
-
sessionStatus: status,
|
|
89
|
-
});
|
|
90
|
-
expect(result).toBe(false);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
describe('Discussion Status After Renewal', () => {
|
|
97
|
-
enum RenewalDiscussionStatus {
|
|
98
|
-
PENDING = 'pending',
|
|
99
|
-
ACTIVE = 'active',
|
|
100
|
-
BROKEN = 'broken',
|
|
101
|
-
SEND_FAILED = 'send_failed',
|
|
102
|
-
RECONNECTING = 'reconnecting',
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
interface RenewResult {
|
|
106
|
-
success: boolean;
|
|
107
|
-
sessionStatus: SessionStatus;
|
|
108
|
-
previousDiscussionStatus: RenewalDiscussionStatus;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function getStatusAfterRenewal(result: RenewResult): RenewalDiscussionStatus {
|
|
112
|
-
if (!result.success) {
|
|
113
|
-
return RenewalDiscussionStatus.SEND_FAILED;
|
|
114
|
-
} else if (result.sessionStatus === SessionStatus.Active) {
|
|
115
|
-
return RenewalDiscussionStatus.ACTIVE;
|
|
116
|
-
} else if (
|
|
117
|
-
result.previousDiscussionStatus === RenewalDiscussionStatus.ACTIVE
|
|
118
|
-
) {
|
|
119
|
-
return RenewalDiscussionStatus.RECONNECTING;
|
|
120
|
-
} else {
|
|
121
|
-
return RenewalDiscussionStatus.PENDING;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
describe('True renewal (previously ACTIVE discussion)', () => {
|
|
126
|
-
it('should set RECONNECTING when session is SelfRequested', () => {
|
|
127
|
-
const status = getStatusAfterRenewal({
|
|
128
|
-
success: true,
|
|
129
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
130
|
-
previousDiscussionStatus: RenewalDiscussionStatus.ACTIVE,
|
|
131
|
-
});
|
|
132
|
-
expect(status).toBe(RenewalDiscussionStatus.RECONNECTING);
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
it('should set ACTIVE when session is already Active', () => {
|
|
136
|
-
const status = getStatusAfterRenewal({
|
|
137
|
-
success: true,
|
|
138
|
-
sessionStatus: SessionStatus.Active,
|
|
139
|
-
previousDiscussionStatus: RenewalDiscussionStatus.ACTIVE,
|
|
140
|
-
});
|
|
141
|
-
expect(status).toBe(RenewalDiscussionStatus.ACTIVE);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should set SEND_FAILED if send fails', () => {
|
|
145
|
-
const status = getStatusAfterRenewal({
|
|
146
|
-
success: false,
|
|
147
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
148
|
-
previousDiscussionStatus: RenewalDiscussionStatus.ACTIVE,
|
|
149
|
-
});
|
|
150
|
-
expect(status).toBe(RenewalDiscussionStatus.SEND_FAILED);
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('First contact retry (previously PENDING/SEND_FAILED discussion)', () => {
|
|
155
|
-
it('should set PENDING when session is SelfRequested (from PENDING)', () => {
|
|
156
|
-
const status = getStatusAfterRenewal({
|
|
157
|
-
success: true,
|
|
158
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
159
|
-
previousDiscussionStatus: RenewalDiscussionStatus.PENDING,
|
|
160
|
-
});
|
|
161
|
-
expect(status).toBe(RenewalDiscussionStatus.PENDING);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('should set PENDING when session is SelfRequested (from SEND_FAILED)', () => {
|
|
165
|
-
const status = getStatusAfterRenewal({
|
|
166
|
-
success: true,
|
|
167
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
168
|
-
previousDiscussionStatus: RenewalDiscussionStatus.SEND_FAILED,
|
|
169
|
-
});
|
|
170
|
-
expect(status).toBe(RenewalDiscussionStatus.PENDING);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
it('should set ACTIVE when session is already Active (peer responded)', () => {
|
|
174
|
-
const status = getStatusAfterRenewal({
|
|
175
|
-
success: true,
|
|
176
|
-
sessionStatus: SessionStatus.Active,
|
|
177
|
-
previousDiscussionStatus: RenewalDiscussionStatus.PENDING,
|
|
178
|
-
});
|
|
179
|
-
expect(status).toBe(RenewalDiscussionStatus.ACTIVE);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it('should set SEND_FAILED if send fails', () => {
|
|
183
|
-
const status = getStatusAfterRenewal({
|
|
184
|
-
success: false,
|
|
185
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
186
|
-
previousDiscussionStatus: RenewalDiscussionStatus.PENDING,
|
|
187
|
-
});
|
|
188
|
-
expect(status).toBe(RenewalDiscussionStatus.SEND_FAILED);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
describe('Edge case: renewal from BROKEN status', () => {
|
|
193
|
-
it('should set PENDING when renewing from BROKEN (no previous active session)', () => {
|
|
194
|
-
const status = getStatusAfterRenewal({
|
|
195
|
-
success: true,
|
|
196
|
-
sessionStatus: SessionStatus.SelfRequested,
|
|
197
|
-
previousDiscussionStatus: RenewalDiscussionStatus.BROKEN,
|
|
198
|
-
});
|
|
199
|
-
expect(status).toBe(RenewalDiscussionStatus.PENDING);
|
|
200
|
-
});
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// ============================================================================
|
|
205
|
-
// Message FIFO Ordering during Resend
|
|
206
|
-
// ============================================================================
|
|
207
|
-
|
|
208
|
-
const GAP_OWNER_USER_ID = encodeUserId(new Uint8Array(32).fill(1));
|
|
209
|
-
const GAP_CONTACT_USER_ID = encodeUserId(new Uint8Array(32).fill(2));
|
|
210
|
-
const GAP_SEEKER_SIZE = 34;
|
|
211
|
-
|
|
212
|
-
describe('Message FIFO Ordering during Resend', () => {
|
|
213
|
-
let testDb: GossipDatabase;
|
|
214
|
-
|
|
215
|
-
beforeEach(async () => {
|
|
216
|
-
testDb = new GossipDatabase();
|
|
217
|
-
await testDb.open();
|
|
218
|
-
await Promise.all(testDb.tables.map(table => table.clear()));
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
it('should process messages in timestamp order (oldest first)', async () => {
|
|
222
|
-
const now = Date.now();
|
|
223
|
-
await testDb.messages.add({
|
|
224
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
225
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
226
|
-
content: 'Message 3 (newest)',
|
|
227
|
-
type: MessageType.TEXT,
|
|
228
|
-
direction: MessageDirection.OUTGOING,
|
|
229
|
-
status: MessageStatus.WAITING_SESSION,
|
|
230
|
-
timestamp: new Date(now),
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
await testDb.messages.add({
|
|
234
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
235
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
236
|
-
content: 'Message 1 (oldest)',
|
|
237
|
-
type: MessageType.TEXT,
|
|
238
|
-
direction: MessageDirection.OUTGOING,
|
|
239
|
-
status: MessageStatus.WAITING_SESSION,
|
|
240
|
-
timestamp: new Date(now - 2000),
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
await testDb.messages.add({
|
|
244
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
245
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
246
|
-
content: 'Message 2 (middle)',
|
|
247
|
-
type: MessageType.TEXT,
|
|
248
|
-
direction: MessageDirection.OUTGOING,
|
|
249
|
-
status: MessageStatus.WAITING_SESSION,
|
|
250
|
-
timestamp: new Date(now - 1000),
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
const sortedMessages = await testDb.messages
|
|
254
|
-
.where('[ownerUserId+contactUserId]')
|
|
255
|
-
.equals([GAP_OWNER_USER_ID, GAP_CONTACT_USER_ID])
|
|
256
|
-
.sortBy('timestamp');
|
|
257
|
-
|
|
258
|
-
expect(sortedMessages[0].content).toBe('Message 1 (oldest)');
|
|
259
|
-
expect(sortedMessages[1].content).toBe('Message 2 (middle)');
|
|
260
|
-
expect(sortedMessages[2].content).toBe('Message 3 (newest)');
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// ============================================================================
|
|
265
|
-
// Reply Target Not Found Fallback
|
|
266
|
-
// ============================================================================
|
|
267
|
-
|
|
268
|
-
describe('Reply Target Not Found Fallback', () => {
|
|
269
|
-
let testDb: GossipDatabase;
|
|
270
|
-
|
|
271
|
-
beforeEach(async () => {
|
|
272
|
-
testDb = new GossipDatabase();
|
|
273
|
-
await testDb.open();
|
|
274
|
-
await Promise.all(testDb.tables.map(table => table.clear()));
|
|
275
|
-
|
|
276
|
-
await testDb.discussions.add({
|
|
277
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
278
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
279
|
-
direction: DiscussionDirection.RECEIVED,
|
|
280
|
-
status: DiscussionStatus.ACTIVE,
|
|
281
|
-
unreadCount: 0,
|
|
282
|
-
createdAt: new Date(),
|
|
283
|
-
updatedAt: new Date(),
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('should store originalContent when reply target is not found', async () => {
|
|
288
|
-
const unknownSeeker = new Uint8Array(GAP_SEEKER_SIZE).fill(99);
|
|
289
|
-
|
|
290
|
-
const existing = await testDb.messages
|
|
291
|
-
.where('[ownerUserId+seeker]')
|
|
292
|
-
.equals([GAP_OWNER_USER_ID, unknownSeeker])
|
|
293
|
-
.first();
|
|
294
|
-
expect(existing).toBeUndefined();
|
|
295
|
-
|
|
296
|
-
const messageId = await testDb.messages.add({
|
|
297
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
298
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
299
|
-
content: 'This is a reply',
|
|
300
|
-
type: MessageType.TEXT,
|
|
301
|
-
direction: MessageDirection.INCOMING,
|
|
302
|
-
status: MessageStatus.DELIVERED,
|
|
303
|
-
timestamp: new Date(),
|
|
304
|
-
replyTo: {
|
|
305
|
-
originalContent: 'The original message that was deleted',
|
|
306
|
-
originalSeeker: unknownSeeker,
|
|
307
|
-
},
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
const stored = await testDb.messages.get(messageId);
|
|
311
|
-
expect(stored?.replyTo?.originalContent).toBe(
|
|
312
|
-
'The original message that was deleted'
|
|
313
|
-
);
|
|
314
|
-
expect(stored?.replyTo?.originalSeeker).toEqual(unknownSeeker);
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
it('should NOT store originalContent when reply target IS found', async () => {
|
|
318
|
-
const knownSeeker = new Uint8Array(GAP_SEEKER_SIZE).fill(88);
|
|
319
|
-
|
|
320
|
-
await testDb.messages.add({
|
|
321
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
322
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
323
|
-
content: 'Original message',
|
|
324
|
-
type: MessageType.TEXT,
|
|
325
|
-
direction: MessageDirection.INCOMING,
|
|
326
|
-
status: MessageStatus.DELIVERED,
|
|
327
|
-
timestamp: new Date(),
|
|
328
|
-
seeker: knownSeeker,
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
const replyId = await testDb.messages.add({
|
|
332
|
-
ownerUserId: GAP_OWNER_USER_ID,
|
|
333
|
-
contactUserId: GAP_CONTACT_USER_ID,
|
|
334
|
-
content: 'Reply to found message',
|
|
335
|
-
type: MessageType.TEXT,
|
|
336
|
-
direction: MessageDirection.INCOMING,
|
|
337
|
-
status: MessageStatus.DELIVERED,
|
|
338
|
-
timestamp: new Date(),
|
|
339
|
-
replyTo: {
|
|
340
|
-
originalContent: undefined,
|
|
341
|
-
originalSeeker: knownSeeker,
|
|
342
|
-
},
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
const stored = await testDb.messages.get(replyId);
|
|
346
|
-
expect(stored?.replyTo?.originalContent).toBeUndefined();
|
|
347
|
-
expect(stored?.replyTo?.originalSeeker).toEqual(knownSeeker);
|
|
348
|
-
});
|
|
349
|
-
});
|