@wireapp/core 46.46.6-beta.10.d7a6c4c53 → 46.46.6-beta.14.f6fd03fe6
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/lib/Account.d.ts +51 -168
- package/lib/Account.d.ts.map +1 -1
- package/lib/Account.js +127 -517
- package/lib/Account.test.js +147 -158
- package/lib/broadcast/AvailabilityType.d.ts +1 -1
- package/lib/broadcast/AvailabilityType.d.ts.map +1 -1
- package/lib/broadcast/BroadcastService.d.ts +1 -1
- package/lib/broadcast/BroadcastService.d.ts.map +1 -1
- package/lib/broadcast/BroadcastService.js +1 -1
- package/lib/client/ClientService.d.ts +3 -4
- package/lib/client/ClientService.d.ts.map +1 -1
- package/lib/client/ClientService.js +5 -19
- package/lib/conversation/AbortReason.d.ts +1 -1
- package/lib/conversation/AbortReason.d.ts.map +1 -1
- package/lib/conversation/AssetService/AssetService.d.ts +30 -12
- package/lib/conversation/AssetService/AssetService.d.ts.map +1 -1
- package/lib/conversation/AssetService/AssetService.js +10 -1
- package/lib/conversation/AssetService/AssetService.test.js +3 -8
- package/lib/conversation/ClientActionType.d.ts +1 -1
- package/lib/conversation/ClientActionType.d.ts.map +1 -1
- package/lib/conversation/ClientActionType.js +1 -1
- package/lib/conversation/ConversationService/ConversationService.d.ts +14 -98
- package/lib/conversation/ConversationService/ConversationService.d.ts.map +1 -1
- package/lib/conversation/ConversationService/ConversationService.js +101 -314
- package/lib/conversation/ConversationService/ConversationService.test.js +47 -441
- package/lib/conversation/ConversationService/ConversationService.types.d.ts +4 -5
- package/lib/conversation/ConversationService/ConversationService.types.d.ts.map +1 -1
- package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.d.ts.map +1 -1
- package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.js +3 -6
- package/lib/conversation/SubconversationService/SubconversationService.d.ts.map +1 -1
- package/lib/conversation/SubconversationService/SubconversationService.js +11 -158
- package/lib/conversation/SubconversationService/SubconversationService.test.js +2 -8
- package/lib/conversation/content/AssetContent.d.ts +1 -1
- package/lib/conversation/content/AssetContent.d.ts.map +1 -1
- package/lib/conversation/content/ButtonActionConfirmationContent.d.ts +1 -1
- package/lib/conversation/content/ButtonActionConfirmationContent.d.ts.map +1 -1
- package/lib/conversation/content/ButtonActionContent.d.ts +1 -1
- package/lib/conversation/content/ButtonActionContent.d.ts.map +1 -1
- package/lib/conversation/content/ClearedContent.d.ts +1 -1
- package/lib/conversation/content/ClearedContent.d.ts.map +1 -1
- package/lib/conversation/content/ClientActionContent.d.ts +1 -1
- package/lib/conversation/content/ClientActionContent.d.ts.map +1 -1
- package/lib/conversation/content/CompositeContent.d.ts +1 -1
- package/lib/conversation/content/CompositeContent.d.ts.map +1 -1
- package/lib/conversation/content/ConfirmationContent.d.ts +1 -1
- package/lib/conversation/content/ConfirmationContent.d.ts.map +1 -1
- package/lib/conversation/content/DeletedContent.d.ts +1 -1
- package/lib/conversation/content/DeletedContent.d.ts.map +1 -1
- package/lib/conversation/content/HiddenContent.d.ts +1 -1
- package/lib/conversation/content/HiddenContent.d.ts.map +1 -1
- package/lib/conversation/content/KnockContent.d.ts +1 -1
- package/lib/conversation/content/KnockContent.d.ts.map +1 -1
- package/lib/conversation/content/LinkPreviewContent.d.ts +1 -1
- package/lib/conversation/content/LinkPreviewContent.d.ts.map +1 -1
- package/lib/conversation/content/MentionContent.d.ts +1 -1
- package/lib/conversation/content/MentionContent.d.ts.map +1 -1
- package/lib/conversation/content/MultipartContent.d.ts +1 -1
- package/lib/conversation/content/MultipartContent.d.ts.map +1 -1
- package/lib/conversation/content/QuoteContent.d.ts +1 -1
- package/lib/conversation/content/QuoteContent.d.ts.map +1 -1
- package/lib/conversation/content/TweetContent.d.ts +1 -1
- package/lib/conversation/content/TweetContent.d.ts.map +1 -1
- package/lib/conversation/content/index.d.ts +1 -1
- package/lib/conversation/content/index.d.ts.map +1 -1
- package/lib/conversation/content/index.js +1 -1
- package/lib/conversation/message/MessageBuilder.d.ts +1 -1
- package/lib/conversation/message/MessageBuilder.d.ts.map +1 -1
- package/lib/conversation/message/MessageBuilder.js +1 -1
- package/lib/conversation/message/MessageService.d.ts.map +1 -1
- package/lib/conversation/message/MessageService.js +1 -1
- package/lib/conversation/message/MessageService.test.js +1 -7
- package/lib/conversation/message/MessageToProtoMapper.d.ts +1 -1
- package/lib/conversation/message/MessageToProtoMapper.d.ts.map +1 -1
- package/lib/conversation/message/MessageToProtoMapper.js +1 -1
- package/lib/conversation/message/messageSender.js +2 -2
- package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts +2 -2
- package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts.map +1 -1
- package/lib/messagingProtocols/common.types.d.ts +0 -9
- package/lib/messagingProtocols/common.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts +2 -2
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.js +1 -2
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.js +11 -13
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.test.js +16 -21
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts +3 -9
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.js +12 -31
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts +0 -6
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.js +1 -19
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts +4 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.js +69 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/index.d.ts +2 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingProposalsQueue → IncomingMessagesQueue}/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingProposalsQueue → IncomingMessagesQueue}/index.js +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts +1 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.js +1 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.js +14 -23
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts +2 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts.map +1 -0
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.js +98 -0
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.js +2 -5
- package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.test.js +3 -13
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts +2 -38
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.js +6 -41
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts +34 -38
- package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts.map +1 -1
- package/lib/messagingProtocols/mls/MLSService/MLSService.js +208 -267
- package/lib/messagingProtocols/mls/MLSService/MLSService.test.js +160 -157
- package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.js +3 -3
- package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.test.js +5 -5
- package/lib/messagingProtocols/mls/conversationRejoinQueue.js +2 -2
- package/lib/messagingProtocols/mls/types.d.ts +8 -0
- package/lib/messagingProtocols/mls/types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.js +1 -7
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts +15 -8
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.js +62 -97
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts +6 -0
- package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts +6 -1
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.js +22 -19
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts +3 -5
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.js +24 -11
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts +0 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.js +2 -11
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.test.js +9 -13
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts +2 -3
- package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/WithMockedGenerics.test.js +4 -11
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts +4 -0
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.js +5 -0
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts +1 -2
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/ProteusService/identityClearer.js +2 -8
- package/lib/messagingProtocols/proteus/Utility/SessionHandler/SessionHandler.test.js +0 -4
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts +1 -1
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts.map +1 -1
- package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.js +1 -1
- package/lib/notification/NotificationService.d.ts +6 -20
- package/lib/notification/NotificationService.d.ts.map +1 -1
- package/lib/notification/NotificationService.js +14 -23
- package/lib/notification/NotificationService.test.js +0 -8
- package/lib/secretStore/secretKeyGenerator.d.ts +0 -1
- package/lib/secretStore/secretKeyGenerator.d.ts.map +1 -1
- package/lib/secretStore/secretKeyGenerator.js +1 -3
- package/lib/self/SelfService.d.ts +2 -2
- package/lib/self/SelfService.d.ts.map +1 -1
- package/lib/self/SelfService.test.js +2 -5
- package/lib/team/TeamService.d.ts +2 -5
- package/lib/team/TeamService.d.ts.map +1 -1
- package/lib/team/TeamService.js +2 -12
- package/lib/user/UserService.d.ts +2 -2
- package/lib/user/UserService.d.ts.map +1 -1
- package/lib/user/UserService.js +3 -3
- package/lib/util/TypePredicateUtil.d.ts.map +1 -1
- package/lib/util/TypePredicateUtil.js +2 -2
- package/package.json +3 -3
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts +0 -7
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.js +0 -48
- package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/index.d.ts +0 -2
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts +0 -2
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.js +0 -124
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts +0 -78
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.js +0 -173
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts +0 -2
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.js +0 -117
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts +0 -167
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.js +0 -317
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts +0 -2
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.js +0 -248
- package/lib/messagingProtocols/mls/recovery/index.d.ts +0 -5
- package/lib/messagingProtocols/mls/recovery/index.d.ts.map +0 -1
- package/lib/messagingProtocols/mls/recovery/index.js +0 -28
- package/lib/test/StoreHelper.d.ts +0 -2
- package/lib/test/StoreHelper.d.ts.map +0 -1
- package/lib/test/StoreHelper.js +0 -27
|
@@ -45,13 +45,10 @@ const client_1 = require("@wireapp/api-client/lib/client");
|
|
|
45
45
|
const conversation_1 = require("@wireapp/api-client/lib/conversation");
|
|
46
46
|
const event_1 = require("@wireapp/api-client/lib/event");
|
|
47
47
|
const http_1 = require("@wireapp/api-client/lib/http");
|
|
48
|
-
const team_1 = require("@wireapp/api-client/lib/team");
|
|
49
48
|
const http_status_codes_1 = require("http-status-codes");
|
|
50
49
|
const api_client_1 = require("@wireapp/api-client");
|
|
51
|
-
const core_crypto_1 = require("@wireapp/core-crypto");
|
|
52
50
|
const __1 = require("..");
|
|
53
51
|
const CoreCryptoMLSError_1 = require("../../messagingProtocols/mls/MLSService/CoreCryptoMLSError");
|
|
54
|
-
const MLSService_1 = require("../../messagingProtocols/mls/MLSService/MLSService");
|
|
55
52
|
const MessagingProtocols = __importStar(require("../../messagingProtocols/proteus"));
|
|
56
53
|
const CoreDB_1 = require("../../storage/CoreDB");
|
|
57
54
|
const PayloadHelper = __importStar(require("../../test/PayloadHelper"));
|
|
@@ -89,14 +86,9 @@ const mockedProteusService = {
|
|
|
89
86
|
encryptGenericMessage: () => Promise.resolve(),
|
|
90
87
|
sendProteusMessage: () => Promise.resolve({ sentAt: new Date() }),
|
|
91
88
|
};
|
|
92
|
-
const apiClients = [];
|
|
93
89
|
describe('ConversationService', () => {
|
|
94
|
-
afterAll(() => {
|
|
95
|
-
apiClients.forEach(client => client.disconnect());
|
|
96
|
-
});
|
|
97
90
|
async function buildConversationService() {
|
|
98
91
|
const client = new api_client_1.APIClient({ urls: api_client_1.APIClient.BACKEND.STAGING });
|
|
99
|
-
apiClients.push(client);
|
|
100
92
|
jest.spyOn(client.api.conversation, 'postMlsMessage').mockReturnValue(Promise.resolve({
|
|
101
93
|
events: [],
|
|
102
94
|
time: new Date().toISOString(),
|
|
@@ -114,14 +106,13 @@ describe('ConversationService', () => {
|
|
|
114
106
|
}));
|
|
115
107
|
jest
|
|
116
108
|
.spyOn(client.api.user, 'getUserSupportedProtocols')
|
|
117
|
-
.mockReturnValue(Promise.resolve([
|
|
109
|
+
.mockReturnValue(Promise.resolve([conversation_1.ConversationProtocol.MLS, conversation_1.ConversationProtocol.PROTEUS]));
|
|
118
110
|
client.context = {
|
|
119
111
|
clientType: client_1.ClientType.NONE,
|
|
120
112
|
userId: PayloadHelper.getUUID(),
|
|
121
113
|
clientId: PayloadHelper.getUUID(),
|
|
122
114
|
};
|
|
123
115
|
const mockedMLSService = {
|
|
124
|
-
on: jest.fn(),
|
|
125
116
|
encryptMessage: () => { },
|
|
126
117
|
commitPendingProposals: () => Promise.resolve(),
|
|
127
118
|
getEpoch: () => Promise.resolve(),
|
|
@@ -133,10 +124,8 @@ describe('ConversationService', () => {
|
|
|
133
124
|
conversationExists: jest.fn(),
|
|
134
125
|
isConversationEstablished: jest.fn(),
|
|
135
126
|
tryEstablishingMLSGroup: jest.fn(),
|
|
136
|
-
getClientIdsInGroup: jest.fn(),
|
|
137
127
|
getKeyPackagesPayload: jest.fn(),
|
|
138
128
|
addUsersToExistingConversation: jest.fn(),
|
|
139
|
-
removeClientsFromConversation: jest.fn(),
|
|
140
129
|
resetKeyMaterialRenewal: jest.fn(),
|
|
141
130
|
handleMLSWelcomeMessageEvent: jest.fn(),
|
|
142
131
|
};
|
|
@@ -145,7 +134,7 @@ describe('ConversationService', () => {
|
|
|
145
134
|
const mockedSubconversationService = {
|
|
146
135
|
joinConferenceSubconversation: jest.fn(),
|
|
147
136
|
};
|
|
148
|
-
const conversationService = new __1.ConversationService(client, mockedProteusService, mockedDb, groupIdFromConversationId, mockedSubconversationService,
|
|
137
|
+
const conversationService = new __1.ConversationService(client, mockedProteusService, mockedDb, groupIdFromConversationId, mockedSubconversationService, mockedMLSService);
|
|
149
138
|
jest.spyOn(conversationService, 'joinByExternalCommit');
|
|
150
139
|
jest.spyOn(conversationService, 'emit');
|
|
151
140
|
return [
|
|
@@ -168,7 +157,7 @@ describe('ConversationService', () => {
|
|
|
168
157
|
const sentTime = new Date().toISOString();
|
|
169
158
|
mockedProteusService.sendMessage = jest.fn().mockResolvedValue({ sentAt: sentTime });
|
|
170
159
|
const promise = conversationService.send({
|
|
171
|
-
protocol:
|
|
160
|
+
protocol: conversation_1.ConversationProtocol.PROTEUS,
|
|
172
161
|
conversationId: { id: 'conv1', domain: '' },
|
|
173
162
|
payload: message,
|
|
174
163
|
});
|
|
@@ -177,80 +166,6 @@ describe('ConversationService', () => {
|
|
|
177
166
|
});
|
|
178
167
|
});
|
|
179
168
|
});
|
|
180
|
-
describe('removeUsersFromMLSConversation', () => {
|
|
181
|
-
it('recovers and retries when stale-message occurs during remove users commit upload', async () => {
|
|
182
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
183
|
-
const mockGroupId = 'groupId-stale-remove';
|
|
184
|
-
const mockConversationId = { id: PayloadHelper.getUUID(), domain: 'staging.zinfra.io' };
|
|
185
|
-
const qualifiedUserIds = [
|
|
186
|
-
{ id: 'test-id-1', domain: 'test-domain' },
|
|
187
|
-
{ id: 'test-id-2', domain: 'test-domain' },
|
|
188
|
-
];
|
|
189
|
-
const staleMessageError = {
|
|
190
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
191
|
-
context: {
|
|
192
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
193
|
-
context: {
|
|
194
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_STALE_MESSAGE }),
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
// First removal attempt fails with stale, second succeeds
|
|
199
|
-
jest
|
|
200
|
-
.spyOn(mlsService, 'removeClientsFromConversation')
|
|
201
|
-
.mockRejectedValueOnce(staleMessageError)
|
|
202
|
-
.mockResolvedValueOnce(undefined);
|
|
203
|
-
const remoteEpoch = 6;
|
|
204
|
-
const localEpoch = 5;
|
|
205
|
-
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
206
|
-
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
207
|
-
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValue({
|
|
208
|
-
qualified_id: mockConversationId,
|
|
209
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
210
|
-
epoch: remoteEpoch,
|
|
211
|
-
group_id: mockGroupId,
|
|
212
|
-
});
|
|
213
|
-
await conversationService.removeUsersFromMLSConversation({
|
|
214
|
-
groupId: mockGroupId,
|
|
215
|
-
conversationId: mockConversationId,
|
|
216
|
-
qualifiedUserIds,
|
|
217
|
-
});
|
|
218
|
-
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(mockConversationId);
|
|
219
|
-
expect(mlsService.removeClientsFromConversation).toHaveBeenCalledTimes(2);
|
|
220
|
-
expect(mlsService.resetKeyMaterialRenewal).toHaveBeenCalledWith(mockGroupId);
|
|
221
|
-
});
|
|
222
|
-
});
|
|
223
|
-
describe('joinByExternalCommit', () => {
|
|
224
|
-
it('retries join when stale-message occurs during external commit join', async () => {
|
|
225
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
226
|
-
const conversationId = { id: 'conv-join-stale', domain: 'staging.zinfra.io' };
|
|
227
|
-
const staleMessageError = {
|
|
228
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
229
|
-
context: {
|
|
230
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
231
|
-
context: {
|
|
232
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_STALE_MESSAGE }),
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
jest
|
|
237
|
-
.spyOn(mlsService, 'joinByExternalCommit')
|
|
238
|
-
.mockRejectedValueOnce(staleMessageError)
|
|
239
|
-
.mockResolvedValueOnce(undefined);
|
|
240
|
-
const remoteEpoch = 10;
|
|
241
|
-
const localEpoch = 9;
|
|
242
|
-
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
243
|
-
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
244
|
-
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
245
|
-
qualified_id: conversationId,
|
|
246
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
247
|
-
epoch: remoteEpoch,
|
|
248
|
-
group_id: 'gid-join-stale',
|
|
249
|
-
});
|
|
250
|
-
await conversationService.joinByExternalCommit(conversationId);
|
|
251
|
-
expect(mlsService.joinByExternalCommit).toHaveBeenCalledTimes(2);
|
|
252
|
-
});
|
|
253
|
-
});
|
|
254
169
|
describe('"send MLS"', () => {
|
|
255
170
|
const groupId = PayloadHelper.getUUID();
|
|
256
171
|
const messages = [
|
|
@@ -266,7 +181,7 @@ describe('ConversationService', () => {
|
|
|
266
181
|
it(`calls callbacks when sending '${type}' message is starting and successful`, async () => {
|
|
267
182
|
const [conversationService] = await buildConversationService();
|
|
268
183
|
const promise = conversationService.send({
|
|
269
|
-
protocol:
|
|
184
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
270
185
|
groupId,
|
|
271
186
|
payload: message,
|
|
272
187
|
conversationId: { id: '', domain: '' },
|
|
@@ -275,62 +190,26 @@ describe('ConversationService', () => {
|
|
|
275
190
|
expect(result.state).toBe(__1.MessageSendingState.OUTGOING_SENT);
|
|
276
191
|
});
|
|
277
192
|
});
|
|
278
|
-
it('rejoins a MLS group when stale-message error occurs during commit bundle upload', async () => {
|
|
279
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
280
|
-
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlamxpbms=';
|
|
281
|
-
const mockConversationId = { id: 'mockConversationId', domain: 'staging.zinfra.io' };
|
|
282
|
-
const mockedMessage = MessageBuilder.buildTextMessage({ text: 'test' });
|
|
283
|
-
const staleMessageError = new conversation_1.MLSStaleMessageError('', http_1.BackendErrorLabel.MLS_STALE_MESSAGE, http_status_codes_1.StatusCodes.CONFLICT);
|
|
284
|
-
// First attempt to upload commit bundle fails with stale-message, second attempt succeeds
|
|
285
|
-
jest
|
|
286
|
-
.spyOn(mlsService, 'commitPendingProposals')
|
|
287
|
-
.mockRejectedValueOnce(staleMessageError)
|
|
288
|
-
.mockResolvedValueOnce(undefined);
|
|
289
|
-
const remoteEpoch = 5;
|
|
290
|
-
const localEpoch = 4;
|
|
291
|
-
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
292
|
-
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
293
|
-
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
294
|
-
qualified_id: mockConversationId,
|
|
295
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
296
|
-
epoch: remoteEpoch,
|
|
297
|
-
group_id: mockGroupId,
|
|
298
|
-
});
|
|
299
|
-
await conversationService.send({
|
|
300
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
301
|
-
groupId: mockGroupId,
|
|
302
|
-
payload: mockedMessage,
|
|
303
|
-
conversationId: mockConversationId,
|
|
304
|
-
});
|
|
305
|
-
// Recovery via external commit should have been triggered
|
|
306
|
-
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(mockConversationId);
|
|
307
|
-
expect(conversationService.emit).toHaveBeenCalledWith('MLSConversationRecovered', {
|
|
308
|
-
conversationId: mockConversationId,
|
|
309
|
-
});
|
|
310
|
-
// Because the failure happened before posting the message, postMlsMessage should be called only once (after recovery)
|
|
311
|
-
expect(apiClient.api.conversation.postMlsMessage).toHaveBeenCalledTimes(1);
|
|
312
|
-
// commitPendingProposals is called twice: first fails, second succeeds
|
|
313
|
-
expect(mlsService.commitPendingProposals).toHaveBeenCalledTimes(2);
|
|
314
|
-
});
|
|
315
193
|
it('rejoins a MLS group when failed encrypting MLS message', async () => {
|
|
316
194
|
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
317
195
|
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlamxpbms=';
|
|
318
196
|
const mockConversationId = { id: 'mockConversationId', domain: 'staging.zinfra.io' };
|
|
319
197
|
const mockedMessage = MessageBuilder.buildTextMessage({ text: 'test' });
|
|
320
|
-
|
|
321
|
-
|
|
198
|
+
jest
|
|
199
|
+
.spyOn(apiClient.api.conversation, 'postMlsMessage')
|
|
200
|
+
.mockRejectedValueOnce(new http_1.BackendError('', http_1.BackendErrorLabel.MLS_STALE_MESSAGE, http_status_codes_1.StatusCodes.CONFLICT));
|
|
322
201
|
const remoteEpoch = 5;
|
|
323
202
|
const localEpoch = 4;
|
|
324
203
|
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
325
204
|
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
326
205
|
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
327
206
|
qualified_id: mockConversationId,
|
|
328
|
-
protocol:
|
|
207
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
329
208
|
epoch: remoteEpoch,
|
|
330
209
|
group_id: mockGroupId,
|
|
331
210
|
});
|
|
332
211
|
await conversationService.send({
|
|
333
|
-
protocol:
|
|
212
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
334
213
|
groupId: mockGroupId,
|
|
335
214
|
payload: mockedMessage,
|
|
336
215
|
conversationId: mockConversationId,
|
|
@@ -341,34 +220,6 @@ describe('ConversationService', () => {
|
|
|
341
220
|
});
|
|
342
221
|
expect(apiClient.api.conversation.postMlsMessage).toHaveBeenCalledTimes(2);
|
|
343
222
|
});
|
|
344
|
-
it('adds missing users to MLS group and retries when group is out of sync during send', async () => {
|
|
345
|
-
const [conversationService, { apiClient }] = await buildConversationService();
|
|
346
|
-
const mockGroupId = 'AAEAAH87aajaQ011i+rNLmwpy0sAZGl5YS53aXJlamxpbms=';
|
|
347
|
-
const mockConversationId = { id: 'mockConversationId', domain: 'staging.zinfra.io' };
|
|
348
|
-
const mockedMessage = MessageBuilder.buildTextMessage({ text: 'test' });
|
|
349
|
-
const missingUsers = [
|
|
350
|
-
{ id: 'user-1', domain: 'staging.zinfra.io' },
|
|
351
|
-
{ id: 'user-2', domain: 'staging.zinfra.io' },
|
|
352
|
-
];
|
|
353
|
-
const outOfSyncError = new conversation_1.MLSGroupOutOfSyncError(http_status_codes_1.StatusCodes.CONFLICT, missingUsers, http_1.BackendErrorLabel.MLS_GROUP_OUT_OF_SYNC);
|
|
354
|
-
// First send fails with out-of-sync, second succeeds via default mock
|
|
355
|
-
jest.spyOn(apiClient.api.conversation, 'postMlsMessage').mockRejectedValueOnce(outOfSyncError);
|
|
356
|
-
const addUsersSpy = jest
|
|
357
|
-
.spyOn(conversationService, 'performAddUsersToMLSConversationAPI')
|
|
358
|
-
.mockResolvedValueOnce({ conversation: { members: { others: [] } } });
|
|
359
|
-
await conversationService.send({
|
|
360
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
361
|
-
groupId: mockGroupId,
|
|
362
|
-
payload: mockedMessage,
|
|
363
|
-
conversationId: mockConversationId,
|
|
364
|
-
});
|
|
365
|
-
expect(addUsersSpy).toHaveBeenCalledWith({
|
|
366
|
-
groupId: mockGroupId,
|
|
367
|
-
conversationId: mockConversationId,
|
|
368
|
-
qualifiedUsers: missingUsers,
|
|
369
|
-
});
|
|
370
|
-
expect(apiClient.api.conversation.postMlsMessage).toHaveBeenCalledTimes(2);
|
|
371
|
-
});
|
|
372
223
|
});
|
|
373
224
|
describe('handleConversationsEpochMismatch', () => {
|
|
374
225
|
beforeEach(() => {
|
|
@@ -378,7 +229,7 @@ describe('ConversationService', () => {
|
|
|
378
229
|
return {
|
|
379
230
|
group_id: 'group-id',
|
|
380
231
|
qualified_id: { id: conversationId || 'conversation-id', domain: 'staging.zinfra.io' },
|
|
381
|
-
protocol:
|
|
232
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
382
233
|
epoch,
|
|
383
234
|
};
|
|
384
235
|
};
|
|
@@ -428,7 +279,7 @@ describe('ConversationService', () => {
|
|
|
428
279
|
const remoteEpoch = 1;
|
|
429
280
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
430
281
|
qualified_id: mockConversationId,
|
|
431
|
-
protocol:
|
|
282
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
432
283
|
epoch: remoteEpoch,
|
|
433
284
|
group_id: mockGroupId,
|
|
434
285
|
});
|
|
@@ -447,19 +298,19 @@ describe('ConversationService', () => {
|
|
|
447
298
|
const updatedEpoch = 2;
|
|
448
299
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
449
300
|
qualified_id: mockConversationId,
|
|
450
|
-
protocol:
|
|
301
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
451
302
|
epoch: remoteEpoch,
|
|
452
303
|
group_id: mockGroupId,
|
|
453
304
|
});
|
|
454
305
|
// The 2nd request we make after joining the conversation with external commit
|
|
455
306
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
456
307
|
qualified_id: mockConversationId,
|
|
457
|
-
protocol:
|
|
308
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
458
309
|
epoch: updatedEpoch,
|
|
459
310
|
group_id: mockGroupId,
|
|
460
311
|
});
|
|
461
312
|
jest.spyOn(mlsService, 'isConversationEstablished').mockResolvedValueOnce(false);
|
|
462
|
-
jest.spyOn(mlsService, 'joinByExternalCommit');
|
|
313
|
+
jest.spyOn(mlsService, 'joinByExternalCommit').mockResolvedValueOnce({ events: [], time: '' });
|
|
463
314
|
const establishedConversation = await conversationService.establishMLS1to1Conversation(mockGroupId, selfUser, otherUserId);
|
|
464
315
|
expect(mlsService.registerConversation).not.toHaveBeenCalled();
|
|
465
316
|
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(mockConversationId);
|
|
@@ -475,14 +326,14 @@ describe('ConversationService', () => {
|
|
|
475
326
|
const updatedEpoch = 1;
|
|
476
327
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
477
328
|
qualified_id: mockConversationId,
|
|
478
|
-
protocol:
|
|
329
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
479
330
|
epoch: remoteEpoch,
|
|
480
331
|
group_id: mockGroupId,
|
|
481
332
|
});
|
|
482
333
|
// The 2nd request we make after successfully registering a group
|
|
483
334
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
484
335
|
qualified_id: mockConversationId,
|
|
485
|
-
protocol:
|
|
336
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
486
337
|
epoch: updatedEpoch,
|
|
487
338
|
group_id: mockGroupId,
|
|
488
339
|
});
|
|
@@ -504,21 +355,21 @@ describe('ConversationService', () => {
|
|
|
504
355
|
const updatedEpoch = 1;
|
|
505
356
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
506
357
|
qualified_id: mockConversationId,
|
|
507
|
-
protocol:
|
|
358
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
508
359
|
epoch: remoteEpoch,
|
|
509
360
|
group_id: mockGroupId,
|
|
510
361
|
});
|
|
511
362
|
// The 2nd request we make when retrying to register the conversation
|
|
512
363
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
513
364
|
qualified_id: mockConversationId,
|
|
514
|
-
protocol:
|
|
365
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
515
366
|
epoch: remoteEpoch,
|
|
516
367
|
group_id: mockGroupId,
|
|
517
368
|
});
|
|
518
369
|
// The 3rd request we make after successfully registering a group
|
|
519
370
|
jest.spyOn(apiClient.api.conversation, 'getMLS1to1Conversation').mockResolvedValueOnce({
|
|
520
371
|
qualified_id: mockConversationId,
|
|
521
|
-
protocol:
|
|
372
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
522
373
|
epoch: updatedEpoch,
|
|
523
374
|
group_id: mockGroupId,
|
|
524
375
|
});
|
|
@@ -547,7 +398,7 @@ describe('ConversationService', () => {
|
|
|
547
398
|
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
548
399
|
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
549
400
|
qualified_id: conversationId,
|
|
550
|
-
protocol:
|
|
401
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
551
402
|
epoch: remoteEpoch,
|
|
552
403
|
group_id: mockGroupId,
|
|
553
404
|
});
|
|
@@ -581,46 +432,19 @@ describe('ConversationService', () => {
|
|
|
581
432
|
expect(subconversationService.joinConferenceSubconversation).toHaveBeenCalledWith(conversationId, 'groupId');
|
|
582
433
|
});
|
|
583
434
|
it('joins a MLS conversation if it was sent an orphan welcome message', async () => {
|
|
584
|
-
const [conversationService, { mlsService }] = await buildConversationService();
|
|
435
|
+
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
585
436
|
const conversationId = { id: 'conversationId', domain: 'staging.zinfra.io' };
|
|
586
437
|
const mockMLSWelcomeMessageEvent = createMLSWelcomeMessageEventMock(conversationId);
|
|
587
438
|
const orphanWelcomeMessageError = new Error();
|
|
588
|
-
|
|
589
|
-
orphanWelcomeMessageError.name = core_crypto_1.MlsErrorType.OrphanWelcome;
|
|
590
|
-
orphanWelcomeMessageError.context = { type: core_crypto_1.MlsErrorType.OrphanWelcome };
|
|
591
|
-
orphanWelcomeMessageError.type = core_crypto_1.ErrorType.Mls;
|
|
439
|
+
orphanWelcomeMessageError.name = CoreCryptoMLSError_1.CORE_CRYPTO_ERROR_NAMES.MlsErrorOrphanWelcomeMessage;
|
|
592
440
|
jest.spyOn(mlsService, 'handleMLSWelcomeMessageEvent').mockRejectedValueOnce(orphanWelcomeMessageError);
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
// Orchestrator triggers a low-level join (performJoinByExternalCommitAPI -> mlsService.joinByExternalCommit)
|
|
598
|
-
expect(mlsService.joinByExternalCommit).toHaveBeenCalled();
|
|
599
|
-
});
|
|
600
|
-
it('wipes local MLS state and retries when welcome fails with ConversationAlreadyExists', async () => {
|
|
601
|
-
const [conversationService, { mlsService }] = await buildConversationService();
|
|
602
|
-
const conversationId = { id: 'conversationId', domain: 'staging.zinfra.io' };
|
|
603
|
-
const mockMLSWelcomeMessageEvent = createMLSWelcomeMessageEventMock(conversationId);
|
|
604
|
-
// Create a base64 group id and its byte-array form to simulate core-crypto error context
|
|
605
|
-
const expectedGroupId = 'AXNhbXBsZQ=='; // base64 for '\u0001sample'
|
|
606
|
-
const conversationIdArray = Array.from(Buffer.from(expectedGroupId, 'base64'));
|
|
607
|
-
const existsError = {};
|
|
608
|
-
// Simulate core-crypto "conversation already exists" classification for the mapper
|
|
609
|
-
existsError.type = core_crypto_1.ErrorType.Mls;
|
|
610
|
-
existsError.context = {
|
|
611
|
-
type: core_crypto_1.MlsErrorType.ConversationAlreadyExists,
|
|
612
|
-
context: { conversationId: conversationIdArray },
|
|
613
|
-
};
|
|
614
|
-
const welcomeSpy = jest
|
|
615
|
-
.spyOn(mlsService, 'handleMLSWelcomeMessageEvent')
|
|
616
|
-
.mockRejectedValueOnce(existsError)
|
|
617
|
-
.mockResolvedValueOnce(undefined);
|
|
441
|
+
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
442
|
+
qualified_id: conversationId,
|
|
443
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
444
|
+
});
|
|
618
445
|
await conversationService.handleEvent(mockMLSWelcomeMessageEvent);
|
|
619
446
|
await new Promise(resolve => setImmediate(resolve));
|
|
620
|
-
|
|
621
|
-
expect(mlsService.wipeConversation).toHaveBeenCalledTimes(1);
|
|
622
|
-
expect(mlsService.wipeConversation).toHaveBeenCalledWith(expectedGroupId);
|
|
623
|
-
expect(welcomeSpy).toHaveBeenCalledTimes(2);
|
|
447
|
+
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(conversationId);
|
|
624
448
|
});
|
|
625
449
|
});
|
|
626
450
|
describe('getConversations', () => {
|
|
@@ -687,22 +511,21 @@ describe('ConversationService', () => {
|
|
|
687
511
|
.map(() => ({ id: PayloadHelper.getUUID(), domain: 'local.wire.com' }));
|
|
688
512
|
const selfUserToAdd = { id: 'self-user-id', domain: 'local.wire.com', skipOwnClientId: apiClient.clientId };
|
|
689
513
|
const qualifiedUsers = [...otherUsersToAdd, selfUserToAdd];
|
|
690
|
-
jest
|
|
691
|
-
.spyOn(mlsService, 'getKeyPackagesPayload')
|
|
692
|
-
.mockResolvedValueOnce({ keyPackages: [new Uint8Array(0)], failures: [] });
|
|
514
|
+
jest.spyOn(mlsService, 'getKeyPackagesPayload').mockResolvedValueOnce({ keyPackages: [], failures: [] });
|
|
693
515
|
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
694
516
|
qualified_id: mockConversationId,
|
|
695
|
-
protocol:
|
|
517
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
696
518
|
epoch: 1,
|
|
697
519
|
group_id: mockGroupId,
|
|
698
520
|
});
|
|
699
|
-
|
|
521
|
+
const mlsMessage = { events: [], time: '', failures: [] };
|
|
522
|
+
jest.spyOn(mlsService, 'addUsersToExistingConversation').mockResolvedValueOnce(mlsMessage);
|
|
700
523
|
await conversationService.addUsersToMLSConversation({
|
|
701
524
|
qualifiedUsers,
|
|
702
525
|
groupId: mockGroupId,
|
|
703
526
|
conversationId: mockConversationId,
|
|
704
527
|
});
|
|
705
|
-
expect(mlsService.getKeyPackagesPayload).toHaveBeenCalledWith(qualifiedUsers
|
|
528
|
+
expect(mlsService.getKeyPackagesPayload).toHaveBeenCalledWith(qualifiedUsers);
|
|
706
529
|
expect(mlsService.resetKeyMaterialRenewal).toHaveBeenCalledWith(mockGroupId);
|
|
707
530
|
});
|
|
708
531
|
it('should return failure reasons for users it was not possible to claim keys for', async () => {
|
|
@@ -718,11 +541,20 @@ describe('ConversationService', () => {
|
|
|
718
541
|
reason: __1.AddUsersFailureReasons.OFFLINE_FOR_TOO_LONG,
|
|
719
542
|
users: [otherUsersToAdd[0]],
|
|
720
543
|
};
|
|
544
|
+
const addUsersFailure = {
|
|
545
|
+
reason: __1.AddUsersFailureReasons.UNREACHABLE_BACKENDS,
|
|
546
|
+
users: [otherUsersToAdd[1]],
|
|
547
|
+
backends: [otherUsersToAdd[1].domain],
|
|
548
|
+
};
|
|
549
|
+
const mlsFailure = {
|
|
550
|
+
reason: __1.AddUsersFailureReasons.NOT_MLS_CAPABLE,
|
|
551
|
+
users: [otherUsersToAdd[2]],
|
|
552
|
+
};
|
|
721
553
|
jest.spyOn(apiClient.api.user, 'getUserSupportedProtocols').mockImplementation(id => {
|
|
722
554
|
if (id === otherUsersToAdd[2]) {
|
|
723
|
-
return Promise.resolve([
|
|
555
|
+
return Promise.resolve([conversation_1.ConversationProtocol.PROTEUS]);
|
|
724
556
|
}
|
|
725
|
-
return Promise.resolve([
|
|
557
|
+
return Promise.resolve([conversation_1.ConversationProtocol.MLS, conversation_1.ConversationProtocol.PROTEUS]);
|
|
726
558
|
});
|
|
727
559
|
jest.spyOn(mlsService, 'getKeyPackagesPayload').mockResolvedValueOnce({
|
|
728
560
|
keyPackages: [new Uint8Array(0)],
|
|
@@ -730,63 +562,18 @@ describe('ConversationService', () => {
|
|
|
730
562
|
});
|
|
731
563
|
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
732
564
|
qualified_id: mockConversationId,
|
|
733
|
-
protocol:
|
|
565
|
+
protocol: conversation_1.ConversationProtocol.MLS,
|
|
734
566
|
epoch: 1,
|
|
735
567
|
group_id: mockGroupId,
|
|
736
568
|
});
|
|
737
|
-
|
|
569
|
+
const mlsMessage = { events: [], time: '', failures: [addUsersFailure] };
|
|
570
|
+
jest.spyOn(mlsService, 'addUsersToExistingConversation').mockResolvedValueOnce(mlsMessage);
|
|
738
571
|
const { failedToAdd } = await conversationService.addUsersToMLSConversation({
|
|
739
572
|
qualifiedUsers,
|
|
740
573
|
groupId: mockGroupId,
|
|
741
574
|
conversationId: mockConversationId,
|
|
742
575
|
});
|
|
743
|
-
expect(failedToAdd).toEqual([keysClaimingFailure]);
|
|
744
|
-
});
|
|
745
|
-
it('recovers and retries when stale-message occurs during add users commit upload', async () => {
|
|
746
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
747
|
-
const mockGroupId = 'groupId-stale-add';
|
|
748
|
-
const mockConversationId = { id: PayloadHelper.getUUID(), domain: 'local.wire.com' };
|
|
749
|
-
const otherUsersToAdd = Array(2)
|
|
750
|
-
.fill(0)
|
|
751
|
-
.map(() => ({ id: PayloadHelper.getUUID(), domain: 'local.wire.com' }));
|
|
752
|
-
const qualifiedUsers = [...otherUsersToAdd];
|
|
753
|
-
const staleMessageError = {
|
|
754
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
755
|
-
context: {
|
|
756
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
757
|
-
context: {
|
|
758
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_STALE_MESSAGE }),
|
|
759
|
-
},
|
|
760
|
-
},
|
|
761
|
-
};
|
|
762
|
-
const getKPSpy = jest.spyOn(mlsService, 'getKeyPackagesPayload');
|
|
763
|
-
getKPSpy.mockResolvedValue({
|
|
764
|
-
keyPackages: [new Uint8Array(0)],
|
|
765
|
-
failures: [],
|
|
766
|
-
});
|
|
767
|
-
// Simulate commit upload failing once with stale, then succeeding
|
|
768
|
-
jest
|
|
769
|
-
.spyOn(mlsService, 'addUsersToExistingConversation')
|
|
770
|
-
.mockRejectedValueOnce(staleMessageError)
|
|
771
|
-
.mockResolvedValueOnce(undefined);
|
|
772
|
-
const remoteEpoch = 5;
|
|
773
|
-
const localEpoch = 4;
|
|
774
|
-
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
775
|
-
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
776
|
-
const getConvSpy = jest.spyOn(apiClient.api.conversation, 'getConversation');
|
|
777
|
-
getConvSpy.mockResolvedValue({
|
|
778
|
-
qualified_id: mockConversationId,
|
|
779
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
780
|
-
epoch: remoteEpoch,
|
|
781
|
-
group_id: mockGroupId,
|
|
782
|
-
});
|
|
783
|
-
await conversationService.addUsersToMLSConversation({
|
|
784
|
-
qualifiedUsers,
|
|
785
|
-
groupId: mockGroupId,
|
|
786
|
-
conversationId: mockConversationId,
|
|
787
|
-
});
|
|
788
|
-
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(mockConversationId);
|
|
789
|
-
expect(mlsService.addUsersToExistingConversation).toHaveBeenCalledTimes(2);
|
|
576
|
+
expect(failedToAdd).toEqual([keysClaimingFailure, addUsersFailure, mlsFailure]);
|
|
790
577
|
});
|
|
791
578
|
});
|
|
792
579
|
describe('tryEstablishingMLSGroup', () => {
|
|
@@ -833,186 +620,6 @@ describe('ConversationService', () => {
|
|
|
833
620
|
expect(conversationService.addUsersToMLSConversation).not.toHaveBeenCalled();
|
|
834
621
|
});
|
|
835
622
|
});
|
|
836
|
-
describe('reactToKeyMaterialUpdateFailure', () => {
|
|
837
|
-
function getKeyMaterialFailureHandler(mlsService) {
|
|
838
|
-
const onMock = mlsService.on;
|
|
839
|
-
const call = onMock.mock.calls.find(([event]) => event === MLSService_1.MLSServiceEvents.KEY_MATERIAL_UPDATE_FAILURE);
|
|
840
|
-
expect(call).toBeTruthy();
|
|
841
|
-
return call[1];
|
|
842
|
-
}
|
|
843
|
-
it('resets a broken MLS conversation', async () => {
|
|
844
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
845
|
-
const groupId = 'group-1';
|
|
846
|
-
const qualified_id = { id: 'conv-1', domain: 'staging.zinfra.io' };
|
|
847
|
-
jest.spyOn(apiClient.api.conversation, 'getConversationList').mockResolvedValueOnce({
|
|
848
|
-
found: [{ group_id: groupId, qualified_id, protocol: team_1.CONVERSATION_PROTOCOL.MLS, epoch: 1 }],
|
|
849
|
-
});
|
|
850
|
-
const resetSpy = jest
|
|
851
|
-
.spyOn(conversationService, 'handleBrokenMLSConversation')
|
|
852
|
-
.mockResolvedValue(undefined);
|
|
853
|
-
const addUsersSpy = jest.spyOn(conversationService, 'addUsersToMLSConversation');
|
|
854
|
-
const handler = getKeyMaterialFailureHandler(mlsService);
|
|
855
|
-
const brokenErr = {
|
|
856
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
857
|
-
context: {
|
|
858
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
859
|
-
context: {
|
|
860
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.BROKEN_MLS_CONVERSATION }),
|
|
861
|
-
},
|
|
862
|
-
},
|
|
863
|
-
};
|
|
864
|
-
await handler({ error: brokenErr, groupId });
|
|
865
|
-
expect(resetSpy).toHaveBeenCalledWith(qualified_id);
|
|
866
|
-
expect(addUsersSpy).not.toHaveBeenCalled();
|
|
867
|
-
});
|
|
868
|
-
it('adds missing users when group is out of sync', async () => {
|
|
869
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
870
|
-
const groupId = 'group-2';
|
|
871
|
-
const qualified_id = { id: 'conv-2', domain: 'staging.zinfra.io' };
|
|
872
|
-
const missingUsers = [
|
|
873
|
-
{ id: 'u1', domain: 'staging.zinfra.io' },
|
|
874
|
-
{ id: 'u2', domain: 'staging.zinfra.io' },
|
|
875
|
-
];
|
|
876
|
-
jest.spyOn(apiClient.api.conversation, 'getConversationList').mockResolvedValueOnce({
|
|
877
|
-
found: [{ group_id: groupId, qualified_id, protocol: team_1.CONVERSATION_PROTOCOL.MLS, epoch: 1 }],
|
|
878
|
-
});
|
|
879
|
-
const addUsersSpy = jest
|
|
880
|
-
.spyOn(conversationService, 'performAddUsersToMLSConversationAPI')
|
|
881
|
-
.mockResolvedValue({ conversation: {} });
|
|
882
|
-
const resetSpy = jest
|
|
883
|
-
.spyOn(conversationService, 'handleBrokenMLSConversation')
|
|
884
|
-
.mockResolvedValue(undefined);
|
|
885
|
-
const handler = getKeyMaterialFailureHandler(mlsService);
|
|
886
|
-
const outOfSyncErr = {
|
|
887
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
888
|
-
context: {
|
|
889
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
890
|
-
context: {
|
|
891
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({
|
|
892
|
-
message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_GROUP_OUT_OF_SYNC,
|
|
893
|
-
missing_users: missingUsers,
|
|
894
|
-
}),
|
|
895
|
-
},
|
|
896
|
-
},
|
|
897
|
-
};
|
|
898
|
-
await handler({ error: outOfSyncErr, groupId });
|
|
899
|
-
expect(addUsersSpy).toHaveBeenCalledWith({
|
|
900
|
-
groupId,
|
|
901
|
-
conversationId: qualified_id,
|
|
902
|
-
qualifiedUsers: missingUsers,
|
|
903
|
-
});
|
|
904
|
-
expect(resetSpy).not.toHaveBeenCalled();
|
|
905
|
-
});
|
|
906
|
-
it('deduplicates concurrent recoveries for the same group id', async () => {
|
|
907
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
908
|
-
const groupId = 'group-dup';
|
|
909
|
-
const qualified_id = { id: 'conv-dup', domain: 'staging.zinfra.io' };
|
|
910
|
-
jest.spyOn(apiClient.api.conversation, 'getConversationList').mockResolvedValue({
|
|
911
|
-
found: [{ group_id: groupId, qualified_id, protocol: team_1.CONVERSATION_PROTOCOL.MLS, epoch: 1 }],
|
|
912
|
-
});
|
|
913
|
-
// Make the recovery hang until we resolve it, to simulate overlapping calls
|
|
914
|
-
let resolveDeferred;
|
|
915
|
-
const deferred = new Promise(res => (resolveDeferred = res));
|
|
916
|
-
const resetSpy = jest
|
|
917
|
-
.spyOn(conversationService, 'handleBrokenMLSConversation')
|
|
918
|
-
.mockReturnValue(deferred);
|
|
919
|
-
const handler = getKeyMaterialFailureHandler(mlsService);
|
|
920
|
-
const brokenErr = {
|
|
921
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
922
|
-
context: {
|
|
923
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
924
|
-
context: {
|
|
925
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.BROKEN_MLS_CONVERSATION }),
|
|
926
|
-
},
|
|
927
|
-
},
|
|
928
|
-
};
|
|
929
|
-
const p1 = handler({ error: brokenErr, groupId });
|
|
930
|
-
const p2 = handler({ error: brokenErr, groupId });
|
|
931
|
-
// Complete the first recovery
|
|
932
|
-
if (resolveDeferred) {
|
|
933
|
-
resolveDeferred();
|
|
934
|
-
}
|
|
935
|
-
await Promise.allSettled([p1, p2]);
|
|
936
|
-
expect(resetSpy).toHaveBeenCalledTimes(1);
|
|
937
|
-
});
|
|
938
|
-
it('handles stale-message by rejoining via external commit and emits recovery event', async () => {
|
|
939
|
-
const [conversationService, { apiClient, mlsService }] = await buildConversationService();
|
|
940
|
-
const groupId = 'group-stale';
|
|
941
|
-
const qualified_id = { id: 'conv-stale', domain: 'staging.zinfra.io' };
|
|
942
|
-
jest.spyOn(apiClient.api.conversation, 'getConversationList').mockResolvedValueOnce({
|
|
943
|
-
found: [{ group_id: groupId, qualified_id, protocol: team_1.CONVERSATION_PROTOCOL.MLS, epoch: 2 }],
|
|
944
|
-
});
|
|
945
|
-
const handler = getKeyMaterialFailureHandler(mlsService);
|
|
946
|
-
const staleMessageError = {
|
|
947
|
-
type: core_crypto_1.ErrorType.Mls,
|
|
948
|
-
context: {
|
|
949
|
-
type: core_crypto_1.MlsErrorType.MessageRejected,
|
|
950
|
-
context: {
|
|
951
|
-
reason: (0, CoreCryptoMLSError_1.serializeAbortReason)({ message: CoreCryptoMLSError_1.UPLOAD_COMMIT_BUNDLE_ABORT_REASONS.MLS_STALE_MESSAGE }),
|
|
952
|
-
},
|
|
953
|
-
},
|
|
954
|
-
};
|
|
955
|
-
const remoteEpoch = 3;
|
|
956
|
-
const localEpoch = 2;
|
|
957
|
-
jest.spyOn(mlsService, 'conversationExists').mockResolvedValueOnce(true);
|
|
958
|
-
jest.spyOn(mlsService, 'getEpoch').mockResolvedValueOnce(localEpoch);
|
|
959
|
-
jest.spyOn(apiClient.api.conversation, 'getConversation').mockResolvedValueOnce({
|
|
960
|
-
qualified_id,
|
|
961
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
962
|
-
epoch: remoteEpoch,
|
|
963
|
-
group_id: groupId,
|
|
964
|
-
});
|
|
965
|
-
await handler({ error: staleMessageError, groupId });
|
|
966
|
-
// Expect a rejoin on stale and a recovery event
|
|
967
|
-
expect(conversationService.joinByExternalCommit).toHaveBeenCalledWith(qualified_id);
|
|
968
|
-
expect(conversationService.emit).toHaveBeenCalledWith('MLSConversationRecovered', { conversationId: qualified_id });
|
|
969
|
-
});
|
|
970
|
-
});
|
|
971
|
-
describe('groupIdConversationMap cache', () => {
|
|
972
|
-
function makeConversation(group_id, id) {
|
|
973
|
-
return {
|
|
974
|
-
group_id,
|
|
975
|
-
qualified_id: { id, domain: 'staging.zinfra.io' },
|
|
976
|
-
protocol: team_1.CONVERSATION_PROTOCOL.MLS,
|
|
977
|
-
epoch: 1,
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
it('refreshGroupIdConversationMap builds the cache from backend list', async () => {
|
|
981
|
-
const [conversationService, { apiClient }] = await buildConversationService();
|
|
982
|
-
const conv1 = makeConversation('g-1', 'c-1');
|
|
983
|
-
const conv2 = makeConversation('g-2', 'c-2');
|
|
984
|
-
const getListSpy = jest
|
|
985
|
-
.spyOn(apiClient.api.conversation, 'getConversationList')
|
|
986
|
-
.mockResolvedValueOnce({ found: [conv1, conv2] });
|
|
987
|
-
await conversationService.refreshGroupIdConversationMap();
|
|
988
|
-
expect(getListSpy).toHaveBeenCalledTimes(1);
|
|
989
|
-
// Validate through the private cache map
|
|
990
|
-
const cache = conversationService.groupIdConversationMap;
|
|
991
|
-
expect(cache.get('g-1')?.qualified_id.id).toBe('c-1');
|
|
992
|
-
expect(cache.get('g-2')?.qualified_id.id).toBe('c-2');
|
|
993
|
-
});
|
|
994
|
-
it('getConversationByGroupId uses cache when available without backend call', async () => {
|
|
995
|
-
const [conversationService, { apiClient }] = await buildConversationService();
|
|
996
|
-
const conv = makeConversation('g-hit', 'c-hit');
|
|
997
|
-
// Pre-populate cache
|
|
998
|
-
conversationService.groupIdConversationMap.set('g-hit', conv);
|
|
999
|
-
const getListSpy = jest
|
|
1000
|
-
.spyOn(apiClient.api.conversation, 'getConversationList')
|
|
1001
|
-
.mockImplementation(() => Promise.reject(new Error('should not be called')));
|
|
1002
|
-
const result = await conversationService.getConversationByGroupId('g-hit');
|
|
1003
|
-
expect(result?.qualified_id.id).toBe('c-hit');
|
|
1004
|
-
expect(getListSpy).not.toHaveBeenCalled();
|
|
1005
|
-
});
|
|
1006
|
-
it('getConversationByGroupId refreshes on cache miss and returns undefined if not found', async () => {
|
|
1007
|
-
const [conversationService, { apiClient }] = await buildConversationService();
|
|
1008
|
-
const getListSpy = jest
|
|
1009
|
-
.spyOn(apiClient.api.conversation, 'getConversationList')
|
|
1010
|
-
.mockResolvedValueOnce({ found: [makeConversation('g-else', 'c-else')] });
|
|
1011
|
-
const result = await conversationService.getConversationByGroupId('g-missing');
|
|
1012
|
-
expect(result).toBeUndefined();
|
|
1013
|
-
expect(getListSpy).toHaveBeenCalledTimes(1);
|
|
1014
|
-
});
|
|
1015
|
-
});
|
|
1016
623
|
});
|
|
1017
624
|
function generateImage() {
|
|
1018
625
|
const image = {
|
|
@@ -1029,7 +636,6 @@ function generateImage() {
|
|
|
1029
636
|
keyBytes: Buffer.from([]),
|
|
1030
637
|
sha256: Buffer.from([]),
|
|
1031
638
|
token: '',
|
|
1032
|
-
domain: 'example.com',
|
|
1033
639
|
},
|
|
1034
640
|
};
|
|
1035
641
|
}
|