@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.
Files changed (195) hide show
  1. package/lib/Account.d.ts +51 -168
  2. package/lib/Account.d.ts.map +1 -1
  3. package/lib/Account.js +127 -517
  4. package/lib/Account.test.js +147 -158
  5. package/lib/broadcast/AvailabilityType.d.ts +1 -1
  6. package/lib/broadcast/AvailabilityType.d.ts.map +1 -1
  7. package/lib/broadcast/BroadcastService.d.ts +1 -1
  8. package/lib/broadcast/BroadcastService.d.ts.map +1 -1
  9. package/lib/broadcast/BroadcastService.js +1 -1
  10. package/lib/client/ClientService.d.ts +3 -4
  11. package/lib/client/ClientService.d.ts.map +1 -1
  12. package/lib/client/ClientService.js +5 -19
  13. package/lib/conversation/AbortReason.d.ts +1 -1
  14. package/lib/conversation/AbortReason.d.ts.map +1 -1
  15. package/lib/conversation/AssetService/AssetService.d.ts +30 -12
  16. package/lib/conversation/AssetService/AssetService.d.ts.map +1 -1
  17. package/lib/conversation/AssetService/AssetService.js +10 -1
  18. package/lib/conversation/AssetService/AssetService.test.js +3 -8
  19. package/lib/conversation/ClientActionType.d.ts +1 -1
  20. package/lib/conversation/ClientActionType.d.ts.map +1 -1
  21. package/lib/conversation/ClientActionType.js +1 -1
  22. package/lib/conversation/ConversationService/ConversationService.d.ts +14 -98
  23. package/lib/conversation/ConversationService/ConversationService.d.ts.map +1 -1
  24. package/lib/conversation/ConversationService/ConversationService.js +101 -314
  25. package/lib/conversation/ConversationService/ConversationService.test.js +47 -441
  26. package/lib/conversation/ConversationService/ConversationService.types.d.ts +4 -5
  27. package/lib/conversation/ConversationService/ConversationService.types.d.ts.map +1 -1
  28. package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.d.ts.map +1 -1
  29. package/lib/conversation/ConversationService/Utility/getConversationQualifiedMembers.js +3 -6
  30. package/lib/conversation/SubconversationService/SubconversationService.d.ts.map +1 -1
  31. package/lib/conversation/SubconversationService/SubconversationService.js +11 -158
  32. package/lib/conversation/SubconversationService/SubconversationService.test.js +2 -8
  33. package/lib/conversation/content/AssetContent.d.ts +1 -1
  34. package/lib/conversation/content/AssetContent.d.ts.map +1 -1
  35. package/lib/conversation/content/ButtonActionConfirmationContent.d.ts +1 -1
  36. package/lib/conversation/content/ButtonActionConfirmationContent.d.ts.map +1 -1
  37. package/lib/conversation/content/ButtonActionContent.d.ts +1 -1
  38. package/lib/conversation/content/ButtonActionContent.d.ts.map +1 -1
  39. package/lib/conversation/content/ClearedContent.d.ts +1 -1
  40. package/lib/conversation/content/ClearedContent.d.ts.map +1 -1
  41. package/lib/conversation/content/ClientActionContent.d.ts +1 -1
  42. package/lib/conversation/content/ClientActionContent.d.ts.map +1 -1
  43. package/lib/conversation/content/CompositeContent.d.ts +1 -1
  44. package/lib/conversation/content/CompositeContent.d.ts.map +1 -1
  45. package/lib/conversation/content/ConfirmationContent.d.ts +1 -1
  46. package/lib/conversation/content/ConfirmationContent.d.ts.map +1 -1
  47. package/lib/conversation/content/DeletedContent.d.ts +1 -1
  48. package/lib/conversation/content/DeletedContent.d.ts.map +1 -1
  49. package/lib/conversation/content/HiddenContent.d.ts +1 -1
  50. package/lib/conversation/content/HiddenContent.d.ts.map +1 -1
  51. package/lib/conversation/content/KnockContent.d.ts +1 -1
  52. package/lib/conversation/content/KnockContent.d.ts.map +1 -1
  53. package/lib/conversation/content/LinkPreviewContent.d.ts +1 -1
  54. package/lib/conversation/content/LinkPreviewContent.d.ts.map +1 -1
  55. package/lib/conversation/content/MentionContent.d.ts +1 -1
  56. package/lib/conversation/content/MentionContent.d.ts.map +1 -1
  57. package/lib/conversation/content/MultipartContent.d.ts +1 -1
  58. package/lib/conversation/content/MultipartContent.d.ts.map +1 -1
  59. package/lib/conversation/content/QuoteContent.d.ts +1 -1
  60. package/lib/conversation/content/QuoteContent.d.ts.map +1 -1
  61. package/lib/conversation/content/TweetContent.d.ts +1 -1
  62. package/lib/conversation/content/TweetContent.d.ts.map +1 -1
  63. package/lib/conversation/content/index.d.ts +1 -1
  64. package/lib/conversation/content/index.d.ts.map +1 -1
  65. package/lib/conversation/content/index.js +1 -1
  66. package/lib/conversation/message/MessageBuilder.d.ts +1 -1
  67. package/lib/conversation/message/MessageBuilder.d.ts.map +1 -1
  68. package/lib/conversation/message/MessageBuilder.js +1 -1
  69. package/lib/conversation/message/MessageService.d.ts.map +1 -1
  70. package/lib/conversation/message/MessageService.js +1 -1
  71. package/lib/conversation/message/MessageService.test.js +1 -7
  72. package/lib/conversation/message/MessageToProtoMapper.d.ts +1 -1
  73. package/lib/conversation/message/MessageToProtoMapper.d.ts.map +1 -1
  74. package/lib/conversation/message/MessageToProtoMapper.js +1 -1
  75. package/lib/conversation/message/messageSender.js +2 -2
  76. package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts +2 -2
  77. package/lib/cryptography/AssetCryptography/EncryptedAsset.d.ts.map +1 -1
  78. package/lib/messagingProtocols/common.types.d.ts +0 -9
  79. package/lib/messagingProtocols/common.types.d.ts.map +1 -1
  80. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts +2 -2
  81. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.d.ts.map +1 -1
  82. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIService.types.js +1 -2
  83. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts +1 -1
  84. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.d.ts.map +1 -1
  85. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.js +11 -13
  86. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceExternal.test.js +16 -21
  87. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts +3 -9
  88. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.d.ts.map +1 -1
  89. package/lib/messagingProtocols/mls/E2EIdentityService/E2EIServiceInternal.js +12 -31
  90. package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts +0 -6
  91. package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.d.ts.map +1 -1
  92. package/lib/messagingProtocols/mls/E2EIdentityService/Helper/index.js +1 -19
  93. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts +4 -0
  94. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.d.ts.map +1 -0
  95. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/IncomingMesssagesQueue.js +69 -0
  96. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingMessagesQueue/index.d.ts +2 -0
  97. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingProposalsQueue → IncomingMessagesQueue}/index.d.ts.map +1 -1
  98. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/{IncomingProposalsQueue → IncomingMessagesQueue}/index.js +1 -1
  99. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts +1 -0
  100. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.d.ts.map +1 -1
  101. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/index.js +1 -0
  102. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.d.ts.map +1 -1
  103. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.js +14 -23
  104. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts +2 -0
  105. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.d.ts.map +1 -0
  106. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/messageAdd.test.js +98 -0
  107. package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.d.ts.map +1 -1
  108. package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.js +2 -5
  109. package/lib/messagingProtocols/mls/EventHandler/events/welcomeMessage/welcomeMessage.test.js +3 -13
  110. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts +2 -38
  111. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.d.ts.map +1 -1
  112. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.js +6 -41
  113. package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts +34 -38
  114. package/lib/messagingProtocols/mls/MLSService/MLSService.d.ts.map +1 -1
  115. package/lib/messagingProtocols/mls/MLSService/MLSService.js +208 -267
  116. package/lib/messagingProtocols/mls/MLSService/MLSService.test.js +160 -157
  117. package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.js +3 -3
  118. package/lib/messagingProtocols/mls/MLSService/commitBundleUtil.test.js +5 -5
  119. package/lib/messagingProtocols/mls/conversationRejoinQueue.js +2 -2
  120. package/lib/messagingProtocols/mls/types.d.ts +8 -0
  121. package/lib/messagingProtocols/mls/types.d.ts.map +1 -1
  122. package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.d.ts.map +1 -1
  123. package/lib/messagingProtocols/proteus/EventHandler/events/otrMessageAdd/otrMessageAdd.js +1 -7
  124. package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts +15 -8
  125. package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.d.ts.map +1 -1
  126. package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CoreCryptoWrapper/CoreCryptoWrapper.js +62 -97
  127. package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts +6 -0
  128. package/lib/messagingProtocols/proteus/ProteusService/CryptoClient/CryptoClient.types.d.ts.map +1 -1
  129. package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts +6 -1
  130. package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.d.ts.map +1 -1
  131. package/lib/messagingProtocols/proteus/ProteusService/DecryptionErrorGenerator/DecryptionErrorGenerator.js +22 -19
  132. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts +3 -5
  133. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.d.ts.map +1 -1
  134. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.js +24 -11
  135. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts +0 -1
  136. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.d.ts.map +1 -1
  137. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.mocks.js +2 -11
  138. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.test.js +9 -13
  139. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts +2 -3
  140. package/lib/messagingProtocols/proteus/ProteusService/ProteusService.types.d.ts.map +1 -1
  141. package/lib/messagingProtocols/proteus/ProteusService/WithMockedGenerics.test.js +4 -11
  142. package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts +4 -0
  143. package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.d.ts.map +1 -1
  144. package/lib/messagingProtocols/proteus/ProteusService/cryptoMigrationStateStore.js +5 -0
  145. package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts +1 -2
  146. package/lib/messagingProtocols/proteus/ProteusService/identityClearer.d.ts.map +1 -1
  147. package/lib/messagingProtocols/proteus/ProteusService/identityClearer.js +2 -8
  148. package/lib/messagingProtocols/proteus/Utility/SessionHandler/SessionHandler.test.js +0 -4
  149. package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts +1 -1
  150. package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.d.ts.map +1 -1
  151. package/lib/messagingProtocols/proteus/Utility/getGenericMessageParams.js +1 -1
  152. package/lib/notification/NotificationService.d.ts +6 -20
  153. package/lib/notification/NotificationService.d.ts.map +1 -1
  154. package/lib/notification/NotificationService.js +14 -23
  155. package/lib/notification/NotificationService.test.js +0 -8
  156. package/lib/secretStore/secretKeyGenerator.d.ts +0 -1
  157. package/lib/secretStore/secretKeyGenerator.d.ts.map +1 -1
  158. package/lib/secretStore/secretKeyGenerator.js +1 -3
  159. package/lib/self/SelfService.d.ts +2 -2
  160. package/lib/self/SelfService.d.ts.map +1 -1
  161. package/lib/self/SelfService.test.js +2 -5
  162. package/lib/team/TeamService.d.ts +2 -5
  163. package/lib/team/TeamService.d.ts.map +1 -1
  164. package/lib/team/TeamService.js +2 -12
  165. package/lib/user/UserService.d.ts +2 -2
  166. package/lib/user/UserService.d.ts.map +1 -1
  167. package/lib/user/UserService.js +3 -3
  168. package/lib/util/TypePredicateUtil.d.ts.map +1 -1
  169. package/lib/util/TypePredicateUtil.js +2 -2
  170. package/package.json +3 -3
  171. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts +0 -7
  172. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.d.ts.map +0 -1
  173. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/IncomingProposalsQueue.js +0 -48
  174. package/lib/messagingProtocols/mls/EventHandler/events/messageAdd/IncomingProposalsQueue/index.d.ts +0 -2
  175. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts +0 -2
  176. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.d.ts.map +0 -1
  177. package/lib/messagingProtocols/mls/MLSService/CoreCryptoMLSError.test.js +0 -124
  178. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts +0 -78
  179. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.d.ts.map +0 -1
  180. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.js +0 -173
  181. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts +0 -2
  182. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.d.ts.map +0 -1
  183. package/lib/messagingProtocols/mls/recovery/MlsErrorMapper.test.js +0 -117
  184. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts +0 -167
  185. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.d.ts.map +0 -1
  186. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.js +0 -317
  187. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts +0 -2
  188. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.d.ts.map +0 -1
  189. package/lib/messagingProtocols/mls/recovery/MlsRecoveryOrchestrator.test.js +0 -248
  190. package/lib/messagingProtocols/mls/recovery/index.d.ts +0 -5
  191. package/lib/messagingProtocols/mls/recovery/index.d.ts.map +0 -1
  192. package/lib/messagingProtocols/mls/recovery/index.js +0 -28
  193. package/lib/test/StoreHelper.d.ts +0 -2
  194. package/lib/test/StoreHelper.d.ts.map +0 -1
  195. 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([team_1.CONVERSATION_PROTOCOL.MLS, team_1.CONVERSATION_PROTOCOL.PROTEUS]));
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, () => Promise.resolve(true), mockedMLSService);
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: team_1.CONVERSATION_PROTOCOL.PROTEUS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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
- const staleMessageError = new conversation_1.MLSStaleMessageError('', http_1.BackendErrorLabel.MLS_STALE_MESSAGE, http_status_codes_1.StatusCodes.CONFLICT);
321
- jest.spyOn(apiClient.api.conversation, 'postMlsMessage').mockRejectedValueOnce(staleMessageError);
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: team_1.CONVERSATION_PROTOCOL.MLS,
207
+ protocol: conversation_1.ConversationProtocol.MLS,
329
208
  epoch: remoteEpoch,
330
209
  group_id: mockGroupId,
331
210
  });
332
211
  await conversationService.send({
333
- protocol: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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: team_1.CONVERSATION_PROTOCOL.MLS,
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
- // Simulate core-crypto orphan welcome classification for mapper
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
- // Ensure welcome processing is attempted (succeeds after recovery join)
594
- jest.spyOn(mlsService, 'handleMLSWelcomeMessageEvent').mockResolvedValueOnce(undefined);
595
- await conversationService.handleEvent(mockMLSWelcomeMessageEvent);
596
- await new Promise(resolve => setImmediate(resolve));
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
- // Expect a single wipe with the extracted group id, and a single retry of welcome handling
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: team_1.CONVERSATION_PROTOCOL.MLS,
517
+ protocol: conversation_1.ConversationProtocol.MLS,
696
518
  epoch: 1,
697
519
  group_id: mockGroupId,
698
520
  });
699
- jest.spyOn(mlsService, 'addUsersToExistingConversation');
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, undefined);
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([team_1.CONVERSATION_PROTOCOL.PROTEUS]);
555
+ return Promise.resolve([conversation_1.ConversationProtocol.PROTEUS]);
724
556
  }
725
- return Promise.resolve([team_1.CONVERSATION_PROTOCOL.MLS, team_1.CONVERSATION_PROTOCOL.PROTEUS]);
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: team_1.CONVERSATION_PROTOCOL.MLS,
565
+ protocol: conversation_1.ConversationProtocol.MLS,
734
566
  epoch: 1,
735
567
  group_id: mockGroupId,
736
568
  });
737
- jest.spyOn(mlsService, 'addUsersToExistingConversation');
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
  }