Add unsupported/sse message export/import

This commit is contained in:
Fedor Indutny 2024-05-29 10:19:33 -07:00 committed by GitHub
parent c67a346218
commit 481928fa4f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 396 additions and 116 deletions

View file

@ -77,6 +77,7 @@ message AccountData {
bool hasSeenGroupStoryEducationSheet = 15; bool hasSeenGroupStoryEducationSheet = 15;
bool hasCompletedUsernameOnboarding = 16; bool hasCompletedUsernameOnboarding = 16;
PhoneNumberSharingMode phoneNumberSharingMode = 17; PhoneNumberSharingMode phoneNumberSharingMode = 17;
ChatStyle defaultChatStyle = 18;
} }
bytes profileKey = 1; bytes profileKey = 1;
@ -228,7 +229,7 @@ message Chat {
uint64 muteUntilMs = 6; uint64 muteUntilMs = 6;
bool markedUnread = 7; bool markedUnread = 7;
bool dontNotifyForMentionsIfMuted = 8; bool dontNotifyForMentionsIfMuted = 8;
FilePointer wallpaper = 9; ChatStyle style = 9;
} }
/** /**
@ -725,6 +726,7 @@ message SimpleChatUpdate {
BAD_DECRYPT = 9; BAD_DECRYPT = 9;
PAYMENTS_ACTIVATED = 10; PAYMENTS_ACTIVATED = 10;
PAYMENT_ACTIVATION_REQUEST = 11; PAYMENT_ACTIVATION_REQUEST = 11;
UNSUPPORTED_PROTOCOL_MESSAGE = 12;
} }
Type type = 1; Type type = 1;
@ -1008,3 +1010,79 @@ message StickerPackSticker {
string emoji = 1; string emoji = 1;
uint32 id = 2; uint32 id = 2;
} }
message ChatStyle {
message Gradient {
uint32 angle = 1; // degrees
repeated uint32 colors = 2;
repeated float positions = 3; // percent from 0 to 1
}
message AutomaticBubbleColor {
}
enum WallpaperPreset {
UNKNOWN_WALLPAPER_PRESET = 0;
SOLID_BLUSH = 1;
SOLID_COPPER = 2;
SOLID_DUST = 3;
SOLID_CELADON = 4;
SOLID_RAINFOREST = 5;
SOLID_PACIFIC = 6;
SOLID_FROST = 7;
SOLID_NAVY = 8;
SOLID_LILAC = 9;
SOLID_PINK = 10;
SOLID_EGGPLANT = 11;
SOLID_SILVER = 12;
GRADIENT_SUNSET = 13;
GRADIENT_NOIR = 14;
GRADIENT_HEATMAP = 15;
GRADIENT_AQUA = 16;
GRADIENT_IRIDESCENT = 17;
GRADIENT_MONSTERA = 18;
GRADIENT_BLISS = 19;
GRADIENT_SKY = 20;
GRADIENT_PEACH = 21;
}
enum BubbleColorPreset {
UNKNOWN_BUBBLE_COLOR_PRESET = 0;
SOLID_ULTRAMARINE = 1;
SOLID_CRIMSON = 2;
SOLID_VERMILION = 3;
SOLID_BURLAP = 4;
SOLID_FOREST = 5;
SOLID_WINTERGREEN = 6;
SOLID_TEAL = 7;
SOLID_BLUE = 8;
SOLID_INDIGO = 9;
SOLID_VIOLET = 10;
SOLID_PLUM = 11;
SOLID_TAUPE = 12;
SOLID_STEEL = 13;
GRADIENT_EMBER = 14;
GRADIENT_MIDNIGHT = 15;
GRADIENT_INFRARED = 16;
GRADIENT_LAGOON = 17;
GRADIENT_FLUORESCENT = 18;
GRADIENT_BASIL = 19;
GRADIENT_SUBLIME = 20;
GRADIENT_SEA = 21;
GRADIENT_TANGERINE = 22;
}
oneof wallpaper {
WallpaperPreset wallpaperPreset = 1;
FilePointer wallpaperPhoto = 2;
}
oneof bubbleColor {
BubbleColorPreset bubbleColorPreset = 3;
Gradient bubbleGradient = 4;
uint32 bubbleSolidColor = 5;
// Bubble setting is automatically determined based on the wallpaper setting.
AutomaticBubbleColor autoBubbleColor = 6;
}
}

View file

@ -620,12 +620,8 @@ export class BackupExportStream extends Readable {
const isOutgoing = message.type === 'outgoing'; const isOutgoing = message.type === 'outgoing';
const isIncoming = message.type === 'incoming'; const isIncoming = message.type === 'incoming';
if (isOutgoing) { // Pacify typescript
authorId = this.getOrPushPrivateRecipient({ if (message.sourceServiceId) {
serviceId: aboutMe.aci,
});
// Pacify typescript
} else if (message.sourceServiceId) {
authorId = this.getOrPushPrivateRecipient({ authorId = this.getOrPushPrivateRecipient({
serviceId: message.sourceServiceId, serviceId: message.sourceServiceId,
e164: message.source, e164: message.source,
@ -635,7 +631,15 @@ export class BackupExportStream extends Readable {
serviceId: message.sourceServiceId, serviceId: message.sourceServiceId,
e164: message.source, e164: message.source,
}); });
} else {
strictAssert(!isIncoming, 'Incoming message must have source');
// Author must be always present, even if we are directionless
authorId = this.getOrPushPrivateRecipient({
serviceId: aboutMe.aci,
});
} }
if (isOutgoing || isIncoming) { if (isOutgoing || isIncoming) {
strictAssert(authorId, 'Incoming/outgoing messages require an author'); strictAssert(authorId, 'Incoming/outgoing messages require an author');
} }
@ -1088,7 +1092,15 @@ export class BackupExportStream extends Readable {
} }
if (isPhoneNumberDiscovery(message)) { if (isPhoneNumberDiscovery(message)) {
// TODO (DESKTOP-6964): need to add to protos const e164 = message.phoneNumberDiscovery?.e164;
if (!e164) {
return { kind: NonBubbleResultKind.Drop };
}
updateMessage.sessionSwitchover = {
e164: Long.fromString(e164),
};
return { kind: NonBubbleResultKind.Directionless, patch };
} }
if (isUniversalTimerNotification(message)) { if (isUniversalTimerNotification(message)) {
@ -1097,7 +1109,9 @@ export class BackupExportStream extends Readable {
} }
if (isContactRemovedNotification(message)) { if (isContactRemovedNotification(message)) {
// TODO (DESKTOP-6964): this doesn't appear to be in the protos at all // Transient, drop it
// TODO: DESKTOP-7124
return { kind: NonBubbleResultKind.Drop };
} }
if (isGiftBadge(message)) { if (isGiftBadge(message)) {
@ -1111,10 +1125,14 @@ export class BackupExportStream extends Readable {
} }
if (isUnsupportedMessage(message)) { if (isUnsupportedMessage(message)) {
// TODO (DESKTOP-6964): need to add to protos const simpleUpdate = new Backups.SimpleChatUpdate();
} simpleUpdate.type =
Backups.SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE;
// TODO (DESKTOP-6964): session switchover updateMessage.simpleUpdate = simpleUpdate;
return { kind: NonBubbleResultKind.Directed, patch };
}
if (isGroupV1Migration(message)) { if (isGroupV1Migration(message)) {
const { groupMigration } = message; const { groupMigration } = message;
@ -1177,7 +1195,7 @@ export class BackupExportStream extends Readable {
updateMessage.simpleUpdate = simpleUpdate; updateMessage.simpleUpdate = simpleUpdate;
return { kind: NonBubbleResultKind.Directionless, patch }; return { kind: NonBubbleResultKind.Directed, patch };
} }
if (isChatSessionRefreshed(message)) { if (isChatSessionRefreshed(message)) {

View file

@ -671,12 +671,14 @@ export class BackupImportStream extends Writable {
conversation.isArchived = chat.archived === true; conversation.isArchived = chat.archived === true;
conversation.isPinned = chat.pinnedOrder != null; conversation.isPinned = chat.pinnedOrder != null;
conversation.expireTimer = chat.expirationTimerMs conversation.expireTimer =
? DurationInSeconds.fromMillis(chat.expirationTimerMs.toNumber()) chat.expirationTimerMs && !chat.expirationTimerMs.isZero()
: undefined; ? DurationInSeconds.fromMillis(chat.expirationTimerMs.toNumber())
conversation.muteExpiresAt = chat.muteUntilMs : undefined;
? getTimestampFromLong(chat.muteUntilMs) conversation.muteExpiresAt =
: undefined; chat.muteUntilMs && !chat.muteUntilMs.isZero()
? getTimestampFromLong(chat.muteUntilMs)
: undefined;
conversation.markedUnread = chat.markedUnread === true; conversation.markedUnread = chat.markedUnread === true;
conversation.dontNotifyForMentionsIfMuted = conversation.dontNotifyForMentionsIfMuted =
chat.dontNotifyForMentionsIfMuted === true; chat.dontNotifyForMentionsIfMuted === true;
@ -715,7 +717,6 @@ export class BackupImportStream extends Writable {
let attributes: MessageAttributesType = { let attributes: MessageAttributesType = {
id: generateUuid(), id: generateUuid(),
canReplyToStory: false,
conversationId: chatConvo.id, conversationId: chatConvo.id,
received_at: incrementMessageCounter(), received_at: incrementMessageCounter(),
sent_at: timestamp, sent_at: timestamp,
@ -723,13 +724,14 @@ export class BackupImportStream extends Writable {
sourceServiceId: authorConvo?.serviceId, sourceServiceId: authorConvo?.serviceId,
timestamp, timestamp,
type: item.outgoing != null ? 'outgoing' : 'incoming', type: item.outgoing != null ? 'outgoing' : 'incoming',
unidentifiedDeliveryReceived: false, expirationStartTimestamp:
expirationStartTimestamp: item.expireStartDate item.expireStartDate && !item.expireStartDate.isZero()
? getTimestampFromLong(item.expireStartDate) ? getTimestampFromLong(item.expireStartDate)
: undefined, : undefined,
expireTimer: item.expiresInMs expireTimer:
? DurationInSeconds.fromMillis(item.expiresInMs.toNumber()) item.expiresInMs && !item.expiresInMs.isZero()
: undefined, ? DurationInSeconds.fromMillis(item.expiresInMs.toNumber())
: undefined,
}; };
const additionalMessages: Array<MessageAttributesType> = []; const additionalMessages: Array<MessageAttributesType> = [];
@ -1110,9 +1112,10 @@ export class BackupImportStream extends Writable {
const { expiresInMs } = updateMessage.expirationTimerChange; const { expiresInMs } = updateMessage.expirationTimerChange;
const sourceServiceId = author?.serviceId ?? aboutMe.aci; const sourceServiceId = author?.serviceId ?? aboutMe.aci;
const expireTimer = isNumber(expiresInMs) const expireTimer =
? DurationInSeconds.fromMillis(expiresInMs) isNumber(expiresInMs) && expiresInMs
: DurationInSeconds.fromSeconds(0); ? DurationInSeconds.fromMillis(expiresInMs)
: DurationInSeconds.fromSeconds(0);
return { return {
message: { message: {
@ -1178,8 +1181,21 @@ export class BackupImportStream extends Writable {
}; };
} }
if (updateMessage.sessionSwitchover) {
const { e164 } = updateMessage.sessionSwitchover;
strictAssert(e164 != null, 'sessionSwitchover must have an old e164');
return {
message: {
type: 'phone-number-discovery',
phoneNumberDiscovery: {
e164: `+${e164}`,
},
},
additionalMessages: [],
};
}
// TODO (DESKTOP-6964): check these fields // TODO (DESKTOP-6964): check these fields
// updateMessage.sessionSwitchover
// updateMessage.callingMessage // updateMessage.callingMessage
return undefined; return undefined;
@ -1703,9 +1719,10 @@ export class BackupImportStream extends Writable {
); );
} }
const sourceServiceId = fromAciObject(Aci.fromUuidBytes(updaterAci)); const sourceServiceId = fromAciObject(Aci.fromUuidBytes(updaterAci));
const expireTimer = isNumber(expiresInMs) const expireTimer =
? DurationInSeconds.fromMillis(expiresInMs) isNumber(expiresInMs) && expiresInMs
: undefined; ? DurationInSeconds.fromMillis(expiresInMs)
: undefined;
additionalMessages.push({ additionalMessages.push({
type: 'timer-notification', type: 'timer-notification',
sourceServiceId, sourceServiceId,
@ -1841,6 +1858,13 @@ export class BackupImportStream extends Writable {
kind: PaymentEventKind.ActivationRequest, kind: PaymentEventKind.ActivationRequest,
}, },
}; };
case Type.UNSUPPORTED_PROTOCOL_MESSAGE:
return {
supportedVersionAtReceive:
SignalService.DataMessage.ProtocolVersion.CURRENT - 2,
requiredProtocolVersion:
SignalService.DataMessage.ProtocolVersion.CURRENT - 1,
};
default: default:
throw new Error('Not implemented'); throw new Error('Not implemented');
} }

View file

@ -7,6 +7,7 @@ import Data from '../../sql/Client';
import { SignalService as Proto } from '../../protobuf'; import { SignalService as Proto } from '../../protobuf';
import { generateAci, generatePni } from '../../types/ServiceId'; import { generateAci, generatePni } from '../../types/ServiceId';
import type { ServiceIdString } from '../../types/ServiceId';
import type { MessageAttributesType } from '../../model-types'; import type { MessageAttributesType } from '../../model-types';
import type { GroupV2ChangeType } from '../../groups'; import type { GroupV2ChangeType } from '../../groups';
import { getRandomBytes } from '../../Crypto'; import { getRandomBytes } from '../../Crypto';
@ -42,7 +43,13 @@ let counter = 0;
function createMessage( function createMessage(
change: GroupV2ChangeType, change: GroupV2ChangeType,
{ disableIncrement }: { disableIncrement: boolean } = { {
disableIncrement = false,
sourceServiceId = change.from || OUR_ACI,
}: {
disableIncrement?: boolean;
sourceServiceId?: ServiceIdString;
} = {
disableIncrement: false, disableIncrement: false,
} }
): MessageAttributesType { ): MessageAttributesType {
@ -60,6 +67,7 @@ function createMessage(
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
type: 'group-v2-change', type: 'group-v2-change',
sourceServiceId,
}; };
} }
@ -695,16 +703,19 @@ describe('backup/groupv2/notifications', () => {
}); });
it('MemberAddFromInvited items', async () => { it('MemberAddFromInvited items', async () => {
const firstBefore = createMessage({ const firstBefore = createMessage(
from: OUR_PNI, {
details: [ from: OUR_PNI,
{ details: [
type: 'member-add-from-invite', {
aci: OUR_ACI, type: 'member-add-from-invite',
inviter: CONTACT_B, aci: OUR_ACI,
}, inviter: CONTACT_B,
], },
}); ],
},
{ sourceServiceId: OUR_ACI }
);
const firstAfter = createMessage( const firstAfter = createMessage(
{ {
from: OUR_ACI, from: OUR_ACI,
@ -719,15 +730,18 @@ describe('backup/groupv2/notifications', () => {
{ disableIncrement: true } { disableIncrement: true }
); );
const secondBefore = createMessage({ const secondBefore = createMessage(
from: OUR_PNI, {
details: [ from: OUR_PNI,
{ details: [
type: 'member-add-from-invite', {
aci: OUR_ACI, type: 'member-add-from-invite',
}, aci: OUR_ACI,
], },
}); ],
},
{ sourceServiceId: OUR_ACI }
);
const secondAfter = createMessage( const secondAfter = createMessage(
{ {
from: OUR_ACI, from: OUR_ACI,
@ -741,15 +755,18 @@ describe('backup/groupv2/notifications', () => {
{ disableIncrement: true } { disableIncrement: true }
); );
const thirdBefore = createMessage({ const thirdBefore = createMessage(
from: CONTACT_A_PNI, {
details: [ from: CONTACT_A_PNI,
{ details: [
type: 'member-add-from-invite', {
aci: CONTACT_A, type: 'member-add-from-invite',
}, aci: CONTACT_A,
], },
}); ],
},
{ sourceServiceId: CONTACT_A }
);
const thirdAfter = createMessage( const thirdAfter = createMessage(
{ {
from: CONTACT_A, from: CONTACT_A,
@ -763,16 +780,19 @@ describe('backup/groupv2/notifications', () => {
{ disableIncrement: true } { disableIncrement: true }
); );
const fourthBefore = createMessage({ const fourthBefore = createMessage(
from: CONTACT_A_PNI, {
details: [ from: CONTACT_A_PNI,
{ details: [
type: 'member-add-from-invite', {
aci: CONTACT_A, type: 'member-add-from-invite',
pni: CONTACT_A_PNI, aci: CONTACT_A,
}, pni: CONTACT_A_PNI,
], },
}); ],
},
{ sourceServiceId: CONTACT_A }
);
const fourthAfter = createMessage( const fourthAfter = createMessage(
{ {
from: CONTACT_A, from: CONTACT_A,
@ -829,14 +849,17 @@ describe('backup/groupv2/notifications', () => {
it('MemberAddFromLink items asymmetric', async () => { it('MemberAddFromLink items asymmetric', async () => {
const before: Array<MessageAttributesType> = [ const before: Array<MessageAttributesType> = [
createMessage({ createMessage(
details: [ {
{ details: [
type: 'member-add-from-link', {
aci: CONTACT_A, type: 'member-add-from-link',
}, aci: CONTACT_A,
], },
}), ],
},
{ sourceServiceId: CONTACT_A }
),
]; ];
const after: Array<MessageAttributesType> = [ const after: Array<MessageAttributesType> = [
createMessage( createMessage(
@ -2008,6 +2031,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: CONTACT_A,
}; };
counter += 1; counter += 1;
@ -2023,6 +2047,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: CONTACT_A,
}; };
const messages: Array<MessageAttributesType> = [ const messages: Array<MessageAttributesType> = [
@ -2099,6 +2124,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
counter += 1; counter += 1;
@ -2114,6 +2140,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
counter += 1; counter += 1;
@ -2129,6 +2156,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
const messages: Array<MessageAttributesType> = [ const messages: Array<MessageAttributesType> = [
@ -2160,6 +2188,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
const legacyAfter = { const legacyAfter = {
id: generateGuid(), id: generateGuid(),
@ -2173,6 +2202,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
counter += 1; counter += 1;
@ -2191,6 +2221,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
const allDataAfter = { const allDataAfter = {
id: generateGuid(), id: generateGuid(),
@ -2204,6 +2235,7 @@ describe('backup/groupv2/notifications', () => {
received_at: counter, received_at: counter,
sent_at: counter, sent_at: counter,
timestamp: counter, timestamp: counter,
sourceServiceId: OUR_ACI,
}; };
const before = [legacyBefore, allDataBefore]; const before = [legacyBefore, allDataBefore];

View file

@ -4,13 +4,18 @@
import { assert } from 'chai'; import { assert } from 'chai';
import path from 'path'; import path from 'path';
import { tmpdir } from 'os'; import { tmpdir } from 'os';
import { pick, sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { createReadStream } from 'fs'; import { createReadStream } from 'fs';
import { mkdtemp, rm } from 'fs/promises'; import { mkdtemp, rm } from 'fs/promises';
import type { MessageAttributesType } from '../../model-types'; import type { MessageAttributesType } from '../../model-types';
import type {
SendStateByConversationId,
SendState,
} from '../../messages/MessageSendState';
import { backupsService } from '../../services/backups'; import { backupsService } from '../../services/backups';
import { isUnsupportedMessage } from '../../state/selectors/message';
import { generateAci, generatePni } from '../../types/ServiceId'; import { generateAci, generatePni } from '../../types/ServiceId';
import Data from '../../sql/Client'; import Data from '../../sql/Client';
import { getRandomBytes } from '../../Crypto'; import { getRandomBytes } from '../../Crypto';
@ -37,37 +42,67 @@ function sortAndNormalize(
messages: Array<MessageAttributesType> messages: Array<MessageAttributesType>
): Array<unknown> { ): Array<unknown> {
return sortBy(messages, 'sent_at').map(message => { return sortBy(messages, 'sent_at').map(message => {
const shallow = pick( const {
message, changedId,
'contact', conversationId,
'conversationMerge', editHistory,
'droppedGV2MemberIds', key_changed: keyChanged,
'expirationTimerUpdate', reactions,
'flags', sendStateByConversationId,
'groupMigration', verifiedChanged,
'groupV2Change',
'invitedGV2Members', // This is not in the backup
'isErased', // eslint-disable-next-line @typescript-eslint/no-unused-vars
'payment', id: _id,
'profileChange', // eslint-disable-next-line @typescript-eslint/no-unused-vars
'sent_at', received_at: _receivedAt,
'sticker', // eslint-disable-next-line @typescript-eslint/no-unused-vars
'timestamp', sourceDevice: _sourceDevice,
'type',
'verified' ...rest
); } = message;
function mapSendState(
sendState?: SendStateByConversationId
): SendStateByConversationId | undefined {
if (sendState == null) {
return undefined;
}
const result: Record<string, SendState> = {};
for (const [id, state] of Object.entries(sendState)) {
result[mapConvoId(id) ?? id] = state;
}
return result;
}
return { return {
...shallow, ...rest,
reactions: message.reactions?.map(({ fromId, ...rest }) => { conversationId: mapConvoId(conversationId),
reactions: reactions?.map(({ fromId, ...restOfReaction }) => {
return { return {
from: mapConvoId(fromId), from: mapConvoId(fromId),
...rest, ...restOfReaction,
}; };
}), }),
changedId: mapConvoId(message.changedId), changedId: mapConvoId(changedId),
key_changed: mapConvoId(message.key_changed), key_changed: mapConvoId(keyChanged),
verifiedChanged: mapConvoId(message.verifiedChanged), verifiedChanged: mapConvoId(verifiedChanged),
sendStateByConverationId: mapSendState(sendStateByConversationId),
editHistory: editHistory?.map(history => {
const {
sendStateByConversationId: historySendState,
...restOfHistory
} = history;
return {
...restOfHistory,
sendStateByConversationId: mapSendState(historySendState),
};
}),
// Not an original property, but useful
isUnsupported: isUnsupportedMessage(message),
}; };
}); });
} }
@ -83,7 +118,7 @@ async function updateConvoIdToTitle() {
for (const convo of all) { for (const convo of all) {
CONVO_ID_TO_STABLE_ID.set( CONVO_ID_TO_STABLE_ID.set(
convo.id, convo.id,
convo.serviceId ?? convo.e164 ?? convo.id convo.serviceId ?? convo.e164 ?? convo.masterKey ?? convo.id
); );
} }
} }

View file

@ -14,8 +14,10 @@ import { generateAci } from '../../types/ServiceId';
import { PaymentEventKind } from '../../types/Payment'; import { PaymentEventKind } from '../../types/Payment';
import { ContactFormType } from '../../types/EmbeddedContact'; import { ContactFormType } from '../../types/EmbeddedContact';
import { DurationInSeconds } from '../../util/durations'; import { DurationInSeconds } from '../../util/durations';
import { ReadStatus } from '../../messages/MessageReadStatus';
import { SeenStatus } from '../../MessageSeenStatus';
import { loadCallsHistory } from '../../services/callHistoryLoader'; import { loadCallsHistory } from '../../services/callHistoryLoader';
import { setupBasics, symmetricRoundtripHarness } from './helpers'; import { setupBasics, symmetricRoundtripHarness, OUR_ACI } from './helpers';
const CONTACT_A = generateAci(); const CONTACT_A = generateAci();
const GROUP_ID = Bytes.toBase64(getRandomBytes(32)); const GROUP_ID = Bytes.toBase64(getRandomBytes(32));
@ -57,10 +59,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
flags: Proto.DataMessage.Flags.END_SESSION, flags: Proto.DataMessage.Flags.END_SESSION,
}, },
]); ]);
@ -75,6 +80,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: OUR_ACI,
}, },
]); ]);
}); });
@ -88,6 +94,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -102,6 +109,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -117,6 +125,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -132,6 +141,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -142,10 +152,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id, conversationId: contactA.id,
id: generateGuid(), id: generateGuid(),
type: 'change-number-notification', type: 'change-number-notification',
sourceServiceId: CONTACT_A,
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -159,6 +169,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -169,10 +180,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id, conversationId: contactA.id,
id: generateGuid(), id: generateGuid(),
type: 'delivery-issue', type: 'delivery-issue',
sourceServiceId: CONTACT_A,
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
}, },
]); ]);
}); });
@ -188,8 +199,11 @@ describe('backup/non-bubble messages', () => {
kind: PaymentEventKind.Activation, kind: PaymentEventKind.Activation,
}, },
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
}, },
]); ]);
}); });
@ -205,8 +219,11 @@ describe('backup/non-bubble messages', () => {
kind: PaymentEventKind.ActivationRequest, kind: PaymentEventKind.ActivationRequest,
}, },
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
}, },
]); ]);
}); });
@ -219,10 +236,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
payment: { payment: {
kind: PaymentEventKind.Notification, kind: PaymentEventKind.Notification,
note: 'note with text', note: 'note with text',
@ -239,10 +259,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
payment: { payment: {
kind: PaymentEventKind.Notification, kind: PaymentEventKind.Notification,
note: 'note with text', note: 'note with text',
@ -267,10 +290,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
contact: [ contact: [
{ {
name: { name: {
@ -306,10 +332,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
// TODO (DESKTOP-6845): properly handle data FilePointer // TODO (DESKTOP-6845): properly handle data FilePointer
sticker: { sticker: {
emoji: '👍', emoji: '👍',
@ -337,10 +366,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(), id: generateGuid(),
type: 'incoming', type: 'incoming',
received_at: 1, received_at: 1,
received_at_ms: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A, sourceServiceId: CONTACT_A,
sourceDevice: 1, sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
isErased: true, isErased: true,
}, },
]); ]);
@ -375,9 +407,8 @@ describe('backup/non-bubble messages', () => {
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
changedId: contactA.id, changedId: contactA.id,
sourceServiceId: CONTACT_A,
profileChange: { profileChange: {
type: 'name', type: 'name',
oldName: 'Old Name', oldName: 'Old Name',
@ -393,11 +424,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id, conversationId: contactA.id,
id: generateGuid(), id: generateGuid(),
type: 'conversation-merge', type: 'conversation-merge',
sourceServiceId: CONTACT_A,
received_at: 1, received_at: 1,
sent_at: 1, sent_at: 1,
timestamp: 1, timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
conversationMerge: { conversationMerge: {
renderInfo: { renderInfo: {
type: 'private', type: 'private',
@ -407,4 +437,42 @@ describe('backup/non-bubble messages', () => {
}, },
]); ]);
}); });
it('roundtrips session switchover', async () => {
await symmetricRoundtripHarness([
{
conversationId: contactA.id,
id: generateGuid(),
type: 'phone-number-discovery',
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
phoneNumberDiscovery: {
e164: '+12125551234',
},
},
]);
});
// TODO: DESKTOP-7122
it.skip('roundtrips unsupported message', async () => {
await symmetricRoundtripHarness([
{
conversationId: contactA.id,
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
sent_at: 1,
timestamp: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
supportedVersionAtReceive: 1,
requiredProtocolVersion: 2,
},
]);
});
}); });

View file

@ -5,9 +5,12 @@
import { ipcRenderer as ipc } from 'electron'; import { ipcRenderer as ipc } from 'electron';
import { sync } from 'fast-glob'; import { sync } from 'fast-glob';
import { inspect } from 'util';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import { assert } from 'chai'; import { assert, config as chaiConfig } from 'chai';
// eslint-disable-next-line import/no-extraneous-dependencies
import { reporters } from 'mocha';
import { getSignalProtocolStore } from '../../SignalProtocolStore'; import { getSignalProtocolStore } from '../../SignalProtocolStore';
import { initMessageCleanup } from '../../services/messageStateCleanup'; import { initMessageCleanup } from '../../services/messageStateCleanup';
@ -16,6 +19,28 @@ import { initializeRedux } from '../../state/initializeRedux';
import * as Stickers from '../../types/Stickers'; import * as Stickers from '../../types/Stickers';
import { ThemeType } from '../../types/Util'; import { ThemeType } from '../../types/Util';
// Show actual objects instead of abbreviated errors
chaiConfig.truncateThreshold = 0;
function patchDeepEqual(method: 'deepEqual' | 'deepStrictEqual'): void {
const originalFn = assert[method];
assert[method] = (...args) => {
try {
return originalFn(...args);
} catch (error) {
reporters.base.useColors = false;
error.message = reporters.base.generateDiff(
inspect(error.actual, { depth: Infinity, sorted: true }),
inspect(error.expected, { depth: Infinity, sorted: true })
);
throw error;
}
};
}
patchDeepEqual('deepEqual');
patchDeepEqual('deepStrictEqual');
window.assert = assert; window.assert = assert;
// This is a hack to let us run TypeScript tests in the renderer process. See the // This is a hack to let us run TypeScript tests in the renderer process. See the