Add unsupported/sse message export/import
This commit is contained in:
parent
c67a346218
commit
481928fa4f
7 changed files with 396 additions and 116 deletions
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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)) {
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue