Replace MessageController with MessageCache
This commit is contained in:
parent
ba1a8aad09
commit
7d35216fda
73 changed files with 2237 additions and 1229 deletions
|
@ -83,7 +83,11 @@ describe('Conversations', () => {
|
|||
forceSave: true,
|
||||
ourAci,
|
||||
});
|
||||
message = window.MessageController.register(message.id, message);
|
||||
message = window.MessageCache.__DEPRECATED$register(
|
||||
message.id,
|
||||
message,
|
||||
'test'
|
||||
);
|
||||
await window.Signal.Data.updateConversation(conversation.attributes);
|
||||
await conversation.updateLastMessage();
|
||||
|
||||
|
|
|
@ -5,17 +5,30 @@ import { assert } from 'chai';
|
|||
import * as sinon from 'sinon';
|
||||
import { v4 as generateUuid } from 'uuid';
|
||||
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import type { AttachmentType } from '../../types/Attachment';
|
||||
import type { CallbackResultType } from '../../textsecure/Types.d';
|
||||
import type { ConversationModel } from '../../models/conversations';
|
||||
import type { MessageAttributesType } from '../../model-types.d';
|
||||
import type { MessageModel } from '../../models/messages';
|
||||
import type { RawBodyRange } from '../../types/BodyRange';
|
||||
import type { StorageAccessType } from '../../types/Storage.d';
|
||||
import type { WebAPIType } from '../../textsecure/WebAPI';
|
||||
import MessageSender from '../../textsecure/SendMessage';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { SendStatus } from '../../messages/MessageSendState';
|
||||
import MessageSender from '../../textsecure/SendMessage';
|
||||
import type { WebAPIType } from '../../textsecure/WebAPI';
|
||||
import type { CallbackResultType } from '../../textsecure/Types.d';
|
||||
import type { StorageAccessType } from '../../types/Storage.d';
|
||||
import { generateAci } from '../../types/ServiceId';
|
||||
import { SignalService as Proto } from '../../protobuf';
|
||||
import { generateAci } from '../../types/ServiceId';
|
||||
import { getContact } from '../../messages/helpers';
|
||||
import type { ConversationModel } from '../../models/conversations';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import {
|
||||
APPLICATION_JSON,
|
||||
AUDIO_MP3,
|
||||
IMAGE_GIF,
|
||||
IMAGE_PNG,
|
||||
LONG_MESSAGE,
|
||||
TEXT_ATTACHMENT,
|
||||
VIDEO_MP4,
|
||||
} from '../../types/MIME';
|
||||
|
||||
describe('Message', () => {
|
||||
const STORAGE_KEYS_TO_RESTORE: Array<keyof StorageAccessType> = [
|
||||
|
@ -28,7 +41,7 @@ describe('Message', () => {
|
|||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
const attributes = {
|
||||
type: 'outgoing',
|
||||
type: 'outgoing' as const,
|
||||
body: 'hi',
|
||||
conversationId: 'foo',
|
||||
attachments: [],
|
||||
|
@ -39,12 +52,12 @@ describe('Message', () => {
|
|||
const me = '+14155555556';
|
||||
const ourServiceId = generateAci();
|
||||
|
||||
function createMessage(attrs: { [key: string]: unknown }) {
|
||||
const messages = new window.Whisper.MessageCollection();
|
||||
return messages.add({
|
||||
received_at: Date.now(),
|
||||
function createMessage(attrs: Partial<MessageAttributesType>): MessageModel {
|
||||
return new window.Whisper.Message({
|
||||
id: generateUuid(),
|
||||
...attrs,
|
||||
});
|
||||
received_at: Date.now(),
|
||||
} as MessageAttributesType);
|
||||
}
|
||||
|
||||
function createMessageAndGetNotificationData(attrs: {
|
||||
|
@ -214,14 +227,12 @@ describe('Message', () => {
|
|||
|
||||
describe('getContact', () => {
|
||||
it('gets outgoing contact', () => {
|
||||
const messages = new window.Whisper.MessageCollection();
|
||||
const message = messages.add(attributes);
|
||||
const message = createMessage(attributes);
|
||||
assert.exists(getContact(message.attributes));
|
||||
});
|
||||
|
||||
it('gets incoming contact', () => {
|
||||
const messages = new window.Whisper.MessageCollection();
|
||||
const message = messages.add({
|
||||
const message = createMessage({
|
||||
type: 'incoming',
|
||||
source,
|
||||
});
|
||||
|
@ -287,7 +298,8 @@ describe('Message', () => {
|
|||
isErased: false,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'image/png',
|
||||
contentType: IMAGE_PNG,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
@ -302,7 +314,8 @@ describe('Message', () => {
|
|||
isErased: false,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'video/mp4',
|
||||
contentType: VIDEO_MP4,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
@ -317,7 +330,8 @@ describe('Message', () => {
|
|||
isErased: false,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'text/plain',
|
||||
contentType: LONG_MESSAGE,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
@ -482,7 +496,7 @@ describe('Message', () => {
|
|||
createMessageAndGetNotificationData({
|
||||
type: 'incoming',
|
||||
source,
|
||||
flags: true,
|
||||
flags: 1,
|
||||
}),
|
||||
{ text: i18n('icu:sessionEnded') }
|
||||
);
|
||||
|
@ -493,17 +507,26 @@ describe('Message', () => {
|
|||
createMessageAndGetNotificationData({
|
||||
type: 'incoming',
|
||||
source,
|
||||
errors: [{}],
|
||||
errors: [new Error()],
|
||||
}),
|
||||
{ text: i18n('icu:incomingError') }
|
||||
);
|
||||
});
|
||||
|
||||
const attachmentTestCases = [
|
||||
const attachmentTestCases: Array<{
|
||||
title: string;
|
||||
attachment: AttachmentType;
|
||||
expectedResult: {
|
||||
text: string;
|
||||
emoji: string;
|
||||
bodyRanges?: Array<RawBodyRange>;
|
||||
};
|
||||
}> = [
|
||||
{
|
||||
title: 'GIF',
|
||||
attachment: {
|
||||
contentType: 'image/gif',
|
||||
contentType: IMAGE_GIF,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'GIF',
|
||||
|
@ -514,7 +537,8 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'photo',
|
||||
attachment: {
|
||||
contentType: 'image/png',
|
||||
contentType: IMAGE_PNG,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'Photo',
|
||||
|
@ -525,7 +549,8 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'video',
|
||||
attachment: {
|
||||
contentType: 'video/mp4',
|
||||
contentType: VIDEO_MP4,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'Video',
|
||||
|
@ -536,8 +561,9 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'voice message',
|
||||
attachment: {
|
||||
contentType: 'audio/ogg',
|
||||
contentType: AUDIO_MP3,
|
||||
flags: Proto.AttachmentPointer.Flags.VOICE_MESSAGE,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'Voice Message',
|
||||
|
@ -548,8 +574,9 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'audio message',
|
||||
attachment: {
|
||||
contentType: 'audio/ogg',
|
||||
fileName: 'audio.ogg',
|
||||
contentType: AUDIO_MP3,
|
||||
fileName: 'audio.mp3',
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'Audio Message',
|
||||
|
@ -560,7 +587,8 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'plain text',
|
||||
attachment: {
|
||||
contentType: 'text/plain',
|
||||
contentType: LONG_MESSAGE,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'File',
|
||||
|
@ -571,7 +599,8 @@ describe('Message', () => {
|
|||
{
|
||||
title: 'unspecified-type',
|
||||
attachment: {
|
||||
contentType: null,
|
||||
contentType: APPLICATION_JSON,
|
||||
size: 0,
|
||||
},
|
||||
expectedResult: {
|
||||
text: 'File',
|
||||
|
@ -600,7 +629,8 @@ describe('Message', () => {
|
|||
attachments: [
|
||||
attachment,
|
||||
{
|
||||
contentType: 'text/html',
|
||||
contentType: TEXT_ATTACHMENT,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
@ -671,7 +701,8 @@ describe('Message', () => {
|
|||
source,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'image/png',
|
||||
contentType: IMAGE_PNG,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}).getNotificationText(),
|
||||
|
@ -699,7 +730,8 @@ describe('Message', () => {
|
|||
source,
|
||||
attachments: [
|
||||
{
|
||||
contentType: 'image/png',
|
||||
contentType: IMAGE_PNG,
|
||||
size: 0,
|
||||
},
|
||||
],
|
||||
}).getNotificationText(),
|
||||
|
@ -708,26 +740,3 @@ describe('Message', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MessageCollection', () => {
|
||||
it('should be ordered oldest to newest', () => {
|
||||
const messages = new window.Whisper.MessageCollection();
|
||||
// Timestamps
|
||||
const today = Date.now();
|
||||
const tomorrow = today + 12345;
|
||||
|
||||
// Add threads
|
||||
messages.add({ received_at: today });
|
||||
messages.add({ received_at: tomorrow });
|
||||
|
||||
const { models } = messages;
|
||||
const firstTimestamp = models[0].get('received_at');
|
||||
const secondTimestamp = models[1].get('received_at');
|
||||
|
||||
// Compare timestamps
|
||||
assert(typeof firstTimestamp === 'number');
|
||||
assert(typeof secondTimestamp === 'number');
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
assert(firstTimestamp! < secondTimestamp!);
|
||||
});
|
||||
});
|
||||
|
|
382
ts/test-electron/services/MessageCache_test.ts
Normal file
382
ts/test-electron/services/MessageCache_test.ts
Normal file
|
@ -0,0 +1,382 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import type { MessageAttributesType } from '../../model-types.d';
|
||||
import { MessageModel } from '../../models/messages';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
|
||||
import { MessageCache } from '../../services/MessageCache';
|
||||
|
||||
describe('MessageCache', () => {
|
||||
describe('filterBySentAt', () => {
|
||||
it('returns an empty iterable if no messages match', () => {
|
||||
const mc = new MessageCache();
|
||||
|
||||
assert.isEmpty([...mc.__DEPRECATED$filterBySentAt(123)]);
|
||||
});
|
||||
|
||||
it('returns all messages that match the timestamp', () => {
|
||||
const mc = new MessageCache();
|
||||
|
||||
let message1 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
body: 'message1',
|
||||
id: uuid(),
|
||||
received_at: 1,
|
||||
sent_at: 1234,
|
||||
timestamp: 9999,
|
||||
type: 'incoming',
|
||||
});
|
||||
let message2 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
body: 'message2',
|
||||
id: uuid(),
|
||||
received_at: 2,
|
||||
sent_at: 1234,
|
||||
timestamp: 9999,
|
||||
type: 'outgoing',
|
||||
});
|
||||
const message3 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
body: 'message3',
|
||||
id: uuid(),
|
||||
received_at: 3,
|
||||
sent_at: 5678,
|
||||
timestamp: 9999,
|
||||
type: 'outgoing',
|
||||
});
|
||||
|
||||
message1 = mc.__DEPRECATED$register(message1.id, message1, 'test');
|
||||
message2 = mc.__DEPRECATED$register(message2.id, message2, 'test');
|
||||
// We deliberately register this message twice for testing.
|
||||
message2 = mc.__DEPRECATED$register(message2.id, message2, 'test');
|
||||
mc.__DEPRECATED$register(message3.id, message3, 'test');
|
||||
|
||||
const filteredMessages = Array.from(
|
||||
mc.__DEPRECATED$filterBySentAt(1234)
|
||||
).map(x => x.attributes);
|
||||
|
||||
assert.deepEqual(
|
||||
filteredMessages,
|
||||
[message1.attributes, message2.attributes],
|
||||
'first'
|
||||
);
|
||||
|
||||
mc.__DEPRECATED$unregister(message2.id);
|
||||
|
||||
const filteredMessages2 = Array.from(
|
||||
mc.__DEPRECATED$filterBySentAt(1234)
|
||||
).map(x => x.attributes);
|
||||
|
||||
assert.deepEqual(filteredMessages2, [message1.attributes], 'second');
|
||||
});
|
||||
});
|
||||
|
||||
describe('__DEPRECATED$register: syncing with backbone', () => {
|
||||
it('backbone to redux', () => {
|
||||
const message1 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
id: uuid(),
|
||||
body: 'test1',
|
||||
received_at: 1,
|
||||
sent_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
type: 'outgoing',
|
||||
});
|
||||
const messageFromController = window.MessageCache.__DEPRECATED$register(
|
||||
message1.id,
|
||||
message1,
|
||||
'test'
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
message1,
|
||||
messageFromController,
|
||||
'same objects from mc.__DEPRECATED$register'
|
||||
);
|
||||
|
||||
const messageById = window.MessageCache.__DEPRECATED$getById(message1.id);
|
||||
|
||||
assert.strictEqual(message1, messageById, 'same objects from mc.getById');
|
||||
|
||||
const messageInCache = window.MessageCache.accessAttributes(message1.id);
|
||||
strictAssert(messageInCache, 'no message found');
|
||||
assert.deepEqual(
|
||||
message1.attributes,
|
||||
messageInCache,
|
||||
'same attributes as in cache'
|
||||
);
|
||||
|
||||
message1.set({ body: 'test2' });
|
||||
assert.equal(message1.attributes.body, 'test2', 'message model updated');
|
||||
assert.equal(
|
||||
messageById?.attributes.body,
|
||||
'test2',
|
||||
'old reference from messageById was updated'
|
||||
);
|
||||
assert.equal(
|
||||
messageInCache.body,
|
||||
'test1',
|
||||
'old cache reference not updated'
|
||||
);
|
||||
|
||||
const newMessageById = window.MessageCache.__DEPRECATED$getById(
|
||||
message1.id
|
||||
);
|
||||
assert.deepEqual(
|
||||
message1.attributes,
|
||||
newMessageById?.attributes,
|
||||
'same attributes from mc.getById (2)'
|
||||
);
|
||||
|
||||
const newMessageInCache = window.MessageCache.accessAttributes(
|
||||
message1.id
|
||||
);
|
||||
strictAssert(newMessageInCache, 'no message found');
|
||||
assert.deepEqual(
|
||||
message1.attributes,
|
||||
newMessageInCache,
|
||||
'same attributes as in cache (2)'
|
||||
);
|
||||
});
|
||||
|
||||
it('redux to backbone (working with models)', () => {
|
||||
const message = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
id: uuid(),
|
||||
body: 'test1',
|
||||
received_at: 1,
|
||||
sent_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
type: 'outgoing',
|
||||
});
|
||||
|
||||
window.MessageCache.toMessageAttributes(message.attributes);
|
||||
|
||||
const messageFromController = window.MessageCache.__DEPRECATED$register(
|
||||
message.id,
|
||||
message,
|
||||
'test'
|
||||
);
|
||||
|
||||
assert.notStrictEqual(
|
||||
message,
|
||||
messageFromController,
|
||||
'mc.__DEPRECATED$register returns existing but it is not the same reference'
|
||||
);
|
||||
assert.deepEqual(
|
||||
message.attributes,
|
||||
messageFromController.attributes,
|
||||
'mc.__DEPRECATED$register returns existing and is the same attributes'
|
||||
);
|
||||
|
||||
messageFromController.set({ body: 'test2' });
|
||||
|
||||
assert.notEqual(
|
||||
message.get('body'),
|
||||
messageFromController.get('body'),
|
||||
'new model is not equal to old model'
|
||||
);
|
||||
|
||||
const messageInCache = window.MessageCache.accessAttributes(message.id);
|
||||
strictAssert(messageInCache, 'no message found');
|
||||
assert.equal(
|
||||
messageFromController.get('body'),
|
||||
messageInCache.body,
|
||||
'new update is in cache'
|
||||
);
|
||||
|
||||
assert.isUndefined(
|
||||
messageFromController.get('storyReplyContext'),
|
||||
'storyReplyContext is undefined'
|
||||
);
|
||||
|
||||
window.MessageCache.setAttributes({
|
||||
messageId: message.id,
|
||||
messageAttributes: {
|
||||
storyReplyContext: {
|
||||
attachment: undefined,
|
||||
authorAci: undefined,
|
||||
messageId: 'test123',
|
||||
},
|
||||
},
|
||||
skipSaveToDatabase: true,
|
||||
});
|
||||
|
||||
// This works because we refresh the model whenever an attribute changes
|
||||
// but this should log a warning.
|
||||
assert.equal(
|
||||
messageFromController.get('storyReplyContext')?.messageId,
|
||||
'test123',
|
||||
'storyReplyContext was updated (stale model)'
|
||||
);
|
||||
|
||||
const newMessageFromController =
|
||||
window.MessageCache.__DEPRECATED$register(message.id, message, 'test');
|
||||
|
||||
assert.equal(
|
||||
newMessageFromController.get('storyReplyContext')?.messageId,
|
||||
'test123',
|
||||
'storyReplyContext was updated (not stale)'
|
||||
);
|
||||
});
|
||||
|
||||
it('redux to backbone (working with attributes)', () => {
|
||||
it('sets the attributes and returns a fresh copy', () => {
|
||||
const mc = new MessageCache();
|
||||
|
||||
const messageAttributes: MessageAttributesType = {
|
||||
conversationId: uuid(),
|
||||
id: uuid(),
|
||||
received_at: 1,
|
||||
sent_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
type: 'incoming',
|
||||
};
|
||||
|
||||
const messageModel = mc.__DEPRECATED$register(
|
||||
messageAttributes.id,
|
||||
messageAttributes,
|
||||
'test/updateAttributes'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
messageAttributes,
|
||||
messageModel.attributes,
|
||||
'initial attributes matches message model'
|
||||
);
|
||||
|
||||
const proposedStoryReplyContext = {
|
||||
attachment: undefined,
|
||||
authorAci: undefined,
|
||||
messageId: 'test123',
|
||||
};
|
||||
|
||||
assert.notDeepEqual(
|
||||
messageModel.attributes.storyReplyContext,
|
||||
proposedStoryReplyContext,
|
||||
'attributes were changed outside of the message model'
|
||||
);
|
||||
|
||||
mc.setAttributes({
|
||||
messageId: messageAttributes.id,
|
||||
messageAttributes: {
|
||||
storyReplyContext: proposedStoryReplyContext,
|
||||
},
|
||||
skipSaveToDatabase: true,
|
||||
});
|
||||
|
||||
const nextMessageAttributes = mc.accessAttributesOrThrow(
|
||||
'test',
|
||||
messageAttributes.id
|
||||
);
|
||||
|
||||
assert.notDeepEqual(
|
||||
messageAttributes,
|
||||
nextMessageAttributes,
|
||||
'initial attributes are stale'
|
||||
);
|
||||
assert.notDeepEqual(
|
||||
messageAttributes.storyReplyContext,
|
||||
proposedStoryReplyContext,
|
||||
'initial attributes are stale 2'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
nextMessageAttributes.storyReplyContext,
|
||||
proposedStoryReplyContext,
|
||||
'fresh attributes match what was proposed'
|
||||
);
|
||||
assert.notStrictEqual(
|
||||
nextMessageAttributes.storyReplyContext,
|
||||
proposedStoryReplyContext,
|
||||
'fresh attributes are not the same reference as proposed attributes'
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
messageModel.attributes,
|
||||
nextMessageAttributes,
|
||||
'model was updated'
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
messageModel.get('storyReplyContext')?.messageId,
|
||||
'test123',
|
||||
'storyReplyContext in model is set correctly'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('accessAttributes', () => {
|
||||
it('gets the attributes if they exist', () => {
|
||||
const mc = new MessageCache();
|
||||
|
||||
const messageAttributes: MessageAttributesType = {
|
||||
conversationId: uuid(),
|
||||
id: uuid(),
|
||||
received_at: 1,
|
||||
sent_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
type: 'incoming',
|
||||
};
|
||||
|
||||
mc.toMessageAttributes(messageAttributes);
|
||||
|
||||
const accessAttributes = mc.accessAttributes(messageAttributes.id);
|
||||
|
||||
assert.deepEqual(
|
||||
accessAttributes,
|
||||
messageAttributes,
|
||||
'attributes returned have the same values'
|
||||
);
|
||||
assert.notStrictEqual(
|
||||
accessAttributes,
|
||||
messageAttributes,
|
||||
'attributes returned are not the same references'
|
||||
);
|
||||
|
||||
const undefinedMessage = mc.accessAttributes(uuid());
|
||||
assert.isUndefined(undefinedMessage, 'access did not find message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('accessAttributesOrThrow', () => {
|
||||
it('accesses the attributes or throws if they do not exist', () => {
|
||||
const mc = new MessageCache();
|
||||
|
||||
const messageAttributes: MessageAttributesType = {
|
||||
conversationId: uuid(),
|
||||
id: uuid(),
|
||||
received_at: 1,
|
||||
sent_at: Date.now(),
|
||||
timestamp: Date.now(),
|
||||
type: 'incoming',
|
||||
};
|
||||
|
||||
mc.toMessageAttributes(messageAttributes);
|
||||
|
||||
const accessAttributes = mc.accessAttributesOrThrow(
|
||||
'tests.1',
|
||||
messageAttributes.id
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
accessAttributes,
|
||||
messageAttributes,
|
||||
'attributes returned have the same values'
|
||||
);
|
||||
assert.notStrictEqual(
|
||||
accessAttributes,
|
||||
messageAttributes,
|
||||
'attributes returned are not the same references'
|
||||
);
|
||||
|
||||
assert.throws(() => {
|
||||
mc.accessAttributesOrThrow('tests.2', uuid());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -861,7 +861,11 @@ describe('both/state/ducks/stories', () => {
|
|||
const storyId = generateUuid();
|
||||
const messageAttributes = getStoryMessage(storyId);
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
window.MessageCache.__DEPRECATED$register(
|
||||
storyId,
|
||||
messageAttributes,
|
||||
'test'
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
await queueStoryDownload(storyId)(dispatch, getEmptyRootState, null);
|
||||
|
@ -883,7 +887,11 @@ describe('both/state/ducks/stories', () => {
|
|||
],
|
||||
};
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
window.MessageCache.__DEPRECATED$register(
|
||||
storyId,
|
||||
messageAttributes,
|
||||
'test'
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
await queueStoryDownload(storyId)(dispatch, getEmptyRootState, null);
|
||||
|
@ -905,7 +913,11 @@ describe('both/state/ducks/stories', () => {
|
|||
],
|
||||
};
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
window.MessageCache.__DEPRECATED$register(
|
||||
storyId,
|
||||
messageAttributes,
|
||||
'test'
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
await queueStoryDownload(storyId)(dispatch, getEmptyRootState, null);
|
||||
|
@ -947,7 +959,11 @@ describe('both/state/ducks/stories', () => {
|
|||
},
|
||||
});
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
window.MessageCache.__DEPRECATED$register(
|
||||
storyId,
|
||||
messageAttributes,
|
||||
'test'
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
await queueStoryDownload(storyId)(dispatch, getState, null);
|
||||
|
@ -1004,7 +1020,11 @@ describe('both/state/ducks/stories', () => {
|
|||
},
|
||||
});
|
||||
|
||||
window.MessageController.register(storyId, messageAttributes);
|
||||
window.MessageCache.__DEPRECATED$register(
|
||||
storyId,
|
||||
messageAttributes,
|
||||
'test'
|
||||
);
|
||||
|
||||
const dispatch = sinon.spy();
|
||||
await queueStoryDownload(storyId)(dispatch, getState, null);
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { MessageModel } from '../../models/messages';
|
||||
|
||||
import { MessageController } from '../../util/MessageController';
|
||||
|
||||
describe('MessageController', () => {
|
||||
describe('filterBySentAt', () => {
|
||||
it('returns an empty iterable if no messages match', () => {
|
||||
const mc = new MessageController();
|
||||
|
||||
assert.isEmpty([...mc.filterBySentAt(123)]);
|
||||
});
|
||||
|
||||
it('returns all messages that match the timestamp', () => {
|
||||
const mc = new MessageController();
|
||||
const message1 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
id: 'abc',
|
||||
received_at: 1,
|
||||
sent_at: 1234,
|
||||
timestamp: 9999,
|
||||
type: 'incoming',
|
||||
});
|
||||
const message2 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
id: 'def',
|
||||
received_at: 2,
|
||||
sent_at: 1234,
|
||||
timestamp: 9999,
|
||||
type: 'outgoing',
|
||||
});
|
||||
const message3 = new MessageModel({
|
||||
conversationId: 'xyz',
|
||||
id: 'ignored',
|
||||
received_at: 3,
|
||||
sent_at: 5678,
|
||||
timestamp: 9999,
|
||||
type: 'outgoing',
|
||||
});
|
||||
mc.register(message1.id, message1);
|
||||
mc.register(message2.id, message2);
|
||||
// We deliberately register this message twice for testing.
|
||||
mc.register(message2.id, message2);
|
||||
mc.register(message3.id, message3);
|
||||
|
||||
assert.sameMembers([...mc.filterBySentAt(1234)], [message1, message2]);
|
||||
|
||||
mc.unregister(message2.id);
|
||||
|
||||
assert.sameMembers([...mc.filterBySentAt(1234)], [message1]);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue