Introduce Service Id Types

Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
Fedor Indutny 2023-08-10 18:43:33 +02:00 committed by Jamie Kyle
parent 414c0a58d3
commit 366b875fd2
269 changed files with 5832 additions and 5550 deletions

View file

@ -26,7 +26,7 @@ describe('ContactsParser', () => {
const contactInfoBuffer = Proto.ContactDetails.encode({
name: 'Zero Cool',
number: '+10000000000',
uuid: '7198E1BD-1293-452A-A098-F982FF201902',
aci: '7198E1BD-1293-452A-A098-F982FF201902',
avatar: { contentType: 'image/jpeg', length: avatarBuffer.length },
}).finish();
@ -52,10 +52,7 @@ describe('ContactsParser', () => {
count += 1;
assert.strictEqual(contact.name, 'Zero Cool');
assert.strictEqual(contact.number, '+10000000000');
assert.strictEqual(
contact.uuid,
'7198e1bd-1293-452a-a098-f982ff201902'
);
assert.strictEqual(contact.aci, '7198e1bd-1293-452a-a098-f982ff201902');
assert.strictEqual(contact.avatar?.contentType, 'image/jpeg');
assert.strictEqual(contact.avatar?.length, 255);
assert.strictEqual(contact.avatar?.data.byteLength, 255);

View file

@ -3,6 +3,7 @@
import { assert } from 'chai';
import { normalizeAci } from '../types/ServiceId';
import {
getCountryCodeValue,
getBucketValue,
@ -10,7 +11,7 @@ import {
} from '../RemoteConfig';
describe('RemoteConfig', () => {
const uuid = '15b9729c-51ea-4ddb-b516-652befe78062';
const uuid = normalizeAci('15b9729c-51ea-4ddb-b516-652befe78062', 'test');
describe('#innerIsBucketValueEnabled', () => {
// Note: bucketValue is 497941 for 'desktop.stories2' key

View file

@ -4,7 +4,7 @@
import { assert } from 'chai';
import { times } from 'lodash';
import { updateRemoteConfig } from '../helpers/RemoteConfigStub';
import { UUID } from '../../types/UUID';
import { generateAci } from '../../types/ServiceId';
import { isConversationTooBigToRing } from '../../conversations/isConversationTooBigToRing';
@ -12,7 +12,7 @@ const CONFIG_KEY = 'global.calling.maxGroupCallRingSize';
describe('isConversationTooBigToRing', () => {
const fakeMemberships = (count: number) =>
times(count, () => ({ uuid: UUID.generate().toString(), isAdmin: false }));
times(count, () => ({ uuid: generateAci(), isAdmin: false }));
it('returns false if there are no memberships (i.e., for a direct conversation)', () => {
assert.isFalse(isConversationTooBigToRing({}));

View file

@ -3,7 +3,7 @@
import { assert } from 'chai';
import { UUID } from '../../types/UUID';
import { generateAci } from '../../types/ServiceId';
import { _maybeBuildAddBannedMemberActions } from '../../groups';
import { getClientZkGroupCipher, decryptUuid } from '../../util/zkgroup';
import { updateRemoteConfig } from '../helpers/RemoteConfigStub';
@ -11,10 +11,10 @@ import { updateRemoteConfig } from '../helpers/RemoteConfigStub';
const HARD_LIMIT_KEY = 'global.groupsv2.groupSizeHardLimit';
describe('group add banned member', () => {
const uuid = UUID.generate();
const ourUuid = UUID.generate();
const serviceId = generateAci();
const ourAci = generateAci();
const existing = Array.from({ length: 10 }, (_, index) => ({
uuid: UUID.generate().toString(),
uuid: generateAci(),
timestamp: index,
}));
const secretParams =
@ -36,8 +36,8 @@ describe('group add banned member', () => {
it('should add banned member without deleting', () => {
const actions = _maybeBuildAddBannedMemberActions({
clientZkGroupCipher,
uuid,
ourUuid,
serviceId,
ourAci,
group: {
bannedMembersV2: [],
},
@ -49,7 +49,7 @@ describe('group add banned member', () => {
clientZkGroupCipher,
actions.addMembersBanned?.[0]?.added?.userId ?? new Uint8Array(0)
),
uuid.toString()
serviceId
);
assert.strictEqual(actions.deleteMembersBanned, null);
});
@ -57,8 +57,8 @@ describe('group add banned member', () => {
it('should add banned member while deleting the oldest', () => {
const actions = _maybeBuildAddBannedMemberActions({
clientZkGroupCipher,
uuid,
ourUuid,
serviceId,
ourAci,
group: {
bannedMembersV2: [...existing],
},
@ -77,7 +77,7 @@ describe('group add banned member', () => {
clientZkGroupCipher,
actions.addMembersBanned?.[0]?.added?.userId ?? new Uint8Array(0)
),
uuid.toString()
serviceId
);
assert.deepStrictEqual(
deleted,
@ -91,8 +91,8 @@ describe('group add banned member', () => {
it('should not ban ourselves', () => {
const actions = _maybeBuildAddBannedMemberActions({
clientZkGroupCipher,
uuid: ourUuid,
ourUuid,
serviceId: ourAci,
ourAci,
group: {
bannedMembersV2: [],
},
@ -105,10 +105,10 @@ describe('group add banned member', () => {
it('should not ban already banned person', () => {
const actions = _maybeBuildAddBannedMemberActions({
clientZkGroupCipher,
uuid,
ourUuid,
serviceId,
ourAci,
group: {
bannedMembersV2: [{ uuid: uuid.toString(), timestamp: 1 }],
bannedMembersV2: [{ uuid: serviceId, timestamp: 1 }],
},
});

View file

@ -2,8 +2,9 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { v4 as generateUuid } from 'uuid';
import { UUID } from '../../types/UUID';
import { generateAci } from '../../types/ServiceId';
import {
_isGroupChangeMessageBounceable,
_mergeGroupChangeMessages,
@ -11,13 +12,13 @@ import {
describe('group message merging', () => {
const defaultMessage = {
id: UUID.generate().toString(),
conversationId: UUID.generate().toString(),
id: generateUuid(),
conversationId: generateUuid(),
timestamp: Date.now(),
sent_at: Date.now(),
received_at: Date.now(),
};
const uuid = UUID.generate().toString();
const uuid = generateAci();
describe('_isGroupChangeMessageBounceable', () => {
it('should return true for admin approval add', () => {
@ -106,7 +107,7 @@ describe('group message merging', () => {
details: [
{
type: 'admin-approval-add-one' as const,
uuid: UUID.generate().toString(),
uuid: generateAci(),
},
],
},
@ -118,7 +119,7 @@ describe('group message merging', () => {
details: [
{
type: 'admin-approval-remove-one' as const,
uuid: UUID.generate().toString(),
uuid: generateAci(),
},
],
},

View file

@ -7,7 +7,7 @@ import type {
UUIDFetchStateType,
UUIDFetchStateKeyType,
} from '../../util/uuidFetchState';
import type { lookupConversationWithoutUuid } from '../../util/lookupConversationWithoutUuid';
import type { lookupConversationWithoutServiceId } from '../../util/lookupConversationWithoutServiceId';
import { sleep } from '../../util/sleep';
import * as durations from '../../util/durations';
import type { ConversationType } from '../../state/ducks/conversations';
@ -18,9 +18,9 @@ const VALID_IDENTIFIERS = new Set<UUIDFetchStateKeyType>([
'username:bobross',
]);
export function makeFakeLookupConversationWithoutUuid(
export function makeFakeLookupConversationWithoutServiceId(
saveConversation?: (convo: ConversationType) => void
): typeof lookupConversationWithoutUuid {
): typeof lookupConversationWithoutServiceId {
const cache = new Map<UUIDFetchStateKeyType, ConversationType>();
return async options => {

View file

@ -3,9 +3,11 @@
import casual from 'casual';
import { sample } from 'lodash';
import { v4 as generateUuid } from 'uuid';
import type { ConversationType } from '../../state/ducks/conversations';
import type { UUIDStringType } from '../../types/UUID';
import { UUID } from '../../types/UUID';
import type { ServiceIdString } from '../../types/ServiceId';
import { generateAci } from '../../types/ServiceId';
import type { GroupListItemConversationType } from '../../components/conversationList/GroupListItem';
import { getRandomColor } from './getRandomColor';
import { ConversationColors } from '../../types/Colors';
@ -32,14 +34,14 @@ export function getDefaultConversation(
conversationColor: ConversationColors[0],
color: getRandomColor(),
firstName,
id: UUID.generate().toString(),
id: generateUuid(),
isMe: false,
lastUpdated: casual.unix_time,
markedUnread: Boolean(overrideProps.markedUnread),
sharedGroupNames: [],
title: `${firstName} ${lastName}`,
titleNoDefault: `${firstName} ${lastName}`,
uuid: UUID.generate().toString(),
uuid: generateAci(),
...overrideProps,
type: 'direct' as const,
acknowledgedGroupNameCollisions: undefined,
@ -63,7 +65,7 @@ export function getDefaultGroup(
overrideProps: Partial<ConversationType> = {}
): ConversationType {
const memberships = Array.from(Array(casual.integer(1, 20)), () => ({
uuid: UUID.generate().toString(),
uuid: generateAci(),
isAdmin: Boolean(casual.coin_flip),
}));
@ -75,10 +77,10 @@ export function getDefaultGroup(
color: getRandomColor(),
conversationColor: ConversationColors[0],
groupDescription: casual.sentence,
groupId: UUID.generate().toString(),
groupId: generateUuid(),
groupLink: casual.url,
groupVersion: 2,
id: UUID.generate().toString(),
id: generateUuid(),
isMe: false,
lastUpdated: casual.unix_time,
markedUnread: Boolean(overrideProps.markedUnread),
@ -86,7 +88,7 @@ export function getDefaultGroup(
memberships,
sharedGroupNames: [],
title: casual.title,
uuid: UUID.generate().toString(),
uuid: generateAci(),
acknowledgedGroupNameCollisions: {},
storySendMode: StorySendMode.IfActive,
...overrideProps,
@ -96,8 +98,8 @@ export function getDefaultGroup(
export function getDefaultConversationWithUuid(
overrideProps: Partial<ConversationType> = {},
uuid: UUIDStringType = UUID.generate().toString()
): ConversationType & { uuid: UUIDStringType } {
uuid: ServiceIdString = generateAci()
): ConversationType & { uuid: ServiceIdString } {
return {
...getDefaultConversation(overrideProps),
uuid,

View file

@ -6,7 +6,8 @@ import casual from 'casual';
import type { StoryDistributionListDataType } from '../../state/ducks/storyDistributionLists';
import type { StoryDistributionListWithMembersDataType } from '../../types/Stories';
import { MY_STORY_ID } from '../../types/Stories';
import { UUID } from '../../types/UUID';
import { generateStoryDistributionId } from '../../types/StoryDistributionId';
import { generateAci } from '../../types/ServiceId';
import { getDefaultConversation } from './getDefaultConversation';
export function getFakeDistributionListsWithMembers(): Array<StoryDistributionListWithMembersDataType> {
@ -34,10 +35,10 @@ export function getFakeDistributionLists(): Array<StoryDistributionListDataType>
export function getFakeDistributionList(): StoryDistributionListDataType {
return {
allowsReplies: Boolean(casual.coin_flip),
id: UUID.generate().toString(),
id: generateStoryDistributionId(),
isBlockList: false,
memberUuids: Array.from(Array(casual.integer(3, 12)), () =>
UUID.generate().toString()
memberServiceIds: Array.from(Array(casual.integer(3, 12)), () =>
generateAci()
),
name: casual.title,
};
@ -48,7 +49,7 @@ export function getMyStories(): StoryDistributionListDataType {
allowsReplies: true,
id: MY_STORY_ID,
isBlockList: true,
memberUuids: [],
memberServiceIds: [],
name: MY_STORY_ID,
};
}

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import casual from 'casual';
import { v4 as generateUuid } from 'uuid';
import type { AttachmentType } from '../../types/Attachment';
import type { ConversationType } from '../../state/ducks/conversations';
@ -11,7 +12,6 @@ import type {
StoryViewType,
} from '../../types/Stories';
import * as durations from '../../util/durations';
import { UUID } from '../../types/UUID';
import { getDefaultConversation } from './getDefaultConversation';
import { fakeAttachment, fakeThumbnail } from './fakeAttachment';
import { MY_STORY_ID, ResolvedSendStatus } from '../../types/Stories';
@ -28,7 +28,7 @@ export function getFakeMyStory(id?: string, name?: string): MyStoryType {
const storyCount = casual.integer(2, 6);
return {
id: id || UUID.generate().toString(),
id: id || generateUuid(),
name: name || id === MY_STORY_ID ? 'My Stories' : casual.catch_phrase,
reducedSendStatus: ResolvedSendStatus.Sent,
stories: Array.from(Array(storyCount), () => ({
@ -45,7 +45,7 @@ export function getFakeStoryView(
): StoryViewType {
const sender = getDefaultConversation();
const messageId = UUID.generate().toString();
const messageId = generateUuid();
return {
attachment: getAttachmentWithThumbnail(

View file

@ -11,7 +11,9 @@ import {
import type { ProcessedAttachment } from '../textsecure/Types.d';
import { SignalService as Proto } from '../protobuf';
import { IMAGE_GIF } from '../types/MIME';
import { generateAci } from '../types/ServiceId';
const AUTHOR_SERVICE_ID = generateAci();
const FLAGS = Proto.DataMessage.Flags;
const TIMESTAMP = Date.now();
@ -122,7 +124,7 @@ describe('processDataMessage', () => {
const out = check({
quote: {
id: Long.fromNumber(1),
authorUuid: 'author',
authorAci: AUTHOR_SERVICE_ID,
text: 'text',
attachments: [
{
@ -136,7 +138,7 @@ describe('processDataMessage', () => {
assert.deepStrictEqual(out.quote, {
id: 1,
authorUuid: 'author',
authorAci: AUTHOR_SERVICE_ID,
text: 'text',
attachments: [
{
@ -188,7 +190,7 @@ describe('processDataMessage', () => {
{
emoji: '😎',
remove: false,
targetAuthorUuid: undefined,
targetAuthorAci: undefined,
targetTimestamp: TIMESTAMP,
}
);
@ -204,7 +206,7 @@ describe('processDataMessage', () => {
{
emoji: '😎',
remove: true,
targetAuthorUuid: undefined,
targetAuthorAci: undefined,
targetTimestamp: TIMESTAMP,
}
);

View file

@ -2,21 +2,21 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { UUID } from '../types/UUID';
import { generateAci } from '../types/ServiceId';
import { processSyncMessage } from '../textsecure/processSyncMessage';
describe('processSyncMessage', () => {
const destinationUuid = UUID.generate().toString();
const destinationServiceId = generateAci();
it('should normalize UUIDs in sent (aci)', () => {
it('should normalize UUIDs in sent', () => {
const out = processSyncMessage({
sent: {
destinationAci: destinationUuid.toUpperCase(),
destinationServiceId: destinationServiceId.toUpperCase(),
unidentifiedStatus: [
{
destinationAci: destinationUuid.toUpperCase(),
destinationServiceId: destinationServiceId.toUpperCase(),
},
],
},
@ -24,51 +24,12 @@ describe('processSyncMessage', () => {
assert.deepStrictEqual(out, {
sent: {
destinationUuid: {
aci: destinationUuid,
pni: undefined,
},
destinationServiceId,
storyMessageRecipients: undefined,
unidentifiedStatus: [
{
destinationUuid: {
aci: destinationUuid,
pni: undefined,
},
},
],
},
});
});
it('should normalize UUIDs in sent (pni)', () => {
const out = processSyncMessage({
sent: {
destinationPni: destinationUuid.toUpperCase(),
unidentifiedStatus: [
{
destinationPni: destinationUuid.toUpperCase(),
},
],
},
});
assert.deepStrictEqual(out, {
sent: {
destinationUuid: {
aci: undefined,
pni: destinationUuid,
},
storyMessageRecipients: undefined,
unidentifiedStatus: [
{
destinationUuid: {
aci: undefined,
pni: destinationUuid,
},
destinationServiceId,
},
],
},

View file

@ -6,6 +6,7 @@ import { v4 as uuid } from 'uuid';
import { omit } from 'lodash';
import type { MessageReactionType } from '../../model-types.d';
import { isEmpty } from '../../util/iterables';
import { generateAci } from '../../types/ServiceId';
import {
addOutgoingReaction,
@ -24,7 +25,7 @@ describe('reaction utilities', () => {
): MessageReactionType => ({
emoji,
fromId: OUR_CONVO_ID,
targetAuthorUuid: uuid(),
targetAuthorUuid: generateAci(),
targetTimestamp: Date.now(),
timestamp: Date.now(),
...(isPending ? { isSentByConversationId: { [uuid()]: false } } : {}),

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { v4 as generateUuid } from 'uuid';
import {
ComposerStep,
@ -27,7 +28,7 @@ import {
getComposeSelectedContacts,
getContactNameColorSelector,
getConversationByIdSelector,
getConversationUuidsStoppingSend,
getConversationServiceIdsStoppingSend,
getConversationsByTitleSelector,
getConversationSelector,
getConversationsStoppingSend,
@ -46,8 +47,8 @@ import { noopAction } from '../../../state/ducks/noop';
import type { StateType } from '../../../state/reducer';
import { reducer as rootReducer } from '../../../state/reducer';
import { setupI18n } from '../../../util/setupI18n';
import { UUID } from '../../../types/UUID';
import type { UUIDStringType } from '../../../types/UUID';
import type { ServiceIdString } from '../../../types/ServiceId';
import { generateAci, getAciFromPrefix } from '../../../types/ServiceId';
import enMessages from '../../../../_locales/en/messages.json';
import {
getDefaultConversation,
@ -61,8 +62,8 @@ import {
} from '../../helpers/defaultComposerStates';
describe('both/state/selectors/conversations-extra', () => {
const UUID_1 = UUID.generate().toString();
const UUID_2 = UUID.generate().toString();
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const getEmptyRootState = (): StateType => {
return rootReducer(undefined, noopAction());
@ -90,7 +91,7 @@ describe('both/state/selectors/conversations-extra', () => {
function makeConversationWithUuid(
id: string
): ConversationType & { uuid: UUIDStringType } {
): ConversationType & { uuid: ServiceIdString } {
const title = `${id} title`;
return getDefaultConversationWithUuid(
@ -100,7 +101,7 @@ describe('both/state/selectors/conversations-extra', () => {
title,
titleNoDefault: title,
},
UUID.fromPrefix(id).toString()
getAciFromPrefix(id)
);
}
@ -315,32 +316,32 @@ describe('both/state/selectors/conversations-extra', () => {
});
it('returns all conversations stopping send', () => {
const convo1 = makeConversation(UUID_1);
const convo2 = makeConversation(UUID_2);
const convo1 = makeConversation(SERVICE_ID_1);
const convo2 = makeConversation(SERVICE_ID_2);
const state: StateType = {
...getEmptyRootState(),
conversations: {
...getEmptyState(),
conversationLookup: {
[UUID_1]: convo1,
[UUID_2]: convo2,
[SERVICE_ID_1]: convo1,
[SERVICE_ID_2]: convo2,
},
verificationDataByConversation: {
'convo a': {
type: ConversationVerificationState.PendingVerification as const,
uuidsNeedingVerification: [UUID_1],
serviceIdsNeedingVerification: [SERVICE_ID_1],
},
'convo b': {
type: ConversationVerificationState.PendingVerification as const,
uuidsNeedingVerification: [UUID_2, UUID_1],
serviceIdsNeedingVerification: [SERVICE_ID_2, SERVICE_ID_1],
},
},
},
};
assert.sameDeepMembers(getConversationUuidsStoppingSend(state), [
UUID_1,
UUID_2,
assert.sameDeepMembers(getConversationServiceIdsStoppingSend(state), [
SERVICE_ID_1,
SERVICE_ID_2,
]);
assert.sameDeepMembers(getConversationsStoppingSend(state), [
@ -368,7 +369,7 @@ describe('both/state/selectors/conversations-extra', () => {
[abc.uuid]: abc,
[def.uuid]: def,
},
invitedUuidsForNewlyCreatedGroup: [def.uuid, abc.uuid],
invitedServiceIdsForNewlyCreatedGroup: [def.uuid, abc.uuid],
},
};
const result = getInvitedContactsForNewlyCreatedGroup(state);
@ -1144,7 +1145,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'No timestamp',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1165,7 +1166,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'B',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1186,7 +1187,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'C',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1207,7 +1208,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'A',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1228,7 +1229,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'First!',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1270,7 +1271,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin Two',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1292,7 +1293,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin Three',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1314,7 +1315,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin One',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1353,7 +1354,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin Two',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1374,7 +1375,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin Three',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1395,7 +1396,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin One',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1417,7 +1418,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin One',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),
@ -1438,7 +1439,7 @@ describe('both/state/selectors/conversations-extra', () => {
title: 'Pin One',
unreadCount: 1,
isSelected: false,
typingContactId: UUID.generate().toString(),
typingContactId: generateUuid(),
acceptedMessageRequest: true,
}),

View file

@ -21,7 +21,7 @@ import {
getSearchResults,
} from '../../../state/selectors/search';
import { makeLookup } from '../../../util/makeLookup';
import { UUID } from '../../../types/UUID';
import { generateAci } from '../../../types/ServiceId';
import {
getDefaultConversation,
getDefaultConversationWithUuid,
@ -58,7 +58,7 @@ describe('both/state/selectors/search', () => {
received_at: NOW,
sent_at: NOW,
source: 'source',
sourceUuid: UUID.generate().toString(),
sourceUuid: generateAci(),
timestamp: NOW,
type: 'incoming' as const,
readStatus: ReadStatus.Read,

View file

@ -11,9 +11,15 @@ import {
insertRange,
processBodyRangesForSearchResult,
} from '../../types/BodyRange';
import { generateAci } from '../../types/ServiceId';
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const SERVICE_ID_4 = generateAci();
const mentionInfo = {
mentionUuid: 'someid',
mentionUuid: SERVICE_ID_1,
conversationID: 'convoid',
replacementText: 'dude',
};
@ -785,14 +791,14 @@ describe('BodyRanges', () => {
{
start: 0,
length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8',
mentionUuid: SERVICE_ID_2,
replacementText: 'Alice',
conversationID: 'x',
},
{
start: 21,
length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8',
mentionUuid: SERVICE_ID_2,
replacementText: 'Eve',
conversationID: 'x',
},
@ -821,21 +827,21 @@ describe('BodyRanges', () => {
{
start: 18,
length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8',
mentionUuid: SERVICE_ID_2,
replacementText: 'Alice',
conversationID: 'x',
},
{
start: 39,
length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8',
mentionUuid: SERVICE_ID_2,
replacementText: 'Bob',
conversationID: 'x',
},
{
start: 45,
length: 1,
mentionUuid: '7d007e95-771d-43ad-9191-eaa86c773cb8',
mentionUuid: SERVICE_ID_2,
replacementText: 'Eve',
conversationID: 'x',
},
@ -941,14 +947,14 @@ describe('BodyRanges', () => {
{
start: 0,
length: 1,
mentionUuid: 'blarg',
mentionUuid: SERVICE_ID_3,
replacementText: 'jerry',
conversationID: 'x',
},
{
start: 7,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'fred',
conversationID: 'x',
},
@ -986,14 +992,14 @@ describe('BodyRanges', () => {
{
start: 49,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'alice',
conversationID: 'x',
},
{
start: 55,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'bob',
conversationID: 'x',
},
@ -1019,21 +1025,21 @@ describe('BodyRanges', () => {
{
start: 0,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'eve',
conversationID: 'x',
},
{
start: 52,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'alice',
conversationID: 'x',
},
{
start: 58,
length: 1,
mentionUuid: 'abcdef',
mentionUuid: SERVICE_ID_4,
replacementText: 'bob',
conversationID: 'x',
},

View file

@ -0,0 +1,46 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { v4 as generateUuid } from 'uuid';
import * as sinon from 'sinon';
import type { LoggerType } from '../../types/Logging';
import { normalizeStoryDistributionId } from '../../types/StoryDistributionId';
describe('StoryDistributionId', () => {
let warn: sinon.SinonStub;
let logger: Pick<LoggerType, 'warn'>;
beforeEach(() => {
warn = sinon.stub();
logger = { warn };
});
describe('normalizeStoryDistributionId', () => {
it('converts uuid to lower case', () => {
const uuid = generateUuid();
assert.strictEqual(
normalizeStoryDistributionId(uuid, 'context 1', logger),
uuid
);
assert.strictEqual(
normalizeStoryDistributionId(uuid.toUpperCase(), 'context 2', logger),
uuid
);
sinon.assert.notCalled(warn);
});
it("warns if passed a string that's not a UUID", () => {
normalizeStoryDistributionId('not-UUID-at-all', 'context 3', logger);
sinon.assert.calledOnce(warn);
sinon.assert.calledWith(
warn,
'Normalizing invalid story distribution id: ' +
'not-UUID-at-all to not-uuid-at-all in ' +
'context "context 3"'
);
});
});
});

View file

@ -4,106 +4,120 @@
import { assert } from 'chai';
import type { RecipientsByConversation } from '../../state/ducks/stories';
import type { UUIDStringType } from '../../types/UUID';
import type { ServiceIdString } from '../../types/ServiceId';
import { UUID } from '../../types/UUID';
import { generateAci } from '../../types/ServiceId';
import { generateStoryDistributionId } from '../../types/StoryDistributionId';
import {
getAllUuids,
filterUuids,
getAllServiceIds,
filterServiceIds,
} from '../../util/blockSendUntilConversationsAreVerified';
describe('both/util/blockSendUntilConversationsAreVerified', () => {
const UUID_1 = UUID.generate().toString();
const UUID_2 = UUID.generate().toString();
const UUID_3 = UUID.generate().toString();
const UUID_4 = UUID.generate().toString();
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const SERVICE_ID_4 = generateAci();
describe('#getAllUuids', () => {
const LIST_ID_1 = generateStoryDistributionId();
const LIST_ID_2 = generateStoryDistributionId();
const LIST_ID_3 = generateStoryDistributionId();
describe('#getAllServiceIds', () => {
it('should return empty set for empty object', () => {
const starting: RecipientsByConversation = {};
const expected: Array<UUIDStringType> = [];
const actual = getAllUuids(starting);
const expected: Array<ServiceIdString> = [];
const actual = getAllServiceIds(starting);
assert.sameMembers(Array.from(actual), expected);
});
it('should return uuids multiple conversations', () => {
it('should return serviceIds multiple conversations', () => {
const starting: RecipientsByConversation = {
abc: {
uuids: [UUID_1, UUID_2],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_1, SERVICE_ID_2],
},
def: {
uuids: [],
[LIST_ID_2]: {
serviceIds: [],
},
ghi: {
uuids: [UUID_2, UUID_3],
[LIST_ID_3]: {
serviceIds: [SERVICE_ID_2, SERVICE_ID_3],
},
};
const expected: Array<UUIDStringType> = [UUID_1, UUID_2, UUID_3];
const actual = getAllUuids(starting);
const expected: Array<ServiceIdString> = [
SERVICE_ID_1,
SERVICE_ID_2,
SERVICE_ID_3,
];
const actual = getAllServiceIds(starting);
assert.sameMembers(Array.from(actual), expected);
});
it('should return uuids from byDistributionId and its parent', () => {
it('should return serviceIds from byDistributionId and its parent', () => {
const starting: RecipientsByConversation = {
abc: {
uuids: [UUID_1, UUID_2],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_1, SERVICE_ID_2],
byDistributionId: {
abc: {
uuids: [UUID_3],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_3],
},
def: {
uuids: [],
[LIST_ID_2]: {
serviceIds: [],
},
ghi: {
uuids: [UUID_4],
[LIST_ID_3]: {
serviceIds: [SERVICE_ID_4],
},
},
},
};
const expected: Array<UUIDStringType> = [UUID_1, UUID_2, UUID_3, UUID_4];
const actual = getAllUuids(starting);
const expected: Array<ServiceIdString> = [
SERVICE_ID_1,
SERVICE_ID_2,
SERVICE_ID_3,
SERVICE_ID_4,
];
const actual = getAllServiceIds(starting);
assert.sameMembers(Array.from(actual), expected);
});
it('should return uuids from byDistributionId with empty parent', () => {
it('should return serviceIds from byDistributionId with empty parent', () => {
const starting: RecipientsByConversation = {
abc: {
uuids: [],
[LIST_ID_1]: {
serviceIds: [],
byDistributionId: {
abc: {
uuids: [UUID_3],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_3],
},
},
},
};
const expected: Array<UUIDStringType> = [UUID_3];
const actual = getAllUuids(starting);
const expected: Array<ServiceIdString> = [SERVICE_ID_3];
const actual = getAllServiceIds(starting);
assert.sameMembers(Array.from(actual), expected);
});
});
describe('#filterUuids', () => {
describe('#filterServiceIds', () => {
const starting: RecipientsByConversation = {
abc: {
uuids: [UUID_1],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_1],
byDistributionId: {
abc: {
uuids: [UUID_2, UUID_3],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_2, SERVICE_ID_3],
},
def: {
uuids: [UUID_1],
[LIST_ID_2]: {
serviceIds: [SERVICE_ID_1],
},
},
},
def: {
uuids: [UUID_1, UUID_4],
[LIST_ID_2]: {
serviceIds: [SERVICE_ID_1, SERVICE_ID_4],
},
ghi: {
uuids: [UUID_3],
[LIST_ID_3]: {
serviceIds: [SERVICE_ID_3],
byDistributionId: {
abc: {
uuids: [UUID_4],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_4],
},
},
},
@ -111,34 +125,35 @@ describe('both/util/blockSendUntilConversationsAreVerified', () => {
it('should return empty object if predicate always returns false', () => {
const expected: RecipientsByConversation = {};
const actual = filterUuids(starting, () => false);
const actual = filterServiceIds(starting, () => false);
assert.deepEqual(actual, expected);
});
it('should return exact copy of object if predicate always returns true', () => {
const expected = starting;
const actual = filterUuids(starting, () => true);
const actual = filterServiceIds(starting, () => true);
assert.notStrictEqual(actual, expected);
assert.deepEqual(actual, expected);
});
it('should return just a few uuids for selective predicate', () => {
it('should return just a few serviceIds for selective predicate', () => {
const expected: RecipientsByConversation = {
abc: {
uuids: [],
[LIST_ID_1]: {
serviceIds: [],
byDistributionId: {
abc: {
uuids: [UUID_2, UUID_3],
[LIST_ID_1]: {
serviceIds: [SERVICE_ID_2, SERVICE_ID_3],
},
},
},
ghi: {
uuids: [UUID_3],
[LIST_ID_3]: {
serviceIds: [SERVICE_ID_3],
},
};
const actual = filterUuids(
const actual = filterServiceIds(
starting,
(uuid: UUIDStringType) => uuid === UUID_2 || uuid === UUID_3
(uuid: ServiceIdString) =>
uuid === SERVICE_ID_2 || uuid === SERVICE_ID_3
);
assert.deepEqual(actual, expected);

View file

@ -3,8 +3,8 @@
import { assert } from 'chai';
import type { ConversationType } from '../../state/ducks/conversations';
import { UUID } from '../../types/UUID';
import type { UUIDStringType } from '../../types/UUID';
import { generateAci, normalizeAci } from '../../types/ServiceId';
import type { ServiceIdString } from '../../types/ServiceId';
import { getDefaultConversationWithUuid } from '../helpers/getDefaultConversation';
import { getGroupMemberships } from '../../util/getGroupMemberships';
@ -16,14 +16,14 @@ describe('getGroupMemberships', () => {
discoveredUnregisteredAt: Date.now(),
});
function getConversationByUuid(
uuid: UUIDStringType
function getConversationByServiceId(
serviceId: ServiceIdString
): undefined | ConversationType {
return [
normalConversation1,
normalConversation2,
unregisteredConversation,
].find(conversation => conversation.uuid === uuid);
].find(conversation => conversation.uuid === serviceId);
}
describe('memberships', () => {
@ -32,7 +32,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).memberships;
assert.isEmpty(result);
@ -43,7 +43,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).memberships;
assert.isEmpty(result);
@ -53,7 +53,7 @@ describe('getGroupMemberships', () => {
const conversation = {
memberships: [
{
uuid: UUID.generate().toString(),
uuid: generateAci(),
isAdmin: true,
},
],
@ -61,7 +61,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).memberships;
assert.isEmpty(result);
@ -71,7 +71,7 @@ describe('getGroupMemberships', () => {
const conversation = {
memberships: [
{
uuid: unregisteredConversation.uuid,
uuid: normalizeAci(unregisteredConversation.uuid, 'test'),
isAdmin: true,
},
],
@ -79,7 +79,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).memberships;
assert.lengthOf(result, 1);
@ -93,11 +93,11 @@ describe('getGroupMemberships', () => {
const conversation = {
memberships: [
{
uuid: normalConversation2.uuid,
uuid: normalizeAci(normalConversation2.uuid, 'test'),
isAdmin: false,
},
{
uuid: normalConversation1.uuid,
uuid: normalizeAci(normalConversation1.uuid, 'test'),
isAdmin: true,
},
],
@ -105,7 +105,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).memberships;
assert.lengthOf(result, 2);
@ -126,7 +126,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingApprovalMemberships;
assert.isEmpty(result);
@ -137,7 +137,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingApprovalMemberships;
assert.isEmpty(result);
@ -145,12 +145,12 @@ describe('getGroupMemberships', () => {
it("filters out conversation IDs that don't exist", () => {
const conversation = {
pendingApprovalMemberships: [{ uuid: UUID.generate().toString() }],
pendingApprovalMemberships: [{ uuid: generateAci() }],
};
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingApprovalMemberships;
assert.isEmpty(result);
@ -158,12 +158,14 @@ describe('getGroupMemberships', () => {
it('filters out unregistered conversations', () => {
const conversation = {
pendingApprovalMemberships: [{ uuid: unregisteredConversation.uuid }],
pendingApprovalMemberships: [
{ uuid: normalizeAci(unregisteredConversation.uuid, 'test') },
],
};
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingApprovalMemberships;
assert.isEmpty(result);
@ -172,14 +174,14 @@ describe('getGroupMemberships', () => {
it('hydrates pending-approval memberships', () => {
const conversation = {
pendingApprovalMemberships: [
{ uuid: normalConversation2.uuid },
{ uuid: normalConversation1.uuid },
{ uuid: normalizeAci(normalConversation2.uuid, 'test') },
{ uuid: normalizeAci(normalConversation1.uuid, 'test') },
],
};
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingApprovalMemberships;
assert.lengthOf(result, 2);
@ -194,7 +196,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingMemberships;
assert.isEmpty(result);
@ -205,7 +207,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingMemberships;
assert.isEmpty(result);
@ -215,15 +217,15 @@ describe('getGroupMemberships', () => {
const conversation = {
pendingMemberships: [
{
uuid: UUID.generate().toString(),
addedByUserId: normalConversation1.uuid,
uuid: generateAci(),
addedByUserId: normalizeAci(normalConversation1.uuid, 'test'),
},
],
};
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingMemberships;
assert.isEmpty(result);
@ -234,22 +236,22 @@ describe('getGroupMemberships', () => {
pendingMemberships: [
{
uuid: unregisteredConversation.uuid,
addedByUserId: normalConversation1.uuid,
addedByUserId: normalizeAci(normalConversation1.uuid, 'test'),
},
],
};
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingMemberships;
assert.isEmpty(result);
});
it('hydrates pending memberships', () => {
const abc = UUID.generate().toString();
const xyz = UUID.generate().toString();
const abc = generateAci();
const xyz = generateAci();
const conversation = {
pendingMemberships: [
@ -260,7 +262,7 @@ describe('getGroupMemberships', () => {
const result = getGroupMemberships(
conversation,
getConversationByUuid
getConversationByServiceId
).pendingMemberships;
assert.lengthOf(result, 2);

View file

@ -3,7 +3,7 @@
import { assert } from 'chai';
import { isValidUuid } from '../../types/UUID';
import { isValidUuid } from '../../util/isValidUuid';
describe('isValidUuid', () => {
const LOWERCASE_V4_UUID = '9cb737ce-2bb3-4c21-9fe0-d286caa0ca68';

View file

@ -1,40 +0,0 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { v4 as generateUuid } from 'uuid';
import * as sinon from 'sinon';
import type { LoggerType } from '../../types/Logging';
import { normalizeUuid } from '../../util/normalizeUuid';
describe('normalizeUuid', () => {
let warn: sinon.SinonStub;
let logger: Pick<LoggerType, 'warn'>;
beforeEach(() => {
warn = sinon.stub();
logger = { warn };
});
it('converts uuid to lower case', () => {
const uuid = generateUuid();
assert.strictEqual(normalizeUuid(uuid, 'context 1', logger), uuid);
assert.strictEqual(
normalizeUuid(uuid.toUpperCase(), 'context 2', logger),
uuid
);
sinon.assert.notCalled(warn);
});
it("warns if passed a string that's not a UUID", () => {
normalizeUuid('not-UUID-at-all', 'context 3', logger);
sinon.assert.calledOnce(warn);
sinon.assert.calledWith(
warn,
'Normalizing invalid uuid: not-UUID-at-all to not-uuid-at-all in ' +
'context "context 3"'
);
});
});