signal-desktop/test/storage_test.js
2018-10-31 17:36:51 -07:00

1010 lines
35 KiB
JavaScript

'use strict';
describe('SignalProtocolStore', function() {
var number = '+5558675309';
var store;
var identityKey;
var testKey;
function wrapDeferred(deferred) {
return new Promise(function(resolve, reject) {
return deferred.then(resolve, reject);
});
}
before(function(done) {
store = textsecure.storage.protocol;
identityKey = {
pubKey: libsignal.crypto.getRandomBytes(33),
privKey: libsignal.crypto.getRandomBytes(32),
};
testKey = {
pubKey: libsignal.crypto.getRandomBytes(33),
privKey: libsignal.crypto.getRandomBytes(32),
};
storage.put('registrationId', 1337);
storage.put('identityKey', identityKey);
storage.fetch().then(done, done);
});
describe('getLocalRegistrationId', function() {
it('retrieves my registration id', async function() {
const id = await store.getLocalRegistrationId();
assert.strictEqual(id, 1337);
});
});
describe('getIdentityKeyPair', function() {
it('retrieves my identity key', async function() {
const key = await store.getIdentityKeyPair();
assertEqualArrayBuffers(key.pubKey, identityKey.pubKey);
assertEqualArrayBuffers(key.privKey, identityKey.privKey);
});
});
describe('saveIdentity', function() {
var address = new libsignal.SignalProtocolAddress(number, 1);
var identifier = address.toString();
it('stores identity keys', async function() {
await store.saveIdentity(identifier, testKey.pubKey);
const key = await store.loadIdentityKey(number);
assertEqualArrayBuffers(key, testKey.pubKey);
});
it('allows key changes', async function() {
var newIdentity = libsignal.crypto.getRandomBytes(33);
await store.saveIdentity(identifier, testKey.pubKey);
await store.saveIdentity(identifier, newIdentity);
});
describe('When there is no existing key (first use)', function() {
before(async function() {
await store.removeIdentityKey(number);
await store.saveIdentity(identifier, testKey.pubKey);
});
it('marks the key firstUse', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert(identity.firstUse);
});
it('sets the timestamp', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert(identity.timestamp);
});
it('sets the verified status to DEFAULT', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
});
});
describe('When there is a different existing key (non first use)', function() {
const newIdentity = libsignal.crypto.getRandomBytes(33);
const oldTimestamp = Date.now();
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: identifier,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: oldTimestamp,
nonblockingApproval: false,
verified: store.VerifiedStatus.DEFAULT,
});
await store.saveIdentity(identifier, newIdentity);
});
it('marks the key not firstUse', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert(!identity.firstUse);
});
it('updates the timestamp', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.notEqual(identity.timestamp, oldTimestamp);
});
describe('The previous verified status was DEFAULT', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: oldTimestamp,
nonblockingApproval: false,
verified: store.VerifiedStatus.DEFAULT,
});
await store.saveIdentity(identifier, newIdentity);
});
it('sets the new key to default', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
});
});
describe('The previous verified status was VERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: oldTimestamp,
nonblockingApproval: false,
verified: store.VerifiedStatus.VERIFIED,
});
await store.saveIdentity(identifier, newIdentity);
});
it('sets the new key to unverified', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.UNVERIFIED
);
});
});
describe('The previous verified status was UNVERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: oldTimestamp,
nonblockingApproval: false,
verified: store.VerifiedStatus.UNVERIFIED,
});
await store.saveIdentity(identifier, newIdentity);
});
it('sets the new key to unverified', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.UNVERIFIED
);
});
});
});
describe('When the key has not changed', function() {
var oldTimestamp = Date.now();
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
timestamp: oldTimestamp,
nonblockingApproval: false,
verified: store.VerifiedStatus.DEFAULT,
});
});
describe('If it is marked firstUse', function() {
before(async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
identity.firstUse = true;
await window.Signal.Data.createOrUpdateIdentityKey(identity);
});
it('nothing changes', async function() {
await store.saveIdentity(identifier, testKey.pubKey, true);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert(!identity.nonblockingApproval);
assert.strictEqual(identity.timestamp, oldTimestamp);
});
});
describe('If it is not marked firstUse', function() {
before(async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
identity.firstUse = false;
await window.Signal.Data.createOrUpdateIdentityKey(identity);
});
describe('If nonblocking approval is required', function() {
let now;
before(async function() {
now = Date.now();
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
identity.timestamp = now;
await window.Signal.Data.createOrUpdateIdentityKey(identity);
});
it('sets non-blocking approval', async function() {
await store.saveIdentity(identifier, testKey.pubKey, true);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(identity.nonblockingApproval, true);
assert.strictEqual(identity.timestamp, now);
assert.strictEqual(identity.firstUse, false);
});
});
});
});
});
describe('saveIdentityWithAttributes', function() {
var now;
var validAttributes;
before(async function() {
now = Date.now();
validAttributes = {
publicKey: testKey.pubKey,
firstUse: true,
timestamp: now,
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
};
await store.removeIdentityKey(number);
});
describe('with valid attributes', function() {
before(async function() {
await store.saveIdentityWithAttributes(number, validAttributes);
});
it('publicKey is saved', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
});
it('firstUse is saved', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.firstUse, true);
});
it('timestamp is saved', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.timestamp, now);
});
it('verified is saved', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
});
it('nonblockingApproval is saved', async function() {
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.nonblockingApproval, false);
});
});
describe('with invalid attributes', function() {
var attributes;
beforeEach(function() {
attributes = _.clone(validAttributes);
});
async function testInvalidAttributes() {
try {
await store.saveIdentityWithAttributes(number, attributes);
throw new Error('saveIdentityWithAttributes should have failed');
} catch (error) {
// good. we expect to fail with invalid attributes.
}
}
it('rejects an invalid publicKey', async function() {
attributes.publicKey = 'a string';
await testInvalidAttributes();
});
it('rejects invalid firstUse', async function() {
attributes.firstUse = 0;
await testInvalidAttributes();
});
it('rejects invalid timestamp', async function() {
attributes.timestamp = NaN;
await testInvalidAttributes();
});
it('rejects invalid verified', async function() {
attributes.verified = null;
await testInvalidAttributes();
});
it('rejects invalid nonblockingApproval', async function() {
attributes.nonblockingApproval = 0;
await testInvalidAttributes();
});
});
});
describe('setApproval', function() {
it('sets nonblockingApproval', async function() {
await store.setApproval(number, true);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.nonblockingApproval, true);
});
});
describe('setVerified', function() {
var record;
async function saveRecordDefault() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
nonblockingApproval: false,
});
}
describe('with no public key argument', function() {
before(saveRecordDefault);
it('updates the verified status', async function() {
await store.setVerified(number, store.VerifiedStatus.VERIFIED);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
});
});
describe('with the current public key', function() {
before(saveRecordDefault);
it('updates the verified status', async function() {
await store.setVerified(
number,
store.VerifiedStatus.VERIFIED,
testKey.pubKey
);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
});
});
describe('with a mismatching public key', function() {
var newIdentity = libsignal.crypto.getRandomBytes(33);
before(saveRecordDefault);
it('does not change the record.', async function() {
await store.setVerified(
number,
store.VerifiedStatus.VERIFIED,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
});
});
});
describe('processContactSyncVerificationState', function() {
var record;
var newIdentity = libsignal.crypto.getRandomBytes(33);
var keychangeTriggered;
beforeEach(function() {
keychangeTriggered = 0;
store.bind('keychange', function() {
keychangeTriggered++;
});
});
afterEach(function() {
store.unbind('keychange');
});
describe('when the new verified status is DEFAULT', function() {
describe('when there is no existing record', function() {
before(async function() {
await window.Signal.Data.removeIdentityKeyById(number);
});
it('does nothing', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.DEFAULT,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(number);
if (identity) {
// fetchRecord resolved so there is a record.
// Bad.
throw new Error(
'processContactSyncVerificationState should not save new records'
);
}
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the record exists', function() {
describe('when the existing key is different', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
});
});
it('does not save the new identity (because this is a less secure state)', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.DEFAULT,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.VERIFIED
);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the existing key is the same but VERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
});
});
it('updates the verified status', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.DEFAULT,
testKey.pubKey
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(identity.verified, store.VerifiedStatus.DEFAULT);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the existing key is the same and already DEFAULT', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
nonblockingApproval: false,
});
});
it('does not hang', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.DEFAULT,
testKey.pubKey
);
assert.strictEqual(keychangeTriggered, 0);
});
});
});
});
describe('when the new verified status is UNVERIFIED', function() {
describe('when there is no existing record', function() {
before(async function() {
await window.Signal.Data.removeIdentityKeyById(number);
});
it('saves the new identity and marks it verified', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.UNVERIFIED,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.UNVERIFIED
);
assertEqualArrayBuffers(identity.publicKey, newIdentity);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the record exists', function() {
describe('when the existing key is different', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
});
});
it('saves the new identity and marks it UNVERIFIED', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.UNVERIFIED,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.UNVERIFIED
);
assertEqualArrayBuffers(identity.publicKey, newIdentity);
assert.strictEqual(keychangeTriggered, 1);
});
});
describe('when the key exists and is DEFAULT', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
nonblockingApproval: false,
});
});
it('updates the verified status', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.UNVERIFIED,
testKey.pubKey
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.UNVERIFIED
);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the key exists and is already UNVERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.UNVERIFIED,
nonblockingApproval: false,
});
});
it('does not hang', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.UNVERIFIED,
testKey.pubKey
);
assert.strictEqual(keychangeTriggered, 0);
});
});
});
});
describe('when the new verified status is VERIFIED', function() {
describe('when there is no existing record', function() {
before(async function() {
await window.Signal.Data.removeIdentityKeyById(number);
});
it('saves the new identity and marks it verified', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.VERIFIED,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(number);
assert.strictEqual(identity.verified, store.VerifiedStatus.VERIFIED);
assertEqualArrayBuffers(identity.publicKey, newIdentity);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the record exists', function() {
describe('when the existing key is different', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
});
});
it('saves the new identity and marks it VERIFIED', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.VERIFIED,
newIdentity
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.VERIFIED
);
assertEqualArrayBuffers(identity.publicKey, newIdentity);
assert.strictEqual(keychangeTriggered, 1);
});
});
describe('when the existing key is the same but UNVERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.UNVERIFIED,
nonblockingApproval: false,
});
});
it('saves the identity and marks it verified', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.VERIFIED,
testKey.pubKey
);
const identity = await window.Signal.Data.getIdentityKeyById(
number
);
assert.strictEqual(
identity.verified,
store.VerifiedStatus.VERIFIED
);
assertEqualArrayBuffers(identity.publicKey, testKey.pubKey);
assert.strictEqual(keychangeTriggered, 0);
});
});
describe('when the existing key is the same and already VERIFIED', function() {
before(async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
firstUse: true,
timestamp: Date.now(),
verified: store.VerifiedStatus.VERIFIED,
nonblockingApproval: false,
});
});
it('does not hang', async function() {
await store.processContactSyncVerificationState(
number,
store.VerifiedStatus.VERIFIED,
testKey.pubKey
);
assert.strictEqual(keychangeTriggered, 0);
});
});
});
});
});
describe('isUntrusted', function() {
it('returns false if identity key old enough', async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
timestamp: Date.now() - 10 * 1000 * 60,
verified: store.VerifiedStatus.DEFAULT,
firstUse: false,
nonblockingApproval: false,
});
const untrusted = await store.isUntrusted(number);
assert.strictEqual(untrusted, false);
});
it('returns false if new but nonblockingApproval is true', async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
firstUse: false,
nonblockingApproval: true,
});
const untrusted = await store.isUntrusted(number);
assert.strictEqual(untrusted, false);
});
it('returns false if new but firstUse is true', async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
firstUse: true,
nonblockingApproval: false,
});
const untrusted = await store.isUntrusted(number);
assert.strictEqual(untrusted, false);
});
it('returns true if new, and no flags are set', async function() {
await window.Signal.Data.createOrUpdateIdentityKey({
id: number,
publicKey: testKey.pubKey,
timestamp: Date.now(),
verified: store.VerifiedStatus.DEFAULT,
firstUse: false,
nonblockingApproval: false,
});
const untrusted = await store.isUntrusted(number);
assert.strictEqual(untrusted, true);
});
});
describe('getVerified', function() {
before(async function() {
await store.setVerified(number, store.VerifiedStatus.VERIFIED);
});
it('resolves to the verified status', async function() {
const result = await store.getVerified(number);
assert.strictEqual(result, store.VerifiedStatus.VERIFIED);
});
});
describe('isTrustedIdentity', function() {
const address = new libsignal.SignalProtocolAddress(number, 1);
const identifier = address.toString();
describe('When invalid direction is given', function() {
it('should fail', async function() {
try {
await store.isTrustedIdentity(number, testKey.pubKey);
throw new Error('isTrustedIdentity should have failed');
} catch (error) {
// good
}
});
});
describe('When direction is RECEIVING', function() {
it('always returns true', async function() {
var newIdentity = libsignal.crypto.getRandomBytes(33);
await store.saveIdentity(identifier, testKey.pubKey);
const trusted = await store.isTrustedIdentity(
identifier,
newIdentity,
store.Direction.RECEIVING
);
if (!trusted) {
throw new Error('isTrusted returned false when receiving');
}
});
});
describe('When direction is SENDING', function() {
describe('When there is no existing key (first use)', function() {
before(async function() {
await store.removeIdentityKey(number);
});
it('returns true', async function() {
const newIdentity = libsignal.crypto.getRandomBytes(33);
const trusted = await store.isTrustedIdentity(
identifier,
newIdentity,
store.Direction.SENDING
);
if (!trusted) {
throw new Error('isTrusted returned false on first use');
}
});
});
describe('When there is an existing key', function() {
before(async function() {
await store.saveIdentity(identifier, testKey.pubKey);
});
describe('When the existing key is different', function() {
it('returns false', async function() {
const newIdentity = libsignal.crypto.getRandomBytes(33);
const trusted = await store.isTrustedIdentity(
identifier,
newIdentity,
store.Direction.SENDING
);
if (trusted) {
throw new Error('isTrusted returned true on untrusted key');
}
});
});
describe('When the existing key matches the new key', function() {
const newIdentity = libsignal.crypto.getRandomBytes(33);
before(async function() {
await store.saveIdentity(identifier, newIdentity);
});
it('returns false if keys match but we just received this new identiy', async function() {
const trusted = await store.isTrustedIdentity(
identifier,
newIdentity,
store.Direction.SENDING
);
if (trusted) {
throw new Error('isTrusted returned true on untrusted key');
}
});
it('returns true if we have already approved identity', async function() {
await store.saveIdentity(identifier, newIdentity, true);
const trusted = await store.isTrustedIdentity(
identifier,
newIdentity,
store.Direction.SENDING
);
if (!trusted) {
throw new Error('isTrusted returned false on an approved key');
}
});
});
});
});
});
describe('storePreKey', function() {
it('stores prekeys', async function() {
await store.storePreKey(1, testKey);
const key = await store.loadPreKey(1);
assertEqualArrayBuffers(key.pubKey, testKey.pubKey);
assertEqualArrayBuffers(key.privKey, testKey.privKey);
});
});
describe('removePreKey', function() {
before(async function() {
await store.storePreKey(2, testKey);
});
it('deletes prekeys', async function() {
await store.removePreKey(2, testKey);
const key = await store.loadPreKey(2);
assert.isUndefined(key);
});
});
describe('storeSignedPreKey', function() {
it('stores signed prekeys', async function() {
await store.storeSignedPreKey(3, testKey);
const key = await store.loadSignedPreKey(3);
assertEqualArrayBuffers(key.pubKey, testKey.pubKey);
assertEqualArrayBuffers(key.privKey, testKey.privKey);
});
});
describe('removeSignedPreKey', function() {
before(async function() {
await store.storeSignedPreKey(4, testKey);
});
it('deletes signed prekeys', async function() {
await store.removeSignedPreKey(4, testKey);
const key = await store.loadSignedPreKey(4);
assert.isUndefined(key);
});
});
describe('storeSession', function() {
it('stores sessions', async function() {
const testRecord = 'an opaque string';
await store.storeSession(number + '.1', testRecord);
const record = await store.loadSession(number + '.1');
assert.deepEqual(record, testRecord);
});
});
describe('removeAllSessions', function() {
it('removes all sessions for a number', async function() {
const testRecord = 'an opaque string';
const devices = [1, 2, 3].map(function(deviceId) {
return [number, deviceId].join('.');
});
await Promise.all(
devices.map(async function(encodedNumber) {
await store.storeSession(encodedNumber, testRecord + encodedNumber);
})
);
await store.removeAllSessions(number);
const records = await Promise.all(
devices.map(store.loadSession.bind(store))
);
for (var i in records) {
assert.isUndefined(records[i]);
}
});
});
describe('clearSessionStore', function() {
it('clears the session store', async function() {
const testRecord = 'an opaque string';
await store.storeSession(number + '.1', testRecord);
await store.clearSessionStore();
const record = await store.loadSession(number + '.1');
assert.isUndefined(record);
});
});
describe('getDeviceIds', function() {
it('returns deviceIds for a number', async function() {
const testRecord = 'an opaque string';
const devices = [1, 2, 3].map(function(deviceId) {
return [number, deviceId].join('.');
});
await Promise.all(
devices.map(async function(encodedNumber) {
await store.storeSession(encodedNumber, testRecord + encodedNumber);
})
);
const deviceIds = await store.getDeviceIds(number);
assert.sameMembers(deviceIds, [1, 2, 3]);
});
it('returns empty array for a number with no device ids', async function() {
const deviceIds = await store.getDeviceIds('foo');
assert.sameMembers(deviceIds, []);
});
});
describe('Not yet processed messages', function() {
beforeEach(async function() {
await store.removeAllUnprocessed();
const items = await store.getAllUnprocessed();
assert.strictEqual(items.length, 0);
});
it('adds two and gets them back', async function() {
await Promise.all([
store.addUnprocessed({ id: 2, name: 'second', timestamp: 2 }),
store.addUnprocessed({ id: 3, name: 'third', timestamp: 3 }),
store.addUnprocessed({ id: 1, name: 'first', timestamp: 1 }),
]);
const items = await store.getAllUnprocessed();
assert.strictEqual(items.length, 3);
// they are in the proper order because the collection comparator is 'timestamp'
assert.strictEqual(items[0].name, 'first');
assert.strictEqual(items[1].name, 'second');
assert.strictEqual(items[2].name, 'third');
});
it('saveUnprocessed successfully updates item', async function() {
const id = 1;
await store.addUnprocessed({ id: id, name: 'first', timestamp: 1 });
await store.saveUnprocessed({ id, name: 'updated', timestamp: 1 });
const items = await store.getAllUnprocessed();
assert.strictEqual(items.length, 1);
assert.strictEqual(items[0].name, 'updated');
assert.strictEqual(items[0].timestamp, 1);
});
it('removeUnprocessed successfully deletes item', async function() {
const id = 1;
await store.addUnprocessed({ id: id, name: 'first', timestamp: 1 });
await store.removeUnprocessed(id);
const items = await store.getAllUnprocessed();
assert.strictEqual(items.length, 0);
});
});
});