// Copyright 2021 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only import { assert } from 'chai'; import { v4 as uuid } from 'uuid'; import { getDefaultConversation } from '../helpers/getDefaultConversation'; import type { ConversationType } from '../../state/ducks/conversations'; import { SendStatus } from '../../messages/MessageSendState'; import { migrateLegacySendAttributes } from '../../messages/migrateLegacySendAttributes'; describe('migrateLegacySendAttributes', () => { const defaultMessage = { type: 'outgoing' as const, sent_at: 123, sent: true, }; const createGetConversation = ( ...conversations: ReadonlyArray ) => { const lookup = new Map(); conversations.forEach(conversation => { [conversation.id, conversation.uuid, conversation.e164].forEach( property => { if (property) { lookup.set(property, conversation); } } ); }); return (id?: string | null) => (id ? lookup.get(id) : undefined); }; it("doesn't migrate messages that already have the modern send state", () => { const ourConversationId = uuid(); const message = { ...defaultMessage, sendStateByConversationId: { [ourConversationId]: { status: SendStatus.Sent, updatedAt: 123, }, }, }; const getConversation = () => undefined; assert.isUndefined( migrateLegacySendAttributes(message, getConversation, ourConversationId) ); }); it("doesn't migrate messages that aren't outgoing", () => { const ourConversationId = uuid(); const message = { ...defaultMessage, type: 'incoming' as const, }; const getConversation = () => undefined; assert.isUndefined( migrateLegacySendAttributes(message, getConversation, ourConversationId) ); }); it('advances the send state machine, starting from "pending", for different state types', () => { let e164Counter = 0; const getTestConversation = () => { const last4Digits = e164Counter.toString().padStart(4); assert.strictEqual( last4Digits.length, 4, 'Test setup failure: E164 is too long' ); e164Counter += 1; return getDefaultConversation({ e164: `+1999555${last4Digits}` }); }; // This is aliased for clarity. const ignoredUuid = uuid; const failedConversationByUuid = getTestConversation(); const failedConversationByE164 = getTestConversation(); const pendingConversation = getTestConversation(); const sentConversation = getTestConversation(); const deliveredConversation = getTestConversation(); const readConversation = getTestConversation(); const conversationNotInRecipientsList = getTestConversation(); const ourConversation = getTestConversation(); const message = { ...defaultMessage, recipients: [ failedConversationByUuid.uuid, failedConversationByE164.uuid, pendingConversation.uuid, sentConversation.uuid, deliveredConversation.uuid, readConversation.uuid, ignoredUuid(), ourConversation.uuid, ], errors: [ Object.assign(new Error('looked up by UUID'), { identifier: failedConversationByUuid.uuid, }), Object.assign(new Error('looked up by E164'), { number: failedConversationByE164.e164, }), Object.assign(new Error('ignored error'), { identifier: ignoredUuid(), }), new Error('a different error'), ], sent_to: [ sentConversation.e164, conversationNotInRecipientsList.uuid, ignoredUuid(), ourConversation.uuid, ], delivered_to: [ deliveredConversation.uuid, ignoredUuid(), ourConversation.uuid, ], read_by: [readConversation.uuid, ignoredUuid()], }; const getConversation = createGetConversation( failedConversationByUuid, failedConversationByE164, pendingConversation, sentConversation, deliveredConversation, readConversation, conversationNotInRecipientsList, ourConversation ); assert.deepEqual( migrateLegacySendAttributes(message, getConversation, ourConversation.id), { [ourConversation.id]: { status: SendStatus.Delivered, updatedAt: undefined, }, [failedConversationByUuid.id]: { status: SendStatus.Failed, updatedAt: undefined, }, [failedConversationByE164.id]: { status: SendStatus.Failed, updatedAt: undefined, }, [pendingConversation.id]: { status: SendStatus.Pending, updatedAt: message.sent_at, }, [sentConversation.id]: { status: SendStatus.Sent, updatedAt: undefined, }, [conversationNotInRecipientsList.id]: { status: SendStatus.Sent, updatedAt: undefined, }, [deliveredConversation.id]: { status: SendStatus.Delivered, updatedAt: undefined, }, [readConversation.id]: { status: SendStatus.Read, updatedAt: undefined, }, } ); }); it('considers our own conversation sent if the "sent" attribute is set', () => { const ourConversation = getDefaultConversation(); const conversation1 = getDefaultConversation(); const conversation2 = getDefaultConversation(); const message = { ...defaultMessage, recipients: [conversation1.id, conversation2.id], sent: true, }; const getConversation = createGetConversation( ourConversation, conversation1, conversation2 ); assert.deepEqual( migrateLegacySendAttributes( message, getConversation, ourConversation.id )?.[ourConversation.id], { status: SendStatus.Sent, updatedAt: undefined, } ); }); it("considers our own conversation failed if the message isn't marked sent and we aren't elsewhere in the recipients list", () => { const ourConversation = getDefaultConversation(); const conversation1 = getDefaultConversation(); const conversation2 = getDefaultConversation(); const message = { ...defaultMessage, recipients: [conversation1.id, conversation2.id], sent: false, }; const getConversation = createGetConversation( ourConversation, conversation1, conversation2 ); assert.deepEqual( migrateLegacySendAttributes( message, getConversation, ourConversation.id )?.[ourConversation.id], { status: SendStatus.Failed, updatedAt: undefined, } ); }); it('migrates a typical legacy note to self message', () => { const ourConversation = getDefaultConversation(); const message = { ...defaultMessage, conversationId: ourConversation.id, recipients: [], destination: ourConversation.uuid, sent_to: [ourConversation.uuid], sent: true, synced: true, unidentifiedDeliveries: [], delivered_to: [ourConversation.id], read_by: [ourConversation.id], }; const getConversation = createGetConversation(ourConversation); assert.deepEqual( migrateLegacySendAttributes(message, getConversation, ourConversation.id), { [ourConversation.id]: { status: SendStatus.Read, updatedAt: undefined, }, } ); }); });