chore: refactor batch request functions to take record

pull/3281/head
Audric Ackermann 5 months ago
parent 315b4b4e4c
commit df2a9dd13f
No known key found for this signature in database

@ -221,7 +221,7 @@ export const NoMessageInConversation = () => {
const isPublic = useSelectedIsPublic();
const getHtmlToRender = () => {
// First, handle the "oteToSelf and various "private" cases
// First, handle the "noteToSelf and various "private" cases
if (isMe) {
return localize('noteToSelfEmpty').toString();
}
@ -229,7 +229,7 @@ export const NoMessageInConversation = () => {
return localize('messageRequestsTurnedOff').withArgs({ name }).toString();
}
if (isPrivate) {
// "You have no messages in X, send a message to start a conversation."
// "You have no messages from X. Send a message to start the conversation!"
return localize('groupNoMessages').withArgs({ group_name: name }).toString();
}
@ -247,6 +247,7 @@ export const NoMessageInConversation = () => {
return localize('groupRemovedYou').withArgs({ group_name: name }).toString();
}
if (canWrite) {
// "You have no messages from X. Send a message to start the conversation!"
return localize('groupNoMessages').withArgs({ group_name: name }).toString();
}
// if we cannot write for some reason, don't show the "send a message" part

@ -33,20 +33,21 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
window?.log?.warn('forceNetworkDeletion: we are offline.');
return null;
}
const snodeToMakeRequestTo = await SnodePool.getNodeFromSwarmOrThrow(usPk);
const targetNode = await SnodePool.getNodeFromSwarmOrThrow(usPk);
const builtRequest = await request.build();
const ret = await BatchRequests.doSnodeBatchRequestNoRetries(
[builtRequest],
snodeToMakeRequestTo,
10000,
usPk,
false
);
const ret = await BatchRequests.doSnodeBatchRequestNoRetries({
subRequests: [builtRequest],
targetNode,
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: usPk,
allow401s: false,
method: 'batch',
});
if (!ret || !ret?.[0].body || ret[0].code !== 200) {
throw new Error(
`Empty response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}`
);
}
@ -58,7 +59,7 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
if (!swarm) {
throw new Error(
`Invalid JSON swarm response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${firstResultParsedBody}`
);
}
@ -66,7 +67,7 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
if (!swarmAsArray.length) {
throw new Error(
`Invalid JSON swarmAsArray response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${firstResultParsedBody}`
);
}
@ -84,7 +85,7 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
if (reason && statusCode) {
window?.log?.warn(
`Could not ${request.method} from ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)} due to error: ${reason}: ${statusCode}`
);
// if we tried to make the delete on a snode not in our swarm, just trigger a pRetry error so the outer block here finds new snodes to make the request to.
@ -95,9 +96,7 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
}
} else {
window?.log?.warn(
`Could not ${request.method} from ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
)}`
`Could not ${request.method} from ${ed25519Str(targetNode.pubkey_ed25519)}`
);
}
return snodePubkey;
@ -133,7 +132,7 @@ const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
} catch (e) {
throw new Error(
`Invalid JSON response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${ret}`
);
}
@ -183,19 +182,19 @@ const networkDeleteMessageOurSwarm = async (
try {
const success = await pRetry(
async () => {
const snodeToMakeRequestTo = await SnodePool.getNodeFromSwarmOrThrow(request.destination);
const targetNode = await SnodePool.getNodeFromSwarmOrThrow(request.destination);
const controller = new AbortController();
const ret = await timeoutWithAbort(
BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[request],
snodeToMakeRequestTo,
10 * DURATION.SECONDS,
request.destination,
false,
'batch',
controller.signal
),
BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [request],
targetNode,
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: request.destination,
allow401s: false,
method: 'batch',
abortSignal: controller.signal,
}),
30 * DURATION.SECONDS,
controller
);
@ -203,7 +202,7 @@ const networkDeleteMessageOurSwarm = async (
if (!ret || !ret?.[0].body || ret[0].code !== 200) {
throw new Error(
`networkDeleteMessageOurSwarm: Empty response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)} about pk: ${ed25519Str(request.destination)}`
);
}
@ -215,7 +214,7 @@ const networkDeleteMessageOurSwarm = async (
if (!swarm) {
throw new Error(
`networkDeleteMessageOurSwarm: Invalid JSON swarm response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${firstResultParsedBody}`
);
}
@ -223,7 +222,7 @@ const networkDeleteMessageOurSwarm = async (
if (!swarmAsArray.length) {
throw new Error(
`networkDeleteMessageOurSwarm: Invalid JSON swarmAsArray response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${firstResultParsedBody}`
);
}
@ -241,13 +240,13 @@ const networkDeleteMessageOurSwarm = async (
if (reason && statusCode) {
window?.log?.warn(
`networkDeleteMessageOurSwarm: Could not ${request.method} from ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)} due to error: ${reason}: ${statusCode}`
);
} else {
window?.log?.warn(
`networkDeleteMessageOurSwarm: Could not ${request.method} from ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}`
);
}
@ -277,7 +276,7 @@ const networkDeleteMessageOurSwarm = async (
} catch (e) {
throw new Error(
`networkDeleteMessageOurSwarm: Invalid JSON response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)}, ${ret}`
);
}
@ -339,18 +338,18 @@ const networkDeleteMessagesForGroup = async (
await pRetry(
async () => {
const snodeToMakeRequestTo = await SnodePool.getNodeFromSwarmOrThrow(request.destination);
const targetNode = await SnodePool.getNodeFromSwarmOrThrow(request.destination);
const controller = new AbortController();
const ret = await timeoutWithAbort(
BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[request],
snodeToMakeRequestTo,
10 * DURATION.SECONDS,
request.destination,
false,
'batch',
controller.signal
),
BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [request],
targetNode,
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: request.destination,
allow401s: false,
method: 'batch',
abortSignal: controller.signal,
}),
30 * DURATION.SECONDS,
controller
);
@ -358,7 +357,7 @@ const networkDeleteMessagesForGroup = async (
if (!ret || !ret?.[0].body || ret[0].code !== 200) {
throw new Error(
`networkDeleteMessagesForGroup: Empty response got for ${request.method} on snode ${ed25519Str(
snodeToMakeRequestTo.pubkey_ed25519
targetNode.pubkey_ed25519
)} about pk: ${ed25519Str(request.destination)}`
);
}

@ -1295,6 +1295,7 @@ type StoreOnNodeSubAccountParams = Pick<
type StoreOnNodeParams = StoreOnNodeNormalParams | StoreOnNodeSubAccountParams;
export type MethodBatchType = 'batch' | 'sequence';
export type WithMethodBatchType = { method: MethodBatchType };
export type RawSnodeSubRequests =
| RetrieveLegacyClosedGroupSubRequest

@ -10,16 +10,20 @@ import {
builtRequestToLoggingId,
BuiltSnodeSubRequests,
MAX_SUBREQUESTS_COUNT,
MethodBatchType,
NotEmptyArrayOfBatchResults,
RawSnodeSubRequests,
WithMethodBatchType,
} from './SnodeRequestTypes';
import { MergedAbortSignal } from './requestWith';
import { MergedAbortSignal, WithTimeoutMs } from './requestWith';
function logSubRequests(requests: Array<BuiltSnodeSubRequests>) {
return `[${requests.map(builtRequestToLoggingId).join(', ')}]`;
}
type WithTargetNode = { targetNode: Snode };
type WithAssociatedWith = { associatedWith: string | null };
type WithAllow401s = { allow401s: boolean };
/**
* This is the equivalent to the batch send on sogs. The target node runs each sub request and returns a list of all the sub status and bodies.
* If the global status code is not 200, an exception is thrown.
@ -32,15 +36,22 @@ function logSubRequests(requests: Array<BuiltSnodeSubRequests>) {
* @param associatedWith used mostly for handling 421 errors, we need the pubkey the change is associated to
* @param method can be either batch or sequence. A batch call will run all calls even if one of them fails. A sequence call will stop as soon as the first one fails
*/
async function doSnodeBatchRequestNoRetries(
subRequests: Array<BuiltSnodeSubRequests>,
targetNode: Snode,
timeoutMs: number,
associatedWith: string | null,
allow401s: boolean,
abortSignal?: MergedAbortSignal,
method: MethodBatchType = 'batch'
): Promise<NotEmptyArrayOfBatchResults> {
async function doSnodeBatchRequestNoRetries({
allow401s,
associatedWith,
method,
subRequests,
targetNode,
timeoutMs,
abortSignal,
}: WithTargetNode &
WithTimeoutMs &
WithAssociatedWith &
WithAllow401s &
WithMethodBatchType & {
subRequests: Array<BuiltSnodeSubRequests>;
abortSignal?: MergedAbortSignal;
}): Promise<NotEmptyArrayOfBatchResults> {
window.log.debug(
`doSnodeBatchRequestNoRetries "${method}":`,
JSON.stringify(logSubRequests(subRequests))
@ -102,27 +113,35 @@ async function doSnodeBatchRequestNoRetries(
* @param timeoutMs the max timeout to wait for a reply
* @param associatedWith the pubkey associated with this request (used to remove snode failing to reply from that users' swarm)
* @param method the type of request to make batch or sequence
* @returns
* @param allow401 on very specific use case, we need to allow 401 (Group kicked event)
* @param abortSignal the signal used to know when we should abort the request
*/
async function doUnsignedSnodeBatchRequestNoRetries(
unsignedSubRequests: Array<RawSnodeSubRequests>,
targetNode: Snode,
timeoutMs: number,
associatedWith: string | null,
allow401s: boolean,
method: MethodBatchType = 'batch',
abortSignal: MergedAbortSignal | null
): Promise<NotEmptyArrayOfBatchResults> {
async function doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests,
targetNode,
timeoutMs,
associatedWith,
method,
allow401s,
abortSignal,
}: WithTargetNode &
WithTimeoutMs &
WithAssociatedWith &
WithAllow401s &
WithMethodBatchType & {
unsignedSubRequests: Array<RawSnodeSubRequests>;
abortSignal: MergedAbortSignal | null;
}): Promise<NotEmptyArrayOfBatchResults> {
const signedSubRequests = await MessageSender.signSubRequests(unsignedSubRequests);
return BatchRequests.doSnodeBatchRequestNoRetries(
signedSubRequests,
return BatchRequests.doSnodeBatchRequestNoRetries({
subRequests: signedSubRequests,
targetNode,
timeoutMs,
associatedWith,
allow401s,
abortSignal || undefined,
method
);
abortSignal: abortSignal || undefined,
method,
});
}
/**

@ -146,15 +146,15 @@ async function updateExpiryOnNodesNoRetries(
expireRequests: Array<UpdateExpiryOnNodeUserSubRequest>
): Promise<Array<UpdatedExpiryWithHash>> {
try {
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
expireRequests,
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: expireRequests,
targetNode,
10 * DURATION.SECONDS,
ourPubKey,
false,
'batch',
null
);
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: ourPubKey,
allow401s: false,
method: 'batch',
abortSignal: null,
});
if (!result || result.length !== expireRequests.length) {
window.log.error(
@ -381,7 +381,7 @@ export async function expireMessagesOnSnode(
// TODO after the next storage server fork we will get a new endpoint allowing to batch
// update expiries even when they are * not * the same for all the message hashes.
// But currently we can't access it that endpoint, so we need to keep this hacky way for now.
// groupby expiries ( expireTimer+ readAt), then batch them with a limit of MAX_SUBREQUESTS_COUNT batch calls per batch requests, then do those in parralel, for now.
// group by expiries ( expireTimer+ readAt), then batch them with a limit of MAX_SUBREQUESTS_COUNT batch calls per batch requests, then do those in parallel, for now.
const expireRequestsParams = await Promise.all(
chunkedExpiries.map(chk =>
getBatchExpiryChunk({

@ -47,15 +47,15 @@ async function getExpiriesFromNodesNoRetries(
) {
try {
const expireRequest = new GetExpiriesFromNodeSubRequest({ messagesHashes: messageHashes });
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[expireRequest],
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [expireRequest],
targetNode,
10 * DURATION.SECONDS,
timeoutMs: 10 * DURATION.SECONDS,
associatedWith,
false,
'batch',
null
);
allow401s: false,
method: 'batch',
abortSignal: null,
});
if (!result || result.length !== 1) {
throw Error(

@ -15,15 +15,15 @@ import { DURATION } from '../../constants';
async function getSnodePoolFromSnode(targetNode: Snode): Promise<Array<Snode>> {
const subRequest = new GetServiceNodesSubRequest();
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[subRequest],
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [subRequest],
targetNode,
10 * DURATION.SECONDS,
null,
false,
'batch',
null
);
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: null,
allow401s: false,
method: 'batch',
abortSignal: null,
});
const firstResult = results[0];

@ -20,19 +20,19 @@ async function requestSnodesForPubkeyWithTargetNodeRetryable(
}
const subRequest = new SwarmForSubRequest(pubkey);
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[subRequest],
const result = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [subRequest],
targetNode,
10 * DURATION.SECONDS,
pubkey,
false,
'batch',
null
);
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: pubkey,
allow401s: false,
method: 'batch',
abortSignal: null,
});
if (!result || !result.length) {
window?.log?.warn(
`SessionSnodeAPI::requestSnodesForPubkeyWithTargetNodeRetryable - sessionRpc on ${targetNode.ip}:${targetNode.port} returned falsish value`,
`SessionSnodeAPI::requestSnodesForPubkeyWithTargetNodeRetryable - sessionRpc on ${targetNode.ip}:${targetNode.port} returned falsy value`,
result
);
throw new Error('requestSnodesForPubkeyWithTargetNodeRetryable: Invalid result');
@ -49,7 +49,7 @@ async function requestSnodesForPubkeyWithTargetNodeRetryable(
const body = firstResult.body;
if (!body.snodes || !isArray(body.snodes) || !body.snodes.length) {
window?.log?.warn(
`SessionSnodeAPI::requestSnodesForPubkeyRetryable - sessionRpc on ${targetNode.ip}:${targetNode.port} returned falsish value for snodes`,
`SessionSnodeAPI::requestSnodesForPubkeyRetryable - sessionRpc on ${targetNode.ip}:${targetNode.port} returned falsy value for snodes`,
result
);
throw new Error('requestSnodesForPubkey: Invalid json (empty)');
@ -117,7 +117,7 @@ async function requestSnodesForPubkeyRetryable(pubKey: string): Promise<Array<Sn
export async function requestSnodesForPubkeyFromNetwork(pubKey: string): Promise<Array<Snode>> {
try {
// catch exception in here only.
// the idea is that the pretry will retry a few times each calls, except if an AbortError is thrown.
// the idea is that the p-retry will retry a few times each calls, except if an AbortError is thrown.
// if all retry fails, we will end up in the catch below when the last exception thrown
return await requestSnodesForPubkeyRetryable(pubKey);

@ -36,18 +36,18 @@ async function getSessionIDForOnsName(onsNameCase: string) {
const promises = range(0, validationCount).map(async () => {
const targetNode = await SnodePool.getRandomSnode();
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
[subRequest],
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: [subRequest],
targetNode,
10 * DURATION.SECONDS,
null,
false,
'batch',
null
);
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: null,
allow401s: false,
method: 'batch',
abortSignal: null,
});
const firstResult = results[0];
if (!firstResult || firstResult.code !== 200 || !firstResult.body) {
throw new Error('ONSresolve:Failed to resolve ONS');
throw new Error('OnsResolve :Failed to resolve ONS');
}
const parsedBody = firstResult.body;
GetNetworkTime.handleTimestampOffsetFromNetwork('ons_resolve', parsedBody.t);
@ -55,7 +55,7 @@ async function getSessionIDForOnsName(onsNameCase: string) {
const intermediate = parsedBody?.result;
if (!intermediate || !intermediate?.encrypted_value) {
throw new NotFoundError('ONSresolve: no encrypted_value');
throw new NotFoundError('OnsResolve: no encrypted_value');
}
const hexEncodedCipherText = intermediate?.encrypted_value;
@ -65,18 +65,18 @@ async function getSessionIDForOnsName(onsNameCase: string) {
const hexEncodedNonce = intermediate.nonce as string;
if (!hexEncodedNonce) {
throw new Error('ONSresolve: No hexEncodedNonce');
throw new Error('OnsResolve: No hexEncodedNonce');
}
const nonce = fromHexToArray(hexEncodedNonce);
try {
key = sodium.crypto_generichash(sodium.crypto_generichash_BYTES, nameAsData, nameHash);
if (!key) {
throw new Error('ONSresolve: Hashing failed');
throw new Error('OnsResolve: Hashing failed');
}
} catch (e) {
window?.log?.warn('ONSresolve: hashing failed', e);
throw new Error('ONSresolve: Hashing failed');
window?.log?.warn('OnsResolve: hashing failed', e);
throw new Error('OnsResolve: Hashing failed');
}
const sessionIDAsData = sodium.crypto_aead_xchacha20poly1305_ietf_decrypt(
@ -88,7 +88,7 @@ async function getSessionIDForOnsName(onsNameCase: string) {
);
if (!sessionIDAsData) {
throw new Error('ONSresolve: Decryption failed');
throw new Error('OnsResolve: Decryption failed');
}
return toHex(sessionIDAsData);
@ -98,16 +98,16 @@ async function getSessionIDForOnsName(onsNameCase: string) {
// if one promise throws, we end un the catch case
const allResolvedSessionIds = await Promise.all(promises);
if (allResolvedSessionIds?.length !== validationCount) {
throw new Error('ONSresolve: Validation failed');
throw new Error('OnsResolve: Validation failed');
}
// assert all the returned account ids are the same
if (_.uniq(allResolvedSessionIds).length !== 1) {
throw new Error('ONSresolve: Validation failed');
throw new Error('OnsResolve: Validation failed');
}
return allResolvedSessionIds[0];
} catch (e) {
window.log.warn('ONSresolve: error', e);
window.log.warn('OnsResolve: error', e);
throw e;
}
}

@ -2,7 +2,7 @@ import { AbortSignal } from 'abort-controller';
// eslint-disable-next-line import/no-unresolved
import { AbortSignal as AbortSignalNode } from 'node-fetch/externals';
export type MergedAbortSignal = AbortSignal | AbortSignalNode
export type MergedAbortSignal = AbortSignal | AbortSignalNode;
export type WithTimeoutMs = { timeoutMs: number };
export type WithAbortSignal = { abortSignal: MergedAbortSignal };

@ -226,16 +226,16 @@ async function retrieveNextMessagesNoRetries(
// no retry for this one as this a call we do every few seconds while polling for messages
// just to make sure that we don't hang for more than timeOutMs
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
rawRequests,
const results = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: rawRequests,
targetNode,
// yes this is a long timeout for just messages, but 4s timeouts way to often...
10 * DURATION.SECONDS,
timeoutMs: 10 * DURATION.SECONDS,
associatedWith,
allow401s,
'batch',
null
);
method: 'batch',
abortSignal: null,
});
try {
if (!results || !isArray(results) || !results.length) {
window?.log?.warn(

@ -289,15 +289,15 @@ async function sendSingleMessage({
const targetNode = await SnodePool.getNodeFromSwarmOrThrow(destination);
const batchResult = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
subRequests,
const batchResult = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: subRequests,
targetNode,
10 * DURATION.SECONDS,
destination,
false,
'sequence',
null
);
timeoutMs: 10 * DURATION.SECONDS,
associatedWith: destination,
allow401s: false,
method: 'sequence',
abortSignal: null,
});
await handleBatchResultWithSubRequests({ batchResult, subRequests, destination });
return {
@ -441,15 +441,15 @@ async function sendMessagesDataToSnode<T extends PubkeyType | GroupPubkeyType>({
const targetNode = await SnodePool.getNodeFromSwarmOrThrow(associatedWith);
try {
const responses = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries(
sortedSubRequests,
const responses = await BatchRequests.doUnsignedSnodeBatchRequestNoRetries({
unsignedSubRequests: sortedSubRequests,
targetNode,
6000,
timeoutMs: 6 * DURATION.SECONDS,
associatedWith,
false,
allow401s: false,
method,
abortSignal
);
abortSignal,
});
if (!responses || !responses.length) {
window?.log?.warn(

@ -16,7 +16,7 @@ export type WithMaxSize = { max_size?: number };
export type WithShortenOrExtend = { shortenOrExtend: 'shorten' | 'extend' | '' };
export type WithCreatedAtNetworkTimestamp = { createdAtNetworkTimestamp: number };
export type WithMethod<T extends string> = { method: T };
export type WithBatchMethod<T extends string> = { method: T };
export type WithConvoId = { conversationId: string };
export type WithMessageId = { messageId: string };

@ -133,11 +133,11 @@ describe('MessageSender', () => {
});
describe('logic', () => {
let messageEncyrptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
let messageEncryptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
beforeEach(() => {
encryptStub.callsFake(async (_device, plainTextBuffer, _type) => ({
envelopeType: messageEncyrptReturnEnvelopeType,
envelopeType: messageEncryptReturnEnvelopeType,
cipherText: plainTextBuffer,
}));
});
@ -164,26 +164,28 @@ describe('MessageSender', () => {
const args = doSnodeBatchRequestStub.getCall(0).args;
expect(args[3]).to.equal(device.key);
expect(args[0].associatedWith).to.equal(device.key);
const firstArg = args[0];
expect(firstArg.length).to.equal(1);
expect(firstArg.subRequests.length).to.equal(1);
if (firstArg[0].method !== 'store') {
const firstSubRequest = firstArg.subRequests[0];
if (firstSubRequest.method !== 'store') {
throw new Error('expected a store request with data');
}
// expect(args[3]).to.equal(visibleMessage.timestamp); the timestamp is overwritten on sending by the network clock offset
expect(firstArg[0].params.ttl).to.equal(visibleMessage.ttl());
expect(firstArg[0].params.pubkey).to.equal(device.key);
expect(firstArg[0].params.namespace).to.equal(SnodeNamespaces.Default);
expect(firstSubRequest.params.ttl).to.equal(visibleMessage.ttl());
expect(firstSubRequest.params.pubkey).to.equal(device.key);
expect(firstSubRequest.params.namespace).to.equal(SnodeNamespaces.Default);
// the request timestamp is always used fresh with the offset as the request will be denied with a 406 otherwise (clock out of sync)
expect(firstArg[0].params.timestamp).to.be.above(Date.now() - 10);
expect(firstArg[0].params.timestamp).to.be.below(Date.now() + 10);
expect(firstSubRequest.params.timestamp).to.be.above(Date.now() - 10);
expect(firstSubRequest.params.timestamp).to.be.below(Date.now() + 10);
});
it('should correctly build the envelope and override the request timestamp but not the msg one', async () => {
TestUtils.setupTestWithSending();
messageEncyrptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
messageEncryptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
// This test assumes the encryption stub returns the plainText passed into it.
const device = TestUtils.generateFakePubKey();
@ -204,11 +206,12 @@ describe('MessageSender', () => {
});
const firstArg = doSnodeBatchRequestStub.getCall(0).args[0];
const firstSubRequest = firstArg.subRequests[0];
if (firstArg[0].method !== 'store') {
if (firstSubRequest.method !== 'store') {
throw new Error('expected a store request with data');
}
const data = fromBase64ToArrayBuffer(firstArg[0].params.data);
const data = fromBase64ToArrayBuffer(firstSubRequest.params.data);
const webSocketMessage = SignalService.WebSocketMessage.decode(new Uint8Array(data));
expect(webSocketMessage.request?.body).to.not.equal(
undefined,
@ -226,7 +229,7 @@ describe('MessageSender', () => {
expect(envelope.source).to.equal('');
// the timestamp in the message is not overridden on sending as it should be set with the network offset when created.
// we need that timestamp to not be overriden as the signature of the message depends on it.
// we need that timestamp to not be overridden as the signature of the message depends on it.
const decodedTimestampFromSending = _.toNumber(envelope.timestamp);
expect(decodedTimestampFromSending).to.be.eq(visibleMessage.createAtNetworkTimestamp);
@ -236,7 +239,7 @@ describe('MessageSender', () => {
describe('SESSION_MESSAGE', () => {
it('should set the envelope source to be empty', async () => {
TestUtils.setupTestWithSending();
messageEncyrptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
messageEncryptReturnEnvelopeType = SignalService.Envelope.Type.SESSION_MESSAGE;
Sinon.stub(ConvoHub.use(), 'get').returns(undefined as any);
// This test assumes the encryption stub returns the plainText passed into it.
@ -255,11 +258,12 @@ describe('MessageSender', () => {
});
const firstArg = doSnodeBatchRequestStub.getCall(0).args[0];
const firstSubRequest = firstArg.subRequests[0];
if (firstArg[0].method !== 'store') {
if (firstSubRequest.method !== 'store') {
throw new Error('expected a store request with data');
}
const data = fromBase64ToArrayBuffer(firstArg[0].params.data);
const data = fromBase64ToArrayBuffer(firstSubRequest.params.data);
const webSocketMessage = SignalService.WebSocketMessage.decode(new Uint8Array(data));
expect(webSocketMessage.request?.body).to.not.equal(
undefined,
@ -343,17 +347,17 @@ describe('MessageSender', () => {
Sinon.stub(OnionSending, 'getMinTimeoutForSogs').returns(5);
const decodev4responseStub = Sinon.stub(OnionV4, 'decodeV4Response');
decodev4responseStub.throws('whate');
const decodeV4responseStub = Sinon.stub(OnionV4, 'decodeV4Response');
decodeV4responseStub.throws('whatever');
decodev4responseStub.onThirdCall().returns({
decodeV4responseStub.onThirdCall().returns({
metadata: { code: 200 },
body: {},
bodyBinary: new Uint8Array(),
bodyContentType: 'a',
});
await MessageSender.sendToOpenGroupV2(message, roomInfos, false, []);
expect(decodev4responseStub.callCount).to.eq(3);
expect(decodeV4responseStub.callCount).to.eq(3);
});
it('should not retry more than 3 sendOnionRequestHandlingSnodeEjectStub ', async () => {
@ -362,10 +366,10 @@ describe('MessageSender', () => {
Sinon.stub(Onions, 'sendOnionRequestHandlingSnodeEjectNoRetries').resolves({} as any);
Sinon.stub(OnionSending, 'getMinTimeoutForSogs').returns(5);
const decodev4responseStub = Sinon.stub(OnionV4, 'decodeV4Response');
decodev4responseStub.throws('whate');
const decodeV4responseStub = Sinon.stub(OnionV4, 'decodeV4Response');
decodeV4responseStub.throws('whatever');
decodev4responseStub.onCall(4).returns({
decodeV4responseStub.onCall(4).returns({
metadata: { code: 200 },
body: {},
bodyBinary: new Uint8Array(),
@ -375,7 +379,7 @@ describe('MessageSender', () => {
await MessageSender.sendToOpenGroupV2(message, roomInfos, false, []);
} catch (e) {}
// we made the fourth call success, but we should not get there. We should stop at 3 the retries (1+2)
expect(decodev4responseStub.calledThrice);
expect(decodeV4responseStub.calledThrice);
});
});
});

@ -141,10 +141,13 @@ describe('GroupSyncJob resultsToSuccessfulChange', () => {
).to.be.deep.eq([]);
expect(
LibSessionUtil.batchResultsToGroupSuccessfulChange([] as unknown as NotEmptyArrayOfBatchResults, {
allOldHashes: new Set(),
messages: [],
})
LibSessionUtil.batchResultsToGroupSuccessfulChange(
[] as unknown as NotEmptyArrayOfBatchResults,
{
allOldHashes: new Set(),
messages: [],
}
)
).to.be.deep.eq([]);
});

Loading…
Cancel
Save