When incoming message should've been sealed sender, reply with profile key
This commit is contained in:
parent
18c86898d1
commit
8ef14e6f39
10 changed files with 384 additions and 38 deletions
178
ts/test-both/services/ourProfileKey_test.ts
Normal file
178
ts/test-both/services/ourProfileKey_test.ts
Normal file
|
@ -0,0 +1,178 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { assert } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { noop } from 'lodash';
|
||||
import { sleep } from '../../util/sleep';
|
||||
|
||||
import { OurProfileKeyService } from '../../services/ourProfileKey';
|
||||
|
||||
describe('"our profile key" service', () => {
|
||||
const createFakeStorage = () => ({
|
||||
get: sinon.stub(),
|
||||
put: sinon.stub().resolves(),
|
||||
remove: sinon.stub().resolves(),
|
||||
onready: sinon.stub().callsArg(0),
|
||||
});
|
||||
|
||||
describe('get', () => {
|
||||
it("fetches the key from storage if it's there", async () => {
|
||||
const fakeProfileKey = new ArrayBuffer(2);
|
||||
const fakeStorage = createFakeStorage();
|
||||
fakeStorage.get.withArgs('profileKey').returns(fakeProfileKey);
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
|
||||
assert.strictEqual(await service.get(), fakeProfileKey);
|
||||
});
|
||||
|
||||
it('resolves with undefined if the key is not in storage', async () => {
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(createFakeStorage());
|
||||
|
||||
assert.isUndefined(await service.get());
|
||||
});
|
||||
|
||||
it("doesn't grab the profile key from storage until storage is ready", async () => {
|
||||
let onReadyCallback = noop;
|
||||
const fakeStorage = {
|
||||
...createFakeStorage(),
|
||||
get: sinon.stub().returns(new ArrayBuffer(2)),
|
||||
onready: sinon.stub().callsFake(callback => {
|
||||
onReadyCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
|
||||
const getPromise = service.get();
|
||||
|
||||
// We want to make sure this isn't called even after a tick of the event loop.
|
||||
await sleep(1);
|
||||
sinon.assert.notCalled(fakeStorage.get);
|
||||
|
||||
onReadyCallback();
|
||||
|
||||
await getPromise;
|
||||
sinon.assert.calledOnce(fakeStorage.get);
|
||||
});
|
||||
|
||||
it("doesn't grab the profile key until all blocking promises are ready", async () => {
|
||||
const fakeStorage = createFakeStorage();
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
|
||||
let resolve1 = noop;
|
||||
service.blockGetWithPromise(
|
||||
new Promise<void>(resolve => {
|
||||
resolve1 = resolve;
|
||||
})
|
||||
);
|
||||
|
||||
let reject2 = noop;
|
||||
service.blockGetWithPromise(
|
||||
new Promise<void>((_resolve, reject) => {
|
||||
reject2 = reject;
|
||||
})
|
||||
);
|
||||
|
||||
let reject3 = noop;
|
||||
service.blockGetWithPromise(
|
||||
new Promise<void>((_resolve, reject) => {
|
||||
reject3 = reject;
|
||||
})
|
||||
);
|
||||
|
||||
const getPromise = service.get();
|
||||
|
||||
resolve1();
|
||||
await sleep(1);
|
||||
sinon.assert.notCalled(fakeStorage.get);
|
||||
|
||||
reject2(new Error('uh oh'));
|
||||
await sleep(1);
|
||||
sinon.assert.notCalled(fakeStorage.get);
|
||||
|
||||
reject3(new Error('oh no'));
|
||||
|
||||
await getPromise;
|
||||
|
||||
sinon.assert.calledOnce(fakeStorage.get);
|
||||
});
|
||||
|
||||
it("if there are blocking promises, doesn't grab the profile key from storage more than once (in other words, subsequent calls piggyback)", async () => {
|
||||
const fakeStorage = createFakeStorage();
|
||||
fakeStorage.get.returns(new ArrayBuffer(2));
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
|
||||
let resolve = noop;
|
||||
service.blockGetWithPromise(
|
||||
new Promise<void>(innerResolve => {
|
||||
resolve = innerResolve;
|
||||
})
|
||||
);
|
||||
|
||||
const getPromises = [service.get(), service.get(), service.get()];
|
||||
resolve();
|
||||
const results = await Promise.all(getPromises);
|
||||
assert(new Set(results).size === 1, 'All results should be the same');
|
||||
|
||||
sinon.assert.calledOnce(fakeStorage.get);
|
||||
});
|
||||
|
||||
it('removes all of the blocking promises after waiting for them once', async () => {
|
||||
const fakeStorage = createFakeStorage();
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
|
||||
let resolve = noop;
|
||||
service.blockGetWithPromise(
|
||||
new Promise<void>(innerResolve => {
|
||||
resolve = innerResolve;
|
||||
})
|
||||
);
|
||||
|
||||
const getPromise = service.get();
|
||||
|
||||
sinon.assert.notCalled(fakeStorage.get);
|
||||
resolve();
|
||||
await getPromise;
|
||||
sinon.assert.calledOnce(fakeStorage.get);
|
||||
|
||||
await service.get();
|
||||
sinon.assert.calledTwice(fakeStorage.get);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set', () => {
|
||||
it('updates the key in storage', async () => {
|
||||
const fakeProfileKey = new ArrayBuffer(2);
|
||||
const fakeStorage = createFakeStorage();
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
await service.set(fakeProfileKey);
|
||||
|
||||
sinon.assert.calledOnce(fakeStorage.put);
|
||||
sinon.assert.calledWith(fakeStorage.put, 'profileKey', fakeProfileKey);
|
||||
});
|
||||
|
||||
it('clears the key in storage', async () => {
|
||||
const fakeStorage = createFakeStorage();
|
||||
|
||||
const service = new OurProfileKeyService();
|
||||
service.initialize(fakeStorage);
|
||||
await service.set(undefined);
|
||||
|
||||
sinon.assert.calledOnce(fakeStorage.remove);
|
||||
sinon.assert.calledWith(fakeStorage.remove, 'profileKey');
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue