@massalabs/gossip-sdk 0.0.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.
- package/README.md +484 -0
- package/package.json +41 -0
- package/src/api/messageProtocol/index.ts +53 -0
- package/src/api/messageProtocol/mock.ts +13 -0
- package/src/api/messageProtocol/rest.ts +209 -0
- package/src/api/messageProtocol/types.ts +70 -0
- package/src/config/protocol.ts +97 -0
- package/src/config/sdk.ts +131 -0
- package/src/contacts.ts +210 -0
- package/src/core/SdkEventEmitter.ts +91 -0
- package/src/core/SdkPolling.ts +134 -0
- package/src/core/index.ts +9 -0
- package/src/crypto/bip39.ts +84 -0
- package/src/crypto/encryption.ts +77 -0
- package/src/db.ts +465 -0
- package/src/gossipSdk.ts +994 -0
- package/src/index.ts +211 -0
- package/src/services/announcement.ts +653 -0
- package/src/services/auth.ts +95 -0
- package/src/services/discussion.ts +380 -0
- package/src/services/message.ts +1055 -0
- package/src/services/refresh.ts +234 -0
- package/src/sw.ts +17 -0
- package/src/types/events.ts +108 -0
- package/src/types.ts +70 -0
- package/src/utils/base64.ts +39 -0
- package/src/utils/contacts.ts +161 -0
- package/src/utils/discussions.ts +55 -0
- package/src/utils/logs.ts +86 -0
- package/src/utils/messageSerialization.ts +257 -0
- package/src/utils/queue.ts +106 -0
- package/src/utils/type.ts +7 -0
- package/src/utils/userId.ts +114 -0
- package/src/utils/validation.ts +144 -0
- package/src/utils.ts +47 -0
- package/src/wasm/encryption.ts +108 -0
- package/src/wasm/index.ts +20 -0
- package/src/wasm/loader.ts +123 -0
- package/src/wasm/session.ts +276 -0
- package/src/wasm/userKeys.ts +31 -0
- package/test/config/protocol.spec.ts +31 -0
- package/test/config/sdk.spec.ts +163 -0
- package/test/db/helpers.spec.ts +142 -0
- package/test/db/operations.spec.ts +128 -0
- package/test/db/states.spec.ts +535 -0
- package/test/integration/discussion-flow.spec.ts +422 -0
- package/test/integration/messaging-flow.spec.ts +708 -0
- package/test/integration/sdk-lifecycle.spec.ts +325 -0
- package/test/mocks/index.ts +9 -0
- package/test/mocks/mockMessageProtocol.ts +100 -0
- package/test/services/auth.spec.ts +311 -0
- package/test/services/discussion.spec.ts +279 -0
- package/test/services/message-deduplication.spec.ts +299 -0
- package/test/services/message-startup.spec.ts +331 -0
- package/test/services/message.spec.ts +817 -0
- package/test/services/refresh.spec.ts +199 -0
- package/test/services/session-status.spec.ts +349 -0
- package/test/session/wasm.spec.ts +227 -0
- package/test/setup.ts +52 -0
- package/test/utils/contacts.spec.ts +156 -0
- package/test/utils/discussions.spec.ts +66 -0
- package/test/utils/queue.spec.ts +52 -0
- package/test/utils/serialization.spec.ts +120 -0
- package/test/utils/userId.spec.ts +120 -0
- package/test/utils/validation.spec.ts +223 -0
- package/test/utils.ts +212 -0
- package/tsconfig.json +26 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +28 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDK Config tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
defaultSdkConfig,
|
|
8
|
+
mergeConfig,
|
|
9
|
+
type SdkConfig,
|
|
10
|
+
type DeepPartial,
|
|
11
|
+
} from '../../src/config/sdk';
|
|
12
|
+
|
|
13
|
+
describe('SDK Config', () => {
|
|
14
|
+
describe('defaultSdkConfig', () => {
|
|
15
|
+
it('should have correct default protocol values', () => {
|
|
16
|
+
expect(defaultSdkConfig.protocol.timeout).toBe(10000);
|
|
17
|
+
expect(defaultSdkConfig.protocol.retryAttempts).toBe(3);
|
|
18
|
+
expect(defaultSdkConfig.protocol.baseUrl).toBeUndefined();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should have correct default polling values', () => {
|
|
22
|
+
expect(defaultSdkConfig.polling.enabled).toBe(false);
|
|
23
|
+
expect(defaultSdkConfig.polling.messagesIntervalMs).toBe(5000);
|
|
24
|
+
expect(defaultSdkConfig.polling.announcementsIntervalMs).toBe(10000);
|
|
25
|
+
expect(defaultSdkConfig.polling.sessionRefreshIntervalMs).toBe(30000);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should have correct default messages values', () => {
|
|
29
|
+
expect(defaultSdkConfig.messages.fetchDelayMs).toBe(100);
|
|
30
|
+
expect(defaultSdkConfig.messages.maxFetchIterations).toBe(30);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should have correct default announcements values', () => {
|
|
34
|
+
expect(defaultSdkConfig.announcements.fetchLimit).toBe(500);
|
|
35
|
+
expect(defaultSdkConfig.announcements.brokenThresholdMs).toBe(
|
|
36
|
+
60 * 60 * 1000
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
describe('mergeConfig', () => {
|
|
42
|
+
it('should return defaults when no partial provided', () => {
|
|
43
|
+
const config = mergeConfig();
|
|
44
|
+
expect(config).toEqual(defaultSdkConfig);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should return defaults when undefined provided', () => {
|
|
48
|
+
const config = mergeConfig(undefined);
|
|
49
|
+
expect(config).toEqual(defaultSdkConfig);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('should merge partial protocol config', () => {
|
|
53
|
+
const partial: DeepPartial<SdkConfig> = {
|
|
54
|
+
protocol: {
|
|
55
|
+
timeout: 5000,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const config = mergeConfig(partial);
|
|
60
|
+
|
|
61
|
+
expect(config.protocol.timeout).toBe(5000);
|
|
62
|
+
expect(config.protocol.retryAttempts).toBe(3);
|
|
63
|
+
expect(config.polling.enabled).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should merge partial polling config', () => {
|
|
67
|
+
const partial: DeepPartial<SdkConfig> = {
|
|
68
|
+
polling: {
|
|
69
|
+
enabled: true,
|
|
70
|
+
messagesIntervalMs: 2000,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const config = mergeConfig(partial);
|
|
75
|
+
|
|
76
|
+
expect(config.polling.enabled).toBe(true);
|
|
77
|
+
expect(config.polling.messagesIntervalMs).toBe(2000);
|
|
78
|
+
expect(config.polling.announcementsIntervalMs).toBe(10000);
|
|
79
|
+
expect(config.polling.sessionRefreshIntervalMs).toBe(30000);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should merge partial messages config', () => {
|
|
83
|
+
const partial: DeepPartial<SdkConfig> = {
|
|
84
|
+
messages: {
|
|
85
|
+
maxFetchIterations: 50,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const config = mergeConfig(partial);
|
|
90
|
+
|
|
91
|
+
expect(config.messages.maxFetchIterations).toBe(50);
|
|
92
|
+
expect(config.messages.fetchDelayMs).toBe(100);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should merge partial announcements config', () => {
|
|
96
|
+
const partial: DeepPartial<SdkConfig> = {
|
|
97
|
+
announcements: {
|
|
98
|
+
brokenThresholdMs: 30 * 60 * 1000,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const config = mergeConfig(partial);
|
|
103
|
+
|
|
104
|
+
expect(config.announcements.brokenThresholdMs).toBe(30 * 60 * 1000);
|
|
105
|
+
expect(config.announcements.fetchLimit).toBe(500);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should merge multiple sections at once', () => {
|
|
109
|
+
const partial: DeepPartial<SdkConfig> = {
|
|
110
|
+
protocol: {
|
|
111
|
+
baseUrl: 'https://custom.api.com',
|
|
112
|
+
},
|
|
113
|
+
polling: {
|
|
114
|
+
enabled: true,
|
|
115
|
+
},
|
|
116
|
+
messages: {
|
|
117
|
+
fetchDelayMs: 50,
|
|
118
|
+
},
|
|
119
|
+
announcements: {
|
|
120
|
+
fetchLimit: 1000,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const config = mergeConfig(partial);
|
|
125
|
+
|
|
126
|
+
expect(config.protocol.baseUrl).toBe('https://custom.api.com');
|
|
127
|
+
expect(config.polling.enabled).toBe(true);
|
|
128
|
+
expect(config.messages.fetchDelayMs).toBe(50);
|
|
129
|
+
expect(config.announcements.fetchLimit).toBe(1000);
|
|
130
|
+
expect(config.protocol.timeout).toBe(10000);
|
|
131
|
+
expect(config.polling.messagesIntervalMs).toBe(5000);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should not mutate the original defaults', () => {
|
|
135
|
+
const originalTimeout = defaultSdkConfig.protocol.timeout;
|
|
136
|
+
|
|
137
|
+
mergeConfig({
|
|
138
|
+
protocol: { timeout: 1234 },
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
expect(defaultSdkConfig.protocol.timeout).toBe(originalTimeout);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe('Max Fetch Iterations Limit', () => {
|
|
147
|
+
it('should respect maxFetchIterations config', async () => {
|
|
148
|
+
const config: SdkConfig = {
|
|
149
|
+
...defaultSdkConfig,
|
|
150
|
+
messages: {
|
|
151
|
+
...defaultSdkConfig.messages,
|
|
152
|
+
maxFetchIterations: 5,
|
|
153
|
+
fetchDelayMs: 0,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
expect(config.messages.maxFetchIterations).toBe(5);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('should have default maxFetchIterations of 30', () => {
|
|
161
|
+
expect(defaultSdkConfig.messages.maxFetchIterations).toBe(30);
|
|
162
|
+
});
|
|
163
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database helper methods tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
db,
|
|
8
|
+
DiscussionDirection,
|
|
9
|
+
DiscussionStatus,
|
|
10
|
+
MessageDirection,
|
|
11
|
+
MessageStatus,
|
|
12
|
+
MessageType,
|
|
13
|
+
} from '../../src/db';
|
|
14
|
+
import { encodeUserId } from '../../src/utils/userId';
|
|
15
|
+
|
|
16
|
+
const OWNER_USER_ID = encodeUserId(new Uint8Array(32).fill(6));
|
|
17
|
+
const CONTACT_USER_ID = encodeUserId(new Uint8Array(32).fill(7));
|
|
18
|
+
|
|
19
|
+
describe('Database helper methods', () => {
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
if (!db.isOpen()) {
|
|
22
|
+
await db.open();
|
|
23
|
+
}
|
|
24
|
+
await Promise.all(db.tables.map(table => table.clear()));
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('returns discussions sorted by last message timestamp', async () => {
|
|
28
|
+
await db.discussions.add({
|
|
29
|
+
ownerUserId: OWNER_USER_ID,
|
|
30
|
+
contactUserId: CONTACT_USER_ID,
|
|
31
|
+
direction: DiscussionDirection.INITIATED,
|
|
32
|
+
status: DiscussionStatus.ACTIVE,
|
|
33
|
+
unreadCount: 0,
|
|
34
|
+
lastMessageTimestamp: new Date('2024-01-01'),
|
|
35
|
+
createdAt: new Date('2024-01-01'),
|
|
36
|
+
updatedAt: new Date('2024-01-01'),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
await db.discussions.add({
|
|
40
|
+
ownerUserId: OWNER_USER_ID,
|
|
41
|
+
contactUserId: encodeUserId(new Uint8Array(32).fill(8)),
|
|
42
|
+
direction: DiscussionDirection.INITIATED,
|
|
43
|
+
status: DiscussionStatus.ACTIVE,
|
|
44
|
+
unreadCount: 0,
|
|
45
|
+
lastMessageTimestamp: new Date('2024-02-01'),
|
|
46
|
+
createdAt: new Date('2024-02-01'),
|
|
47
|
+
updatedAt: new Date('2024-02-01'),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const discussions = await db.getDiscussionsByOwner(OWNER_USER_ID);
|
|
51
|
+
expect(discussions[0].lastMessageTimestamp?.toISOString()).toBe(
|
|
52
|
+
new Date('2024-02-01').toISOString()
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns active discussions only', async () => {
|
|
57
|
+
await db.discussions.add({
|
|
58
|
+
ownerUserId: OWNER_USER_ID,
|
|
59
|
+
contactUserId: CONTACT_USER_ID,
|
|
60
|
+
direction: DiscussionDirection.INITIATED,
|
|
61
|
+
status: DiscussionStatus.ACTIVE,
|
|
62
|
+
unreadCount: 0,
|
|
63
|
+
createdAt: new Date(),
|
|
64
|
+
updatedAt: new Date(),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await db.discussions.add({
|
|
68
|
+
ownerUserId: OWNER_USER_ID,
|
|
69
|
+
contactUserId: encodeUserId(new Uint8Array(32).fill(9)),
|
|
70
|
+
direction: DiscussionDirection.RECEIVED,
|
|
71
|
+
status: DiscussionStatus.PENDING,
|
|
72
|
+
unreadCount: 0,
|
|
73
|
+
createdAt: new Date(),
|
|
74
|
+
updatedAt: new Date(),
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const active = await db.getActiveDiscussionsByOwner(OWNER_USER_ID);
|
|
78
|
+
expect(active.length).toBe(1);
|
|
79
|
+
expect(active[0].status).toBe(DiscussionStatus.ACTIVE);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('aggregates unread counts across discussions', async () => {
|
|
83
|
+
await db.discussions.add({
|
|
84
|
+
ownerUserId: OWNER_USER_ID,
|
|
85
|
+
contactUserId: CONTACT_USER_ID,
|
|
86
|
+
direction: DiscussionDirection.INITIATED,
|
|
87
|
+
status: DiscussionStatus.ACTIVE,
|
|
88
|
+
unreadCount: 2,
|
|
89
|
+
createdAt: new Date(),
|
|
90
|
+
updatedAt: new Date(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await db.discussions.add({
|
|
94
|
+
ownerUserId: OWNER_USER_ID,
|
|
95
|
+
contactUserId: encodeUserId(new Uint8Array(32).fill(10)),
|
|
96
|
+
direction: DiscussionDirection.RECEIVED,
|
|
97
|
+
status: DiscussionStatus.ACTIVE,
|
|
98
|
+
unreadCount: 3,
|
|
99
|
+
createdAt: new Date(),
|
|
100
|
+
updatedAt: new Date(),
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const unread = await db.getUnreadCountByOwner(OWNER_USER_ID);
|
|
104
|
+
expect(unread).toBe(5);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('marks delivered incoming messages as read', async () => {
|
|
108
|
+
await db.discussions.add({
|
|
109
|
+
ownerUserId: OWNER_USER_ID,
|
|
110
|
+
contactUserId: CONTACT_USER_ID,
|
|
111
|
+
direction: DiscussionDirection.INITIATED,
|
|
112
|
+
status: DiscussionStatus.ACTIVE,
|
|
113
|
+
unreadCount: 1,
|
|
114
|
+
createdAt: new Date(),
|
|
115
|
+
updatedAt: new Date(),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await db.messages.add({
|
|
119
|
+
ownerUserId: OWNER_USER_ID,
|
|
120
|
+
contactUserId: CONTACT_USER_ID,
|
|
121
|
+
content: 'Hello',
|
|
122
|
+
type: MessageType.TEXT,
|
|
123
|
+
direction: MessageDirection.INCOMING,
|
|
124
|
+
status: MessageStatus.DELIVERED,
|
|
125
|
+
timestamp: new Date(),
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await db.markMessagesAsRead(OWNER_USER_ID, CONTACT_USER_ID);
|
|
129
|
+
|
|
130
|
+
const messages = await db.messages
|
|
131
|
+
.where('[ownerUserId+contactUserId+status]')
|
|
132
|
+
.equals([OWNER_USER_ID, CONTACT_USER_ID, MessageStatus.READ])
|
|
133
|
+
.toArray();
|
|
134
|
+
expect(messages.length).toBe(1);
|
|
135
|
+
|
|
136
|
+
const discussion = await db.getDiscussionByOwnerAndContact(
|
|
137
|
+
OWNER_USER_ID,
|
|
138
|
+
CONTACT_USER_ID
|
|
139
|
+
);
|
|
140
|
+
expect(discussion?.unreadCount).toBe(0);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database operations tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterAll } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
GossipDatabase,
|
|
8
|
+
DiscussionStatus,
|
|
9
|
+
DiscussionDirection,
|
|
10
|
+
MessageDirection,
|
|
11
|
+
MessageStatus,
|
|
12
|
+
MessageType,
|
|
13
|
+
} from '../../src/db';
|
|
14
|
+
|
|
15
|
+
const TEST_OWNER_USER_ID = 'gossip1testowner';
|
|
16
|
+
const TEST_CONTACT_USER_ID = 'gossip1testcontact';
|
|
17
|
+
|
|
18
|
+
const testDb = new GossipDatabase();
|
|
19
|
+
|
|
20
|
+
describe('Simple Database Tests', () => {
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
if (!testDb.isOpen()) {
|
|
23
|
+
await testDb.open();
|
|
24
|
+
}
|
|
25
|
+
await testDb.discussions.clear();
|
|
26
|
+
await testDb.messages.clear();
|
|
27
|
+
await testDb.contacts.clear();
|
|
28
|
+
await testDb.pendingAnnouncements.clear();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterAll(async () => {
|
|
32
|
+
await testDb.close();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should create and retrieve a discussion', async () => {
|
|
36
|
+
const discussionId = await testDb.discussions.add({
|
|
37
|
+
ownerUserId: TEST_OWNER_USER_ID,
|
|
38
|
+
contactUserId: TEST_CONTACT_USER_ID,
|
|
39
|
+
direction: DiscussionDirection.INITIATED,
|
|
40
|
+
status: DiscussionStatus.PENDING,
|
|
41
|
+
unreadCount: 0,
|
|
42
|
+
createdAt: new Date(),
|
|
43
|
+
updatedAt: new Date(),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const discussion = await testDb.discussions.get(discussionId);
|
|
47
|
+
expect(discussion?.direction).toBe(DiscussionDirection.INITIATED);
|
|
48
|
+
expect(discussion?.status).toBe(DiscussionStatus.PENDING);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should update discussion status', async () => {
|
|
52
|
+
const discussionId = await testDb.discussions.add({
|
|
53
|
+
ownerUserId: TEST_OWNER_USER_ID,
|
|
54
|
+
contactUserId: TEST_CONTACT_USER_ID,
|
|
55
|
+
direction: DiscussionDirection.INITIATED,
|
|
56
|
+
status: DiscussionStatus.PENDING,
|
|
57
|
+
unreadCount: 0,
|
|
58
|
+
createdAt: new Date(),
|
|
59
|
+
updatedAt: new Date(),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await testDb.discussions.update(discussionId, {
|
|
63
|
+
status: DiscussionStatus.ACTIVE,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const discussion = await testDb.discussions.get(discussionId);
|
|
67
|
+
expect(discussion?.status).toBe(DiscussionStatus.ACTIVE);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should create and retrieve a message', async () => {
|
|
71
|
+
const messageId = await testDb.messages.add({
|
|
72
|
+
ownerUserId: TEST_OWNER_USER_ID,
|
|
73
|
+
contactUserId: TEST_CONTACT_USER_ID,
|
|
74
|
+
content: 'Test message',
|
|
75
|
+
type: MessageType.TEXT,
|
|
76
|
+
direction: MessageDirection.OUTGOING,
|
|
77
|
+
status: MessageStatus.SENDING,
|
|
78
|
+
timestamp: new Date(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const message = await testDb.messages.get(messageId);
|
|
82
|
+
expect(message?.content).toBe('Test message');
|
|
83
|
+
expect(message?.status).toBe(MessageStatus.SENDING);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should reset SENDING messages to FAILED', async () => {
|
|
87
|
+
await testDb.messages.add({
|
|
88
|
+
ownerUserId: TEST_OWNER_USER_ID,
|
|
89
|
+
contactUserId: TEST_CONTACT_USER_ID,
|
|
90
|
+
content: 'Stuck message',
|
|
91
|
+
type: MessageType.TEXT,
|
|
92
|
+
direction: MessageDirection.OUTGOING,
|
|
93
|
+
status: MessageStatus.SENDING,
|
|
94
|
+
timestamp: new Date(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const count = await testDb.messages
|
|
98
|
+
.where('status')
|
|
99
|
+
.equals(MessageStatus.SENDING)
|
|
100
|
+
.modify({ status: MessageStatus.FAILED });
|
|
101
|
+
|
|
102
|
+
expect(count).toBe(1);
|
|
103
|
+
|
|
104
|
+
const messages = await testDb.messages
|
|
105
|
+
.where('status')
|
|
106
|
+
.equals(MessageStatus.FAILED)
|
|
107
|
+
.toArray();
|
|
108
|
+
expect(messages.length).toBe(1);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should store announcement bytes for retry', async () => {
|
|
112
|
+
const announcement = new Uint8Array([1, 2, 3, 4, 5]);
|
|
113
|
+
const discussionId = await testDb.discussions.add({
|
|
114
|
+
ownerUserId: TEST_OWNER_USER_ID,
|
|
115
|
+
contactUserId: TEST_CONTACT_USER_ID,
|
|
116
|
+
direction: DiscussionDirection.INITIATED,
|
|
117
|
+
status: DiscussionStatus.SEND_FAILED,
|
|
118
|
+
initiationAnnouncement: announcement,
|
|
119
|
+
unreadCount: 0,
|
|
120
|
+
createdAt: new Date(),
|
|
121
|
+
updatedAt: new Date(),
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const discussion = await testDb.discussions.get(discussionId);
|
|
125
|
+
expect(discussion?.initiationAnnouncement).toBeDefined();
|
|
126
|
+
expect(discussion?.initiationAnnouncement?.length).toBe(5);
|
|
127
|
+
});
|
|
128
|
+
});
|