Revert pulls
parent
d31c250e2d
commit
dec7aa10c8
@ -1,36 +1,115 @@
|
|||||||
|
import * as Data from '../../../js/modules/data';
|
||||||
import { RawMessage } from '../types/RawMessage';
|
import { RawMessage } from '../types/RawMessage';
|
||||||
import { OutgoingContentMessage } from '../messages/outgoing';
|
import { ChatMessage, ContentMessage } from '../messages/outgoing';
|
||||||
|
import { MessageUtils, PubKey } from '../utils';
|
||||||
|
|
||||||
// TODO: We should be able to import functions straight from the db here without going through the window object
|
// TODO: We should be able to import functions straight from the db here without going through the window object
|
||||||
|
|
||||||
|
|
||||||
|
// This is an abstraction for storing pending messages.
|
||||||
|
// Ideally we want to store pending messages in the database so that
|
||||||
|
// on next launch we can re-send the pending messages, but we don't want
|
||||||
|
// to constantly fetch pending messages from the database.
|
||||||
|
// Thus we have an intermediary cache which will store pending messagesin
|
||||||
|
// memory and sync its state with the database on modification (add or remove).
|
||||||
|
|
||||||
export class PendingMessageCache {
|
export class PendingMessageCache {
|
||||||
private readonly cachedMessages: Array<RawMessage> = [];
|
public cache: Array<RawMessage>;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
// TODO: We should load pending messages from db here
|
// Load pending messages from the database
|
||||||
|
// You must call init() on this class in order to load from DB.
|
||||||
|
// const pendingMessageCache = new PendingMessageCache();
|
||||||
|
// await pendingMessageCache.init()
|
||||||
|
// >> do stuff
|
||||||
|
this.cache = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public async add(device: PubKey, message: ContentMessage): Promise<RawMessage> {
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(device, message);
|
||||||
|
|
||||||
|
// Does it exist in cache already?
|
||||||
|
if(this.find(rawMessage)) {
|
||||||
|
return rawMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cache.push(rawMessage);
|
||||||
|
await this.syncCacheWithDB();
|
||||||
|
|
||||||
|
return rawMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addPendingMessage(
|
public async remove(message: RawMessage): Promise<Array<RawMessage> | undefined> {
|
||||||
device: string,
|
// Should only be called after message is processed
|
||||||
message: OutgoingContentMessage
|
|
||||||
): RawMessage {
|
// Return if message doesn't exist in cache
|
||||||
// TODO: Maybe have a util for converting OutgoingContentMessage to RawMessage?
|
if (!this.find(message)) {
|
||||||
// TODO: Raw message has uuid, how are we going to set that? maybe use a different identifier?
|
return;
|
||||||
// One could be device + timestamp would make a unique identifier
|
}
|
||||||
// TODO: Return previous pending message if it exists
|
|
||||||
return {} as RawMessage;
|
// Remove item from cache and sync with database
|
||||||
|
const updatedCache = this.cache.filter(
|
||||||
|
m => m.identifier !== message.identifier
|
||||||
|
);
|
||||||
|
this.cache = updatedCache;
|
||||||
|
await this.syncCacheWithDB();
|
||||||
|
|
||||||
|
return updatedCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public removePendingMessage(message: RawMessage) {
|
public find(message: RawMessage): RawMessage | undefined {
|
||||||
// TODO: implement
|
// Find a message in the cache
|
||||||
|
return this.cache.find(
|
||||||
|
m => m.device === message.device && m.timestamp === message.timestamp
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPendingDevices(): Array<String> {
|
public getForDevice(device: PubKey): Array<RawMessage> {
|
||||||
// TODO: this should return all devices which have pending messages
|
return this.cache.filter(m => m.device === device.key);
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPendingMessages(device: string): Array<RawMessage> {
|
public async clear() {
|
||||||
return [];
|
// Clears the cache and syncs to DB
|
||||||
|
this.cache = [];
|
||||||
|
await this.syncCacheWithDB();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public getDevices(): Array<PubKey> {
|
||||||
|
// Gets all devices with pending messages
|
||||||
|
const pubkeys = [...new Set(this.cache.map(m => m.device))];
|
||||||
|
|
||||||
|
return pubkeys.map(d => PubKey.from(d));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async init() {
|
||||||
|
const messages = await this.getFromStorage();
|
||||||
|
this.cache = messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getFromStorage(): Promise<Array<RawMessage>> {
|
||||||
|
// tslint:disable-next-line: no-backbone-get-set-outside-model
|
||||||
|
const pendingMessagesData = await Data.getItemById('pendingMessages');
|
||||||
|
const pendingMessagesJSON = pendingMessagesData
|
||||||
|
? String(pendingMessagesData.value)
|
||||||
|
: '';
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-unnecessary-local-variable
|
||||||
|
const encodedPendingMessages = pendingMessagesJSON
|
||||||
|
? JSON.parse(pendingMessagesJSON)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
// Set pubkey from string to PubKey.from()
|
||||||
|
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// Build up Uint8Array from painTextBuffer in JSON
|
||||||
|
return encodedPendingMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async syncCacheWithDB() {
|
||||||
|
// Only call when adding / removing from cache.
|
||||||
|
const encodedPendingMessages = JSON.stringify(this.cache) || '';
|
||||||
|
await Data.createOrUpdateItem({id: 'pendingMessages', value: encodedPendingMessages});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
import { RawMessage } from '../types/RawMessage';
|
||||||
|
import { ContentMessage } from '../messages/outgoing';
|
||||||
|
import { EncryptionType } from '../types/EncryptionType';
|
||||||
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
|
|
||||||
|
function toRawMessage(device: PubKey, message: ContentMessage): RawMessage {
|
||||||
|
const ttl = message.ttl();
|
||||||
|
const timestamp = message.timestamp;
|
||||||
|
const plainTextBuffer = message.plainTextBuffer();
|
||||||
|
|
||||||
|
// Get EncryptionType depending on message type.
|
||||||
|
// let encryption: EncryptionType;
|
||||||
|
|
||||||
|
// switch (message.constructor.name) {
|
||||||
|
// case MessageType.Chat:
|
||||||
|
// encryption = EncryptionType.Signal;
|
||||||
|
// break;
|
||||||
|
// case MessageType.SessionReset:
|
||||||
|
// encryption = EncryptionType
|
||||||
|
// }
|
||||||
|
|
||||||
|
// export enum EncryptionType {
|
||||||
|
// Signal,
|
||||||
|
// SessionReset,
|
||||||
|
// MediumGroup,
|
||||||
|
// }
|
||||||
|
|
||||||
|
// tslint:disable-next-line: no-unnecessary-local-variable
|
||||||
|
const rawMessage: RawMessage = {
|
||||||
|
identifier: message.identifier,
|
||||||
|
plainTextBuffer,
|
||||||
|
timestamp,
|
||||||
|
device: device.key,
|
||||||
|
ttl,
|
||||||
|
encryption: EncryptionType.Signal,
|
||||||
|
};
|
||||||
|
|
||||||
|
return rawMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export enum PubKeyType {
|
||||||
|
Primary = 'priamry',
|
||||||
|
Secondary = 'secondary',
|
||||||
|
Group = 'group',
|
||||||
|
}
|
||||||
|
|
||||||
|
export class PubKey {
|
||||||
|
private static readonly regex: string = '^0[0-9a-fA-F]{65}$';
|
||||||
|
public readonly key: string;
|
||||||
|
public type?: PubKeyType;
|
||||||
|
|
||||||
|
constructor(pubkeyString: string, type?: PubKeyType) {
|
||||||
|
PubKey.validate(pubkeyString);
|
||||||
|
this.key = pubkeyString;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static from(pubkeyString: string): PubKey {
|
||||||
|
// Returns a new instance if the pubkey is valid
|
||||||
|
if (PubKey.validate(pubkeyString)) {
|
||||||
|
return new PubKey(pubkeyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid pubkey format');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static validate(pubkeyString: string): boolean {
|
||||||
|
if (pubkeyString.match(PubKey.regex)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Invalid pubkey format');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static generate(): PubKey {
|
||||||
|
// Generates a mock pubkey for testing
|
||||||
|
const PUBKEY_LEN = 66;
|
||||||
|
const numBytes = PUBKEY_LEN / 2;
|
||||||
|
const hexBuffer = crypto.randomBytes(numBytes).toString('hex');
|
||||||
|
const pubkeyString = `0${hexBuffer}`.slice(0, PUBKEY_LEN);
|
||||||
|
|
||||||
|
return new PubKey(pubkeyString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions / Tools
|
||||||
|
export const MessageUtils = {
|
||||||
|
toRawMessage,
|
||||||
|
};
|
@ -1,2 +1,3 @@
|
|||||||
export * from './TypedEmitter';
|
export * from './TypedEmitter';
|
||||||
export * from './JobQueue';
|
export * from './JobQueue';
|
||||||
|
export * from './MessageUtils';
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
// import { expect } from 'chai';
|
||||||
|
|
||||||
|
// import { ChatMessage, SessionResetMessage, } from '../../../session/messages/outgoing';
|
||||||
|
// import { TextEncoder } from 'util';
|
||||||
|
// import { MessageUtils } from '../../../session/utils';
|
||||||
|
// import { PendingMessageCache } from '../../../session/sending/PendingMessageCache';
|
||||||
|
|
||||||
|
// describe('PendingMessageCache', () => {
|
||||||
|
// const pendingMessageCache = new PendingMessageCache();
|
||||||
|
|
||||||
|
// let sessionResetMessage: SessionResetMessage;
|
||||||
|
// const preKeyBundle = {
|
||||||
|
// deviceId: 123456,
|
||||||
|
// preKeyId: 654321,
|
||||||
|
// signedKeyId: 111111,
|
||||||
|
// preKey: new TextEncoder().encode('preKey'),
|
||||||
|
// signature: new TextEncoder().encode('signature'),
|
||||||
|
// signedKey: new TextEncoder().encode('signedKey'),
|
||||||
|
// identityKey: new TextEncoder().encode('identityKey'),
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// // queue with session reset message.
|
||||||
|
// // should return undefined
|
||||||
|
// // TOOD: Send me to MESSAGE QUEUE TEST
|
||||||
|
// it('queue session reset message', () => {
|
||||||
|
// const timestamp = Date.now();
|
||||||
|
// sessionResetMessage = new SessionResetMessage({timestamp, preKeyBundle});
|
||||||
|
|
||||||
|
|
||||||
|
// });
|
||||||
|
|
||||||
|
// });
|
@ -0,0 +1,212 @@
|
|||||||
|
// tslint:disable-next-line: no-require-imports no-var-requires
|
||||||
|
const Data = require('../../../../js/modules/data');
|
||||||
|
|
||||||
|
import { expect, assert } from 'chai';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
import uuid from 'uuid';
|
||||||
|
|
||||||
|
import { ChatMessage } from '../../../session/messages/outgoing';
|
||||||
|
import { MessageUtils, PubKey } from '../../../session/utils';
|
||||||
|
import { PendingMessageCache } from '../../../session/sending/PendingMessageCache';
|
||||||
|
import { RawMessage } from '../../../session/types/RawMessage';
|
||||||
|
import { SignalService } from '../../../protobuf';
|
||||||
|
|
||||||
|
describe('PendingMessageCache', () => {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
let pendingMessageCacheStub: PendingMessageCache;
|
||||||
|
|
||||||
|
// tslint:disable-next-line: promise-function-async
|
||||||
|
const wrapInPromise = (value: any) => new Promise(r => {
|
||||||
|
r(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const generateUniqueMessage = (): ChatMessage => {
|
||||||
|
return new ChatMessage({
|
||||||
|
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
|
||||||
|
identifier: uuid(),
|
||||||
|
timestamp: Date.now(),
|
||||||
|
attachments: undefined,
|
||||||
|
quote: undefined,
|
||||||
|
expireTimer: undefined,
|
||||||
|
lokiProfile: undefined,
|
||||||
|
preview: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const mockStorageObject = wrapInPromise([] as Array<RawMessage>);
|
||||||
|
const voidPromise = wrapInPromise(undefined);
|
||||||
|
|
||||||
|
// Stub out methods which touch the database.
|
||||||
|
sandbox.stub(PendingMessageCache.prototype, 'getFromStorage').returns(mockStorageObject);
|
||||||
|
sandbox.stub(PendingMessageCache.prototype, 'syncCacheWithDB').returns(voidPromise);
|
||||||
|
|
||||||
|
// Initialize new stubbed cache
|
||||||
|
pendingMessageCacheStub = new PendingMessageCache();
|
||||||
|
await pendingMessageCacheStub.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('can initialize cache', async () => {
|
||||||
|
const { cache } = pendingMessageCacheStub;
|
||||||
|
|
||||||
|
// We expect the cache to initialise as an empty array
|
||||||
|
expect(cache).to.be.instanceOf(Array);
|
||||||
|
expect(cache).to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('can add to cache', async () => {
|
||||||
|
const device = PubKey.generate();
|
||||||
|
const message = generateUniqueMessage();
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(device, message);
|
||||||
|
|
||||||
|
await pendingMessageCacheStub.add(device, message);
|
||||||
|
|
||||||
|
// Verify that the message is in the cache
|
||||||
|
const finalCache = pendingMessageCacheStub.cache;
|
||||||
|
|
||||||
|
expect(finalCache).to.have.length(1);
|
||||||
|
|
||||||
|
const addedMessage = finalCache[0];
|
||||||
|
expect(addedMessage.device).to.deep.equal(rawMessage.device);
|
||||||
|
expect(addedMessage.timestamp).to.deep.equal(rawMessage.timestamp);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can remove from cache', async () => {
|
||||||
|
const device = PubKey.generate();
|
||||||
|
const message = generateUniqueMessage();
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(device, message);
|
||||||
|
|
||||||
|
await pendingMessageCacheStub.add(device, message);
|
||||||
|
|
||||||
|
const initialCache = pendingMessageCacheStub.cache;
|
||||||
|
expect(initialCache).to.have.length(1);
|
||||||
|
|
||||||
|
// Remove the message
|
||||||
|
await pendingMessageCacheStub.remove(rawMessage);
|
||||||
|
|
||||||
|
const finalCache = pendingMessageCacheStub.cache;
|
||||||
|
|
||||||
|
// Verify that the message was removed
|
||||||
|
expect(finalCache).to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get devices', async () => {
|
||||||
|
const cacheItems = [
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cacheItems.forEach(async item => {
|
||||||
|
await pendingMessageCacheStub.add(item.device, item.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cache } = pendingMessageCacheStub;
|
||||||
|
expect(cache).to.have.length(cacheItems.length);
|
||||||
|
|
||||||
|
// Get list of devices
|
||||||
|
const devicesKeys = cacheItems.map(item => item.device.key);
|
||||||
|
const pulledDevices = pendingMessageCacheStub.getDevices();
|
||||||
|
const pulledDevicesKeys = pulledDevices.map(d => d.key);
|
||||||
|
|
||||||
|
// Verify that device list from cache is equivalent to devices added
|
||||||
|
expect(pulledDevicesKeys).to.have.members(devicesKeys);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can get pending for device', async () => {
|
||||||
|
const cacheItems = [
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cacheItems.forEach(async item => {
|
||||||
|
await pendingMessageCacheStub.add(item.device, item.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialCache = pendingMessageCacheStub.cache;
|
||||||
|
expect(initialCache).to.have.length(cacheItems.length);
|
||||||
|
|
||||||
|
// Get pending for each specific device
|
||||||
|
cacheItems.forEach(item => {
|
||||||
|
const pendingForDevice = pendingMessageCacheStub.getForDevice(item.device);
|
||||||
|
expect(pendingForDevice).to.have.length(1);
|
||||||
|
expect(pendingForDevice[0].device).to.equal(item.device.key);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can find nothing when empty', async () => {
|
||||||
|
const device = PubKey.generate();
|
||||||
|
const message = generateUniqueMessage();
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(device, message);
|
||||||
|
|
||||||
|
const foundMessage = pendingMessageCacheStub.find(rawMessage);
|
||||||
|
expect(foundMessage, 'a message was found in empty cache').to.be.undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can find message in cache', async () => {
|
||||||
|
const device = PubKey.generate();
|
||||||
|
const message = generateUniqueMessage();
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(device, message);
|
||||||
|
|
||||||
|
await pendingMessageCacheStub.add(device, message);
|
||||||
|
|
||||||
|
const finalCache = pendingMessageCacheStub.cache;
|
||||||
|
expect(finalCache).to.have.length(1);
|
||||||
|
|
||||||
|
const foundMessage = pendingMessageCacheStub.find(rawMessage);
|
||||||
|
expect(foundMessage, 'message not found in cache').to.be.ok;
|
||||||
|
foundMessage && expect(foundMessage.device).to.equal(device.key);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can clear cache', async () => {
|
||||||
|
const cacheItems = [
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: PubKey.generate(),
|
||||||
|
message: generateUniqueMessage(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
cacheItems.forEach(async item => {
|
||||||
|
await pendingMessageCacheStub.add(item.device, item.message);
|
||||||
|
});
|
||||||
|
|
||||||
|
const initialCache = pendingMessageCacheStub.cache;
|
||||||
|
expect(initialCache).to.have.length(cacheItems.length);
|
||||||
|
|
||||||
|
// Clear cache
|
||||||
|
await pendingMessageCacheStub.clear();
|
||||||
|
|
||||||
|
const finalCache = pendingMessageCacheStub.cache;
|
||||||
|
expect(finalCache).to.have.length(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,72 @@
|
|||||||
|
import { expect, should } from 'chai';
|
||||||
|
import { SignalService } from '../../../protobuf';
|
||||||
|
import { ChatMessage } from '../../../session/messages/outgoing';
|
||||||
|
import { RawMessage } from '../../../session/types/RawMessage';
|
||||||
|
import { MessageUtils, PubKey, PubKeyType } from '../../../session/utils';
|
||||||
|
|
||||||
|
describe('MessageUtils', () => {
|
||||||
|
it('can convert to RawMessage', () => {
|
||||||
|
// TOOD: MOVE ME TO MESSAGE UTILS TEST
|
||||||
|
const pubkey = "0582fe8822c684999663cc6636148328fbd47c0836814c118af4e326bb4f0e1000";
|
||||||
|
const messageText = "This is some message content";
|
||||||
|
|
||||||
|
const isRawMessage = (object: any): object is RawMessage => {
|
||||||
|
return (
|
||||||
|
'identifier' in object &&
|
||||||
|
'plainTextBuffer' in object &&
|
||||||
|
'timestamp' in object &&
|
||||||
|
'device' in object &&
|
||||||
|
'ttl' in object &&
|
||||||
|
'encryption' in object
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = new ChatMessage({
|
||||||
|
body: messageText,
|
||||||
|
identifier: '1234567890',
|
||||||
|
timestamp: Date.now(),
|
||||||
|
attachments: undefined,
|
||||||
|
quote: undefined,
|
||||||
|
expireTimer: undefined,
|
||||||
|
lokiProfile: undefined,
|
||||||
|
preview: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Explicitly check that it's a RawMessage
|
||||||
|
const rawMessage = MessageUtils.toRawMessage(pubkey, message);
|
||||||
|
expect(isRawMessage(rawMessage)).to.be.equal(true);
|
||||||
|
|
||||||
|
// console.log('[vince] isRawMessage(rawMessage):', isRawMessage(rawMessage));
|
||||||
|
|
||||||
|
// Check plaintext
|
||||||
|
const plainText = message.plainTextBuffer();
|
||||||
|
const decoded = SignalService.Content.decode(plainText);
|
||||||
|
expect(decoded.dataMessage?.body).to.be.equal(messageText);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pubkeys
|
||||||
|
it('can create new valid pubkey', () => {
|
||||||
|
const validPubkey = '0582fe8822c684999663cc6636148328fbd47c0836814c118af4e326bb4f0e1000';
|
||||||
|
should().not.Throw(() => new PubKey(validPubkey), Error);
|
||||||
|
|
||||||
|
const pubkey = new PubKey(validPubkey);
|
||||||
|
expect(pubkey instanceof PubKey).to.be.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid pubkey should throw error', () => {
|
||||||
|
const invalidPubkey = 'Lorem Ipsum';
|
||||||
|
|
||||||
|
should().Throw(() => new PubKey(invalidPubkey), Error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can set pubkey type', () => {
|
||||||
|
const validPubkey = '0582fe8822c684999663cc6636148328fbd47c0836814c118af4e326bb4f0e1000';
|
||||||
|
const pubkeyType = PubKeyType.Primary;
|
||||||
|
|
||||||
|
should().not.Throw(() => new PubKey(validPubkey, pubkeyType), Error);
|
||||||
|
|
||||||
|
const pubkey = new PubKey(validPubkey, pubkeyType);
|
||||||
|
expect(pubkey.type).to.be.equal(PubKeyType.Primary);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,27 @@
|
|||||||
|
import { SignalProtocolAddress } from '../../../window/types/libsignal-protocol';
|
||||||
|
|
||||||
|
export class SignalProtocolAddressStub extends SignalProtocolAddress {
|
||||||
|
private readonly hexEncodedPublicKey: string;
|
||||||
|
private readonly deviceId: number;
|
||||||
|
constructor(hexEncodedPublicKey: string, deviceId: number) {
|
||||||
|
super(hexEncodedPublicKey, deviceId);
|
||||||
|
this.hexEncodedPublicKey = hexEncodedPublicKey;
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tslint:disable-next-line: function-name
|
||||||
|
public static fromString(encodedAddress: string): SignalProtocolAddressStub {
|
||||||
|
const values = encodedAddress.split('.');
|
||||||
|
|
||||||
|
return new SignalProtocolAddressStub(values[0], Number(values[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getName(): string { return this.hexEncodedPublicKey; }
|
||||||
|
public getDeviceId(): number { return this.deviceId; }
|
||||||
|
|
||||||
|
public equals(other: SignalProtocolAddress): boolean {
|
||||||
|
return other.getName() === this.hexEncodedPublicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString(): string { return this.hexEncodedPublicKey; }
|
||||||
|
}
|
Loading…
Reference in New Issue