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 hasCompletedUsernameOnboarding = 16;
PhoneNumberSharingMode phoneNumberSharingMode = 17;
ChatStyle defaultChatStyle = 18;
}
bytes profileKey = 1;
@ -228,7 +229,7 @@ message Chat {
uint64 muteUntilMs = 6;
bool markedUnread = 7;
bool dontNotifyForMentionsIfMuted = 8;
FilePointer wallpaper = 9;
ChatStyle style = 9;
}
/**
@ -725,6 +726,7 @@ message SimpleChatUpdate {
BAD_DECRYPT = 9;
PAYMENTS_ACTIVATED = 10;
PAYMENT_ACTIVATION_REQUEST = 11;
UNSUPPORTED_PROTOCOL_MESSAGE = 12;
}
Type type = 1;
@ -1008,3 +1010,79 @@ message StickerPackSticker {
string emoji = 1;
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 isIncoming = message.type === 'incoming';
if (isOutgoing) {
authorId = this.getOrPushPrivateRecipient({
serviceId: aboutMe.aci,
});
// Pacify typescript
} else if (message.sourceServiceId) {
// Pacify typescript
if (message.sourceServiceId) {
authorId = this.getOrPushPrivateRecipient({
serviceId: message.sourceServiceId,
e164: message.source,
@ -635,7 +631,15 @@ export class BackupExportStream extends Readable {
serviceId: message.sourceServiceId,
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) {
strictAssert(authorId, 'Incoming/outgoing messages require an author');
}
@ -1088,7 +1092,15 @@ export class BackupExportStream extends Readable {
}
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)) {
@ -1097,7 +1109,9 @@ export class BackupExportStream extends Readable {
}
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)) {
@ -1111,10 +1125,14 @@ export class BackupExportStream extends Readable {
}
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)) {
const { groupMigration } = message;
@ -1177,7 +1195,7 @@ export class BackupExportStream extends Readable {
updateMessage.simpleUpdate = simpleUpdate;
return { kind: NonBubbleResultKind.Directionless, patch };
return { kind: NonBubbleResultKind.Directed, patch };
}
if (isChatSessionRefreshed(message)) {

View file

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

View file

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

View file

@ -4,13 +4,18 @@
import { assert } from 'chai';
import path from 'path';
import { tmpdir } from 'os';
import { pick, sortBy } from 'lodash';
import { sortBy } from 'lodash';
import { createReadStream } from 'fs';
import { mkdtemp, rm } from 'fs/promises';
import type { MessageAttributesType } from '../../model-types';
import type {
SendStateByConversationId,
SendState,
} from '../../messages/MessageSendState';
import { backupsService } from '../../services/backups';
import { isUnsupportedMessage } from '../../state/selectors/message';
import { generateAci, generatePni } from '../../types/ServiceId';
import Data from '../../sql/Client';
import { getRandomBytes } from '../../Crypto';
@ -37,37 +42,67 @@ function sortAndNormalize(
messages: Array<MessageAttributesType>
): Array<unknown> {
return sortBy(messages, 'sent_at').map(message => {
const shallow = pick(
message,
'contact',
'conversationMerge',
'droppedGV2MemberIds',
'expirationTimerUpdate',
'flags',
'groupMigration',
'groupV2Change',
'invitedGV2Members',
'isErased',
'payment',
'profileChange',
'sent_at',
'sticker',
'timestamp',
'type',
'verified'
);
const {
changedId,
conversationId,
editHistory,
key_changed: keyChanged,
reactions,
sendStateByConversationId,
verifiedChanged,
// This is not in the backup
// eslint-disable-next-line @typescript-eslint/no-unused-vars
id: _id,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
received_at: _receivedAt,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
sourceDevice: _sourceDevice,
...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 {
...shallow,
reactions: message.reactions?.map(({ fromId, ...rest }) => {
...rest,
conversationId: mapConvoId(conversationId),
reactions: reactions?.map(({ fromId, ...restOfReaction }) => {
return {
from: mapConvoId(fromId),
...rest,
...restOfReaction,
};
}),
changedId: mapConvoId(message.changedId),
key_changed: mapConvoId(message.key_changed),
verifiedChanged: mapConvoId(message.verifiedChanged),
changedId: mapConvoId(changedId),
key_changed: mapConvoId(keyChanged),
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) {
CONVO_ID_TO_STABLE_ID.set(
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 { ContactFormType } from '../../types/EmbeddedContact';
import { DurationInSeconds } from '../../util/durations';
import { ReadStatus } from '../../messages/MessageReadStatus';
import { SeenStatus } from '../../MessageSeenStatus';
import { loadCallsHistory } from '../../services/callHistoryLoader';
import { setupBasics, symmetricRoundtripHarness } from './helpers';
import { setupBasics, symmetricRoundtripHarness, OUR_ACI } from './helpers';
const CONTACT_A = generateAci();
const GROUP_ID = Bytes.toBase64(getRandomBytes(32));
@ -57,10 +59,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
flags: Proto.DataMessage.Flags.END_SESSION,
},
]);
@ -75,6 +80,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: OUR_ACI,
},
]);
});
@ -88,6 +94,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -102,6 +109,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -117,6 +125,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -132,6 +141,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -142,10 +152,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id,
id: generateGuid(),
type: 'change-number-notification',
sourceServiceId: CONTACT_A,
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -159,6 +169,7 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -169,10 +180,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id,
id: generateGuid(),
type: 'delivery-issue',
sourceServiceId: CONTACT_A,
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
},
]);
});
@ -188,8 +199,11 @@ describe('backup/non-bubble messages', () => {
kind: PaymentEventKind.Activation,
},
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
},
]);
});
@ -205,8 +219,11 @@ describe('backup/non-bubble messages', () => {
kind: PaymentEventKind.ActivationRequest,
},
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
},
]);
});
@ -219,10 +236,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
payment: {
kind: PaymentEventKind.Notification,
note: 'note with text',
@ -239,10 +259,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
payment: {
kind: PaymentEventKind.Notification,
note: 'note with text',
@ -267,10 +290,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
contact: [
{
name: {
@ -306,10 +332,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
// TODO (DESKTOP-6845): properly handle data FilePointer
sticker: {
emoji: '👍',
@ -337,10 +366,13 @@ describe('backup/non-bubble messages', () => {
id: generateGuid(),
type: 'incoming',
received_at: 1,
received_at_ms: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
readStatus: ReadStatus.Unread,
seenStatus: SeenStatus.Unseen,
isErased: true,
},
]);
@ -375,9 +407,8 @@ describe('backup/non-bubble messages', () => {
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
changedId: contactA.id,
sourceServiceId: CONTACT_A,
profileChange: {
type: 'name',
oldName: 'Old Name',
@ -393,11 +424,10 @@ describe('backup/non-bubble messages', () => {
conversationId: contactA.id,
id: generateGuid(),
type: 'conversation-merge',
sourceServiceId: CONTACT_A,
received_at: 1,
sent_at: 1,
timestamp: 1,
sourceServiceId: CONTACT_A,
sourceDevice: 1,
conversationMerge: {
renderInfo: {
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 { sync } from 'fast-glob';
import { inspect } from 'util';
// 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 { initMessageCleanup } from '../../services/messageStateCleanup';
@ -16,6 +19,28 @@ import { initializeRedux } from '../../state/initializeRedux';
import * as Stickers from '../../types/Stickers';
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;
// This is a hack to let us run TypeScript tests in the renderer process. See the