2022-11-11 04:10:30 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
|
|
|
import { assert } from 'chai';
|
|
|
|
|
|
|
|
import type { StateType } from '../../../state/reducer';
|
|
|
|
import type { ConversationType } from '../../../state/ducks/conversations';
|
|
|
|
import type { StoryDistributionListDataType } from '../../../state/ducks/storyDistributionLists';
|
2023-08-10 16:43:33 +00:00
|
|
|
import type { StoryDistributionIdString } from '../../../types/StoryDistributionId';
|
|
|
|
import type { ServiceIdString } from '../../../types/ServiceId';
|
2022-11-11 04:10:30 +00:00
|
|
|
import type { ContactsByStory } from '../../../components/SafetyNumberChangeDialog';
|
|
|
|
|
|
|
|
import * as Bytes from '../../../Bytes';
|
|
|
|
import { reducer as rootReducer } from '../../../state/reducer';
|
|
|
|
import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation';
|
|
|
|
import { getEmptyState } from '../../../state/ducks/conversations';
|
|
|
|
import { getByDistributionListConversationsStoppingSend } from '../../../state/selectors/conversations-extra';
|
2023-08-10 16:43:33 +00:00
|
|
|
import { generateAci } from '../../../types/ServiceId';
|
|
|
|
import { generateStoryDistributionId } from '../../../types/StoryDistributionId';
|
2022-11-11 04:10:30 +00:00
|
|
|
import { noopAction } from '../../../state/ducks/noop';
|
|
|
|
import { ID_LENGTH } from '../../../groups';
|
|
|
|
import { ConversationVerificationState } from '../../../state/ducks/conversationsEnums';
|
|
|
|
|
|
|
|
describe('both/state/selectors/conversations-extra', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const SERVICE_ID_1 = generateAci();
|
|
|
|
const SERVICE_ID_2 = generateAci();
|
|
|
|
const LIST_1 = generateStoryDistributionId();
|
|
|
|
const LIST_2 = generateStoryDistributionId();
|
2022-11-11 04:10:30 +00:00
|
|
|
const GROUP_ID = Bytes.toBase64(new Uint8Array(ID_LENGTH));
|
|
|
|
|
|
|
|
const getEmptyRootState = (): StateType => {
|
|
|
|
return rootReducer(undefined, noopAction());
|
|
|
|
};
|
|
|
|
|
|
|
|
function makeConversation(
|
|
|
|
id: string,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceId?: ServiceIdString
|
2022-11-11 04:10:30 +00:00
|
|
|
): ConversationType {
|
|
|
|
const title = `${id} title`;
|
|
|
|
return getDefaultConversation({
|
|
|
|
id,
|
2023-08-10 16:43:33 +00:00
|
|
|
uuid: serviceId,
|
2022-11-11 04:10:30 +00:00
|
|
|
searchableTitle: title,
|
|
|
|
title,
|
|
|
|
titleNoDefault: title,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeDistributionList(
|
|
|
|
name: string,
|
2023-08-10 16:43:33 +00:00
|
|
|
id: StoryDistributionIdString
|
2022-11-11 04:10:30 +00:00
|
|
|
): StoryDistributionListDataType {
|
|
|
|
return {
|
|
|
|
id,
|
|
|
|
name: `distribution ${name}`,
|
|
|
|
allowsReplies: true,
|
|
|
|
isBlockList: false,
|
2023-08-10 16:43:33 +00:00
|
|
|
memberServiceIds: [],
|
2022-11-11 04:10:30 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
describe('#getByDistributionListConversationsStoppingSend', () => {
|
2023-08-10 16:43:33 +00:00
|
|
|
const direct1 = makeConversation('direct1', SERVICE_ID_1);
|
|
|
|
const direct2 = makeConversation('direct2', SERVICE_ID_2);
|
2022-11-11 04:10:30 +00:00
|
|
|
const group1 = {
|
|
|
|
...makeConversation('group1'),
|
|
|
|
groupVersion: 2 as const,
|
|
|
|
groupId: GROUP_ID,
|
|
|
|
};
|
|
|
|
const state: StateType = {
|
|
|
|
...getEmptyRootState(),
|
|
|
|
conversations: {
|
|
|
|
...getEmptyState(),
|
|
|
|
conversationLookup: {
|
|
|
|
direct1,
|
|
|
|
direct2,
|
|
|
|
group1,
|
|
|
|
},
|
|
|
|
conversationsByUuid: {
|
2023-08-10 16:43:33 +00:00
|
|
|
[SERVICE_ID_1]: direct1,
|
|
|
|
[SERVICE_ID_2]: direct2,
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
verificationDataByConversation: {},
|
|
|
|
},
|
|
|
|
storyDistributionLists: {
|
|
|
|
distributionLists: [
|
|
|
|
makeDistributionList('list1', LIST_1),
|
|
|
|
makeDistributionList('list2', LIST_2),
|
|
|
|
],
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
it('returns empty array for no untrusted recipients', () => {
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(state);
|
|
|
|
assert.isEmpty(actual);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns empty story field for 1:1 conversations', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
direct1: {
|
|
|
|
type: ConversationVerificationState.PendingVerification,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_1, SERVICE_ID_2],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [
|
|
|
|
{
|
|
|
|
story: undefined,
|
|
|
|
contacts: [direct1, direct2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns groups with name set', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
group1: {
|
|
|
|
type: ConversationVerificationState.PendingVerification,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_1, SERVICE_ID_2],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [
|
|
|
|
{
|
|
|
|
story: {
|
|
|
|
name: 'group1 title',
|
|
|
|
conversationId: 'group1',
|
|
|
|
},
|
|
|
|
contacts: [direct1, direct2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns distribution lists with distributionId set', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
direct1: {
|
|
|
|
type: ConversationVerificationState.PendingVerification,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_1],
|
2022-11-11 04:10:30 +00:00
|
|
|
byDistributionId: {
|
|
|
|
[LIST_1]: {
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_2],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [
|
|
|
|
{
|
|
|
|
story: undefined,
|
|
|
|
contacts: [direct1],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
story: {
|
|
|
|
name: 'distribution list1',
|
|
|
|
conversationId: 'direct1',
|
|
|
|
distributionId: LIST_1,
|
|
|
|
},
|
|
|
|
contacts: [direct2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns distribution lists even if parent is empty', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
direct1: {
|
|
|
|
type: ConversationVerificationState.PendingVerification,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [],
|
2022-11-11 04:10:30 +00:00
|
|
|
byDistributionId: {
|
|
|
|
[LIST_1]: {
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_1],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
[LIST_2]: {
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_2],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [
|
|
|
|
{
|
|
|
|
story: {
|
|
|
|
name: 'distribution list1',
|
|
|
|
conversationId: 'direct1',
|
|
|
|
distributionId: LIST_1,
|
|
|
|
},
|
|
|
|
contacts: [direct1],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
story: {
|
|
|
|
name: 'distribution list2',
|
|
|
|
conversationId: 'direct1',
|
|
|
|
distributionId: LIST_2,
|
|
|
|
},
|
|
|
|
contacts: [direct2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('drops items that are not pending verification', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
direct1: {
|
|
|
|
type: ConversationVerificationState.VerificationCancelled,
|
|
|
|
canceledAt: Date.now(),
|
|
|
|
},
|
|
|
|
direct2: {
|
|
|
|
type: ConversationVerificationState.VerificationCancelled,
|
|
|
|
canceledAt: Date.now(),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('puts UUIDs from unknown distribution lists into their parent', () => {
|
|
|
|
const starting: StateType = {
|
|
|
|
...state,
|
|
|
|
conversations: {
|
|
|
|
...state.conversations,
|
|
|
|
verificationDataByConversation: {
|
|
|
|
direct1: {
|
|
|
|
type: ConversationVerificationState.PendingVerification,
|
2023-08-10 16:43:33 +00:00
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_1],
|
2022-11-11 04:10:30 +00:00
|
|
|
byDistributionId: {
|
|
|
|
// Not a list id!
|
2023-08-10 16:43:33 +00:00
|
|
|
[SERVICE_ID_1]: {
|
|
|
|
serviceIdsNeedingVerification: [SERVICE_ID_2],
|
2022-11-11 04:10:30 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
const expected: ContactsByStory = [
|
|
|
|
{
|
|
|
|
story: undefined,
|
|
|
|
contacts: [direct1, direct2],
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
const actual = getByDistributionListConversationsStoppingSend(starting);
|
|
|
|
assert.sameDeepMembers(actual, expected);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|