207 lines
6.2 KiB
TypeScript
207 lines
6.2 KiB
TypeScript
|
// Copyright 2023 Signal Messenger, LLC
|
||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||
|
|
||
|
import uuid from 'uuid';
|
||
|
import { assert } from 'chai';
|
||
|
|
||
|
import { type AciString, generateAci } from '../types/ServiceId';
|
||
|
import type { MessageAttributesType } from '../model-types';
|
||
|
import { SendStatus } from '../messages/MessageSendState';
|
||
|
import {
|
||
|
type MessageReceiptAttributesType,
|
||
|
MessageReceiptType,
|
||
|
onReceipt,
|
||
|
} from '../messageModifiers/MessageReceipts';
|
||
|
import { ReadStatus } from '../messages/MessageReadStatus';
|
||
|
|
||
|
describe('MessageReceipts', () => {
|
||
|
let ourAci: AciString;
|
||
|
|
||
|
beforeEach(async () => {
|
||
|
ourAci = generateAci();
|
||
|
await window.textsecure.storage.put('uuid_id', `${ourAci}.1`);
|
||
|
await window.textsecure.storage.put('read-receipt-setting', true);
|
||
|
await window.ConversationController.load();
|
||
|
});
|
||
|
|
||
|
function generateReceipt(
|
||
|
sourceConversationId: string,
|
||
|
messageSentAt: number,
|
||
|
type: MessageReceiptType
|
||
|
): MessageReceiptAttributesType {
|
||
|
return {
|
||
|
envelopeId: uuid(),
|
||
|
messageSentAt,
|
||
|
receiptTimestamp: 1,
|
||
|
removeFromMessageReceiverCache: () => null,
|
||
|
sourceConversationId,
|
||
|
sourceDevice: 1,
|
||
|
sourceServiceId: generateAci(),
|
||
|
type,
|
||
|
wasSentEncrypted: true,
|
||
|
};
|
||
|
}
|
||
|
it('processes all receipts in a batch', async () => {
|
||
|
const id = uuid();
|
||
|
const sentAt = Date.now();
|
||
|
|
||
|
const messageAttributes: MessageAttributesType = {
|
||
|
conversationId: uuid(),
|
||
|
id,
|
||
|
received_at: 1,
|
||
|
sent_at: sentAt,
|
||
|
timestamp: sentAt,
|
||
|
type: 'outgoing',
|
||
|
sendStateByConversationId: {
|
||
|
aaaa: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
bbbb: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
cccc: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
dddd: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
|
||
|
await window.Signal.Data.saveMessage(messageAttributes, {
|
||
|
forceSave: true,
|
||
|
ourAci,
|
||
|
});
|
||
|
|
||
|
await Promise.all([
|
||
|
onReceipt(generateReceipt('aaaa', sentAt, MessageReceiptType.Delivery)),
|
||
|
onReceipt(generateReceipt('bbbb', sentAt, MessageReceiptType.Delivery)),
|
||
|
onReceipt(generateReceipt('cccc', sentAt, MessageReceiptType.Read)),
|
||
|
onReceipt(generateReceipt('aaaa', sentAt, MessageReceiptType.Read)),
|
||
|
]);
|
||
|
|
||
|
const messageFromDatabase = await window.Signal.Data.getMessageById(id);
|
||
|
const savedSendState = messageFromDatabase?.sendStateByConversationId;
|
||
|
|
||
|
assert.equal(savedSendState?.aaaa.status, SendStatus.Read, 'aaaa');
|
||
|
assert.equal(savedSendState?.bbbb.status, SendStatus.Delivered, 'bbbb');
|
||
|
assert.equal(savedSendState?.cccc.status, SendStatus.Read, 'cccc');
|
||
|
assert.equal(savedSendState?.dddd.status, SendStatus.Sent, 'dddd');
|
||
|
});
|
||
|
|
||
|
it('updates sendStateByConversationId for edits', async () => {
|
||
|
const id = uuid();
|
||
|
const sentAt = Date.now();
|
||
|
const editedSentAt = sentAt + 1000;
|
||
|
const defaultSendState = {
|
||
|
aaaa: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
bbbb: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
cccc: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
dddd: {
|
||
|
status: SendStatus.Sent,
|
||
|
updatedAt: Date.now(),
|
||
|
},
|
||
|
};
|
||
|
|
||
|
const messageAttributes: MessageAttributesType = {
|
||
|
conversationId: uuid(),
|
||
|
id,
|
||
|
received_at: 1,
|
||
|
sent_at: sentAt,
|
||
|
timestamp: sentAt,
|
||
|
editMessageTimestamp: editedSentAt,
|
||
|
type: 'outgoing',
|
||
|
sendStateByConversationId: defaultSendState,
|
||
|
editHistory: [
|
||
|
{
|
||
|
sendStateByConversationId: defaultSendState,
|
||
|
timestamp: editedSentAt,
|
||
|
},
|
||
|
{
|
||
|
sendStateByConversationId: defaultSendState,
|
||
|
timestamp: sentAt,
|
||
|
},
|
||
|
],
|
||
|
};
|
||
|
|
||
|
await window.Signal.Data.saveMessage(messageAttributes, {
|
||
|
forceSave: true,
|
||
|
ourAci,
|
||
|
});
|
||
|
await window.Signal.Data.saveEditedMessage(messageAttributes, ourAci, {
|
||
|
conversationId: messageAttributes.conversationId,
|
||
|
messageId: messageAttributes.id,
|
||
|
readStatus: ReadStatus.Read,
|
||
|
sentAt: editedSentAt,
|
||
|
});
|
||
|
|
||
|
await Promise.all([
|
||
|
// send receipts for original message
|
||
|
onReceipt(generateReceipt('aaaa', sentAt, MessageReceiptType.Delivery)),
|
||
|
onReceipt(generateReceipt('bbbb', sentAt, MessageReceiptType.Delivery)),
|
||
|
onReceipt(generateReceipt('cccc', sentAt, MessageReceiptType.Read)),
|
||
|
onReceipt(generateReceipt('aaaa', sentAt, MessageReceiptType.Read)),
|
||
|
|
||
|
// and send receipts for edited message
|
||
|
onReceipt(
|
||
|
generateReceipt('aaaa', editedSentAt, MessageReceiptType.Delivery)
|
||
|
),
|
||
|
onReceipt(
|
||
|
generateReceipt('bbbb', editedSentAt, MessageReceiptType.Delivery)
|
||
|
),
|
||
|
onReceipt(generateReceipt('cccc', editedSentAt, MessageReceiptType.Read)),
|
||
|
onReceipt(generateReceipt('bbbb', editedSentAt, MessageReceiptType.Read)),
|
||
|
]);
|
||
|
|
||
|
const messageFromDatabase = await window.Signal.Data.getMessageById(id);
|
||
|
const rootSendState = messageFromDatabase?.sendStateByConversationId;
|
||
|
|
||
|
assert.deepEqual(
|
||
|
rootSendState,
|
||
|
messageFromDatabase?.editHistory?.[0].sendStateByConversationId,
|
||
|
'edit history version should match root version'
|
||
|
);
|
||
|
assert.equal(rootSendState?.aaaa.status, SendStatus.Delivered, 'aaaa');
|
||
|
assert.equal(rootSendState?.bbbb.status, SendStatus.Read, 'bbbb');
|
||
|
assert.equal(rootSendState?.cccc.status, SendStatus.Read, 'cccc');
|
||
|
assert.equal(rootSendState?.dddd.status, SendStatus.Sent, 'dddd');
|
||
|
|
||
|
const originalMessageSendState =
|
||
|
messageFromDatabase?.editHistory?.[1].sendStateByConversationId;
|
||
|
|
||
|
assert.equal(
|
||
|
originalMessageSendState?.aaaa.status,
|
||
|
SendStatus.Read,
|
||
|
'original-aaaa'
|
||
|
);
|
||
|
assert.equal(
|
||
|
originalMessageSendState?.bbbb.status,
|
||
|
SendStatus.Delivered,
|
||
|
'original-bbbb'
|
||
|
);
|
||
|
assert.equal(
|
||
|
originalMessageSendState?.cccc.status,
|
||
|
SendStatus.Read,
|
||
|
'original-cccc'
|
||
|
);
|
||
|
assert.equal(
|
||
|
originalMessageSendState?.dddd.status,
|
||
|
SendStatus.Sent,
|
||
|
'original-dddd'
|
||
|
);
|
||
|
});
|
||
|
});
|