Introduce Service Id Types
Co-authored-by: Scott Nonnenberg <scott@signal.org>
This commit is contained in:
parent
414c0a58d3
commit
366b875fd2
269 changed files with 5832 additions and 5550 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
33
ts/test-both/util/isValidUuid.ts
Normal file
33
ts/test-both/util/isValidUuid.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
|
||||
import { isValidUuid } from '../../util/isValidUuid';
|
||||
|
||||
describe('isValidUuid', () => {
|
||||
const LOWERCASE_V4_UUID = '9cb737ce-2bb3-4c21-9fe0-d286caa0ca68';
|
||||
|
||||
it('returns false for non-strings', () => {
|
||||
assert.isFalse(isValidUuid(undefined));
|
||||
assert.isFalse(isValidUuid(null));
|
||||
assert.isFalse(isValidUuid(1234));
|
||||
});
|
||||
|
||||
it('returns false for non-UUID strings', () => {
|
||||
assert.isFalse(isValidUuid(''));
|
||||
assert.isFalse(isValidUuid('hello world'));
|
||||
assert.isFalse(isValidUuid(` ${LOWERCASE_V4_UUID}`));
|
||||
assert.isFalse(isValidUuid(`${LOWERCASE_V4_UUID} `));
|
||||
});
|
||||
|
||||
it("returns false for UUIDs that aren't version 4", () => {
|
||||
assert.isFalse(isValidUuid('a200a6e0-d2d9-11eb-bda7-dd5936a30ddf'));
|
||||
assert.isFalse(isValidUuid('2adb8b83-4f2c-55ca-a481-7f98b716e615'));
|
||||
});
|
||||
|
||||
it('returns true for v4 UUIDs', () => {
|
||||
assert.isTrue(isValidUuid(LOWERCASE_V4_UUID));
|
||||
assert.isTrue(isValidUuid(LOWERCASE_V4_UUID.toUpperCase()));
|
||||
});
|
||||
});
|
|
@ -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"'
|
||||
);
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue