signal-desktop/ts/test-electron/services/profiles_test.ts
2024-02-16 15:40:38 -05:00

193 lines
5.9 KiB
TypeScript

// Copyright 2022 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { assert } from 'chai';
import { sleep } from '../../util/sleep';
import { MINUTE } from '../../util/durations';
import { drop } from '../../util/drop';
import { ProfileService } from '../../services/profiles';
import { generateAci } from '../../types/ServiceId';
import { HTTPError } from '../../textsecure/Errors';
describe('util/profiles', () => {
const SERVICE_ID_1 = generateAci();
const SERVICE_ID_2 = generateAci();
const SERVICE_ID_3 = generateAci();
const SERVICE_ID_4 = generateAci();
const SERVICE_ID_5 = generateAci();
beforeEach(async () => {
await window.ConversationController.getOrCreateAndWait(
SERVICE_ID_1,
'private'
);
await window.ConversationController.getOrCreateAndWait(
SERVICE_ID_2,
'private'
);
await window.ConversationController.getOrCreateAndWait(
SERVICE_ID_3,
'private'
);
await window.ConversationController.getOrCreateAndWait(
SERVICE_ID_4,
'private'
);
await window.ConversationController.getOrCreateAndWait(
SERVICE_ID_5,
'private'
);
});
describe('clearAll', () => {
it('Cancels all in-flight requests', async () => {
const getProfileWithLongDelay = async () => {
await sleep(MINUTE);
};
const service = new ProfileService(getProfileWithLongDelay);
const promise1 = service.get(SERVICE_ID_1);
const promise2 = service.get(SERVICE_ID_2);
const promise3 = service.get(SERVICE_ID_3);
const promise4 = service.get(SERVICE_ID_4);
service.clearAll('testing');
await assert.isRejected(promise1, 'job cancelled');
await assert.isRejected(promise2, 'job cancelled');
await assert.isRejected(promise3, 'job cancelled');
await assert.isRejected(promise4, 'job cancelled');
});
});
describe('pause', () => {
it('pauses the queue', async () => {
let runCount = 0;
const getProfileWithIncrement = () => {
runCount += 1;
return Promise.resolve();
};
const service = new ProfileService(getProfileWithIncrement);
// Queued and immediately started due to concurrency = 3
drop(service.get(SERVICE_ID_1));
drop(service.get(SERVICE_ID_2));
drop(service.get(SERVICE_ID_3));
// Queued but only run after paused queue restarts
const lastPromise = service.get(SERVICE_ID_4);
const pausePromise = service.pause(5);
assert.strictEqual(runCount, 3, 'as pause starts');
await pausePromise;
await lastPromise;
assert.strictEqual(runCount, 4, 'after last promise');
});
});
describe('get', () => {
it('throws if we are currently paused', async () => {
let runCount = 0;
const getProfileWithIncrement = () => {
runCount += 1;
return Promise.resolve();
};
const service = new ProfileService(getProfileWithIncrement);
const pausePromise = service.pause(5);
// None of these are even queued
const promise1 = service.get(SERVICE_ID_1);
const promise2 = service.get(SERVICE_ID_2);
const promise3 = service.get(SERVICE_ID_3);
const promise4 = service.get(SERVICE_ID_4);
await assert.isRejected(promise1, 'paused queue');
await assert.isRejected(promise2, 'paused queue');
await assert.isRejected(promise3, 'paused queue');
await assert.isRejected(promise4, 'paused queue');
await pausePromise;
assert.strictEqual(runCount, 0);
});
for (const code of [413, 429] as const) {
it(`clears all outstanding jobs if we get a ${code}, then pauses`, async () => {
let runCount = 0;
const getProfileWhichThrows = async () => {
runCount += 1;
const error = new HTTPError(`fake ${code}`, {
code,
headers: {
'retry-after': '1',
},
});
throw error;
};
const service = new ProfileService(getProfileWhichThrows);
// Queued and immediately started due to concurrency = 3
const promise1 = service.get(SERVICE_ID_1);
const promise2 = service.get(SERVICE_ID_2);
const promise3 = service.get(SERVICE_ID_3);
// Never started, but queued
const promise4 = service.get(SERVICE_ID_4);
assert.strictEqual(runCount, 3, 'before await');
await assert.isRejected(promise1, `fake ${code}`);
// Never queued
const promise5 = service.get(SERVICE_ID_5);
await assert.isRejected(promise2, 'job cancelled');
await assert.isRejected(promise3, 'job cancelled');
await assert.isRejected(promise4, 'job cancelled');
await assert.isRejected(promise5, 'paused queue');
assert.strictEqual(runCount, 3, 'after await');
});
}
it('clears all outstanding jobs if we get a -1', async () => {
let runCount = 0;
const getProfileWhichThrows = async () => {
runCount += 1;
const error = new HTTPError('fake -1', {
code: -1,
headers: {},
});
throw error;
};
const service = new ProfileService(getProfileWhichThrows);
// Queued and immediately started due to concurrency = 3
const promise1 = service.get(SERVICE_ID_1);
const promise2 = service.get(SERVICE_ID_2);
const promise3 = service.get(SERVICE_ID_3);
// Never started, but queued
const promise4 = service.get(SERVICE_ID_4);
assert.strictEqual(runCount, 3, 'before await');
await assert.isRejected(promise1, 'fake -1');
// Queued, because we aren't pausing
const promise5 = service.get(SERVICE_ID_5);
await assert.isRejected(promise2, 'job cancelled');
await assert.isRejected(promise3, 'job cancelled');
await assert.isRejected(promise4, 'job cancelled');
await assert.isRejected(promise5, 'fake -1');
assert.strictEqual(runCount, 4, 'after await');
});
});
});