Merge remote-tracking branch 'upstream/master' into ELECTRON-249

This commit is contained in:
kiranniranjan 2018-01-17 15:24:27 +05:30
commit fdb397d343
12 changed files with 789 additions and 54 deletions

View File

@ -182,7 +182,7 @@
threadIdObj = JSON.parse(threadIdEl.value);
}
let _has = has.value || null;
search.searchQuery(queryEl.value, senderIdObj, threadIdObj, _has, startDate, endDate, limitEl.value, offsetEl.value, 0).then(function (result) {
search.searchQuery(queryEl.value, senderIdObj, threadIdObj, _has, startDate, endDate, parseInt(limitEl.value, 10), parseInt(offsetEl.value, 10), 0).then(function (result) {
if (result.messages.length < 1) {
resultsEl.innerHTML = "No results found"
}
@ -220,7 +220,6 @@
});
sendMessage.addEventListener('click', function () {
search.deleteRealTimeFolder();
if (realTimeIndexing.value !== "") {
let message = realTimeIndexing.value;
search.batchRealTimeIndexing(JSON.parse(message));

View File

@ -10,6 +10,9 @@ let Transform = stream.Transform;
let util = require('util');
let crypto = require('crypto');
const log = require('../log.js');
const logLevels = require('../enums/logLevels.js');
let KEY_LENGTH = 32; // bytes
let GCM_NONCE_LENGTH = 12; //bytes
let GCM_MAC_LENGTH = 16; //bytes
@ -151,7 +154,8 @@ DecryptionStream.prototype._transform = function(chunk, enc, cb) {
DecryptionStream.prototype._flush = function(cb) {
let mac = pullOutMac(this._cipherTextChunks);
if (!mac) {
return this.emit('error', new Error('Decryption failed: bad cipher text.'));
log.send(logLevels.ERROR, 'Crypto: Decryption failed: bad cipher text.');
return cb();
}
this._decipher.setAuthTag(mac);
let decrypted = this._cipherTextChunks.map(function(item) {
@ -160,6 +164,7 @@ DecryptionStream.prototype._flush = function(cb) {
try {
this._decipher.final();
} catch (e) {
log.send(logLevels.ERROR, 'Crypto: Decryption failed: ' + e);
return cb();
}
decrypted.forEach(function(item) {

View File

@ -18,12 +18,13 @@ class Crypto {
* @param key
*/
constructor(userId, key) {
this.indexDataFolder = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}_${searchConfig.INDEX_VERSION}`;
this.permanentIndexName = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}_${searchConfig.INDEX_VERSION}`;
this.dump = DUMP_PATH;
this.indexDataFolder = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}`;
this.permanentIndexName = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}`;
this.key = key;
this.encryptedIndex = `${DUMP_PATH}/${this.permanentIndexName}.enc`;
this.dataFolder = searchConfig.FOLDERS_CONSTANTS.INDEX_PATH;
this.lz4Temp = `${DUMP_PATH}/${this.permanentIndexName}${searchConfig.TAR_LZ4_EXT}`;
this.decryptedTemp = `${DUMP_PATH}/decrypted${searchConfig.TAR_LZ4_EXT}`;
}
/**
@ -51,22 +52,37 @@ class Crypto {
if (response && response.stderr) {
log.send(logLevels.WARN, 'Crypto: Child process stderr while compression, ' + response.stderr);
}
const input = fs.createReadStream(`${this.dump}/${this.permanentIndexName}${searchConfig.TAR_LZ4_EXT}`);
const input = fs.createReadStream(this.lz4Temp);
const outputEncryption = fs.createWriteStream(this.encryptedIndex);
let config = {
key: key
};
const encrypt = crypto.encrypt(config);
let encrypt;
try {
encrypt = crypto.encrypt(config);
} catch (e) {
log.send(logLevels.ERROR, 'Error encrypting : ' + e);
if (fs.existsSync(this.lz4Temp)) {
fs.unlinkSync(this.lz4Temp);
}
reject();
return;
}
let encryptionProcess = input.pipe(encrypt).pipe(outputEncryption);
encryptionProcess.on('finish', (err) => {
if (err) {
log.send(logLevels.ERROR, 'Crypto: Error while encrypting the compressed file: ' + err);
if (fs.existsSync(this.lz4Temp)) {
fs.unlinkSync(this.lz4Temp);
}
reject(new Error(err));
return;
}
fs.unlinkSync(`${this.dump}/${this.permanentIndexName}${searchConfig.TAR_LZ4_EXT}`);
if (fs.existsSync(this.lz4Temp)) {
fs.unlinkSync(this.lz4Temp);
}
resolve('Success');
});
});
@ -88,23 +104,33 @@ class Crypto {
}
const input = fs.createReadStream(this.encryptedIndex);
const output = fs.createWriteStream(`${this.dump}/decrypted${searchConfig.TAR_LZ4_EXT}`);
const output = fs.createWriteStream(this.decryptedTemp);
let config = {
key: this.key
};
const decrypt = crypto.decrypt(config);
let decrypt;
try {
decrypt = crypto.decrypt(config);
} catch (e) {
log.send(logLevels.ERROR, 'Error decrypting : ' + e);
if (fs.existsSync(this.decryptedTemp)) {
fs.unlinkSync(this.decryptedTemp);
}
reject();
return;
}
let decryptionProcess = input.pipe(decrypt).pipe(output);
decryptionProcess.on('finish', () => {
if (!fs.existsSync(`${this.dump}/decrypted${searchConfig.TAR_LZ4_EXT}`)){
if (!fs.existsSync(this.decryptedTemp)){
log.send(logLevels.ERROR, 'decrypted.tar.lz4 file not found');
reject();
return;
}
lz4.deCompression(`${this.dump}/decrypted${searchConfig.TAR_LZ4_EXT}`,(error, response) => {
lz4.deCompression(this.decryptedTemp,(error, response) => {
if (error) {
log.send(logLevels.ERROR, 'Crypto: Error while deCompression, ' + error);
// no return, need to unlink if error
@ -113,7 +139,7 @@ class Crypto {
if (response && response.stderr) {
log.send(logLevels.WARN, 'Crypto: Child process stderr while deCompression, ' + response.stderr);
}
fs.unlink(`${this.dump}/decrypted${searchConfig.TAR_LZ4_EXT}`, () => {
fs.unlink(this.decryptedTemp, () => {
resolve('success');
});
})

View File

@ -369,8 +369,17 @@ function createAPI() {
});
}
// reset the badge count whenever an user refreshes the electron client
function resetBadgeCount() {
local.ipcRenderer.send(apiName, {
cmd: apiCmds.setBadgeCount,
count: 0
});
}
window.addEventListener('offline', updateOnlineStatus, false);
window.addEventListener('online', updateOnlineStatus, false);
window.addEventListener('beforeunload', resetBadgeCount, false);
updateOnlineStatus();
}

View File

@ -6,7 +6,7 @@ let makeBoundTimedCollector = function(isIndexing, timeout, callback) {
return function (...args) {
if (!timer){
timer = setTimeout(function(){
if (!isIndexing) {
if (!isIndexing()) {
flush(getQueue());
}
}, timeout);
@ -24,7 +24,9 @@ let makeBoundTimedCollector = function(isIndexing, timeout, callback) {
clearTimeout(timer);
timer = null;
resetQueue();
callback(JSON.stringify(queue));
if (queue) {
callback(JSON.stringify(queue));
}
}
function getQueue(){

View File

@ -31,7 +31,7 @@ class Search {
this.isInitialized = false;
this.userId = userId;
this.key = key;
this.indexFolderName = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${this.userId}_${searchConfig.INDEX_VERSION}`;
this.indexFolderName = `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${this.userId}`;
this.dataFolder = searchConfig.FOLDERS_CONSTANTS.INDEX_PATH;
this.realTimeIndex = searchConfig.FOLDERS_CONSTANTS.TEMP_REAL_TIME_INDEX;
this.batchIndex = searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER;
@ -92,7 +92,7 @@ class Search {
return new Promise((resolve, reject) => {
if (!messages) {
log.send(logLevels.ERROR, 'Batch Indexing: Messages not provided');
reject(new Error('Batch Indexing: Messages is required'));
reject(new Error('Batch Indexing: Messages are required'));
return;
}
@ -115,6 +115,12 @@ class Search {
return;
}
if (!fs.existsSync(this.dataFolder)) {
log.send(logLevels.ERROR, 'User index folder not found');
reject(new Error('User index folder not found'));
return;
}
const indexId = randomString.generate(searchConfig.BATCH_RANDOM_INDEX_PATH_LENGTH);
libSymphonySearch.symSECreatePartialIndexAsync(this.batchIndex, indexId, messages, (err, res) => {
if (err) {
@ -133,6 +139,13 @@ class Search {
*/
mergeIndexBatches() {
return new Promise((resolve, reject) => {
if (!fs.existsSync(this.dataFolder)) {
log.send(logLevels.ERROR, 'User index folder not found');
reject(new Error('User index folder not found'));
return;
}
libSymphonySearch.symSEMergePartialIndexAsync(this.indexFolderName, this.batchIndex, (err, res) => {
if (err) {
log.send(logLevels.ERROR, 'Error merging the index ->' + err);
@ -169,10 +182,6 @@ class Search {
* @param message
*/
realTimeIndexing(message) {
if (!message) {
log.send(logLevels.ERROR, 'RealTime Indexing: Messages not provided');
return new Error('RealTime Indexing: Messages is required');
}
try {
let msg = JSON.parse(message);
@ -182,12 +191,17 @@ class Search {
}
} catch(e) {
log.send(logLevels.ERROR, 'RealTime Indexing: parse error -> ' + e);
return (new Error(e));
throw (new Error(e));
}
if (!this.isInitialized) {
log.send(logLevels.ERROR, 'Library not initialized');
return new Error('Library not initialized');
throw new Error('Library not initialized');
}
if (!fs.existsSync(this.dataFolder)) {
log.send(logLevels.ERROR, 'User index folder not found');
throw new Error('User index folder not found');
}
this.isRealTimeIndexing = true;
@ -195,7 +209,7 @@ class Search {
this.isRealTimeIndexing = false;
if (err) {
log.send(logLevels.ERROR, 'RealTime Indexing: error -> ' + err);
return new Error(err);
throw new Error(err);
}
return result;
});
@ -268,7 +282,7 @@ class Search {
if (!fs.existsSync(this.indexFolderName) || !fs.existsSync(this.realTimeIndex)) {
log.send(logLevels.ERROR, 'Index folder does not exist.');
reject('Index folder does not exist.');
reject(new Error('Index folder does not exist.'));
return;
}
@ -296,15 +310,15 @@ class Search {
}
}
if (!_limit && _limit === "" && typeof _limit !== 'number' && Math.round(_limit) !== _limit) {
if (!_limit || _limit === "" || typeof _limit !== 'number' || Math.round(_limit) !== _limit) {
_limit = 25;
}
if (!_offset && _offset === "" && typeof _offset !== 'number' && Math.round(_offset) !== _offset) {
if (!_offset || _offset === "" || typeof _offset !== 'number' || Math.round(_offset) !== _offset) {
_offset = 0
}
if (!_sortOrder && _sortOrder === "" && typeof _sortOrder !== 'number' && Math.round(_sortOrder) !== _sortOrder) {
if (!_sortOrder || _sortOrder === "" || typeof _sortOrder !== 'number' || Math.round(_sortOrder) !== _sortOrder) {
_sortOrder = searchConfig.SORT_BY_SCORE;
}
@ -327,13 +341,13 @@ class Search {
return new Promise((resolve, reject) => {
if (!this.isInitialized) {
log.send(logLevels.ERROR, 'Library not initialized');
reject('Not initialized');
reject(new Error('Not initialized'));
return;
}
if (!fs.existsSync(this.indexFolderName)) {
log.send(logLevels.ERROR, 'Index folder does not exist.');
reject('Index folder does not exist.');
reject(new Error('Index folder does not exist.'));
return;
}

View File

@ -51,7 +51,6 @@ const searchConfig = {
REAL_TIME_INDEXING_TIME: 60000,
MINIMUM_DATE: '0000000000000',
MAXIMUM_DATE: '9999999999999',
INDEX_VERSION: 'v1',
SORT_BY_SCORE: 0,
BATCH_RANDOM_INDEX_PATH_LENGTH: 20,
LIBRARY_CONSTANTS: libraryPaths,

View File

@ -23,14 +23,7 @@ class SearchUtils {
if (!isMac) {
this.path = this.path.substring(0, 2);
}
checkDiskSpace(this.path, function (error, res) {
if (error) {
return reject(new Error(error));
}
return resolve(res >= searchConfig.MINIMUM_DISK_SPACE);
});
checkDiskSpace(this.path, resolve, reject);
});
}
@ -120,7 +113,13 @@ function createUser(userId, oldConfig) {
function createUserConfigFile(userId, data) {
let createStream = fs.createWriteStream(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE);
if (data) {
createStream.write(`{"${userId}": ${JSON.stringify(data)}}`);
let jsonData;
try {
jsonData = JSON.stringify(data);
createStream.write(`{"${userId}": ${jsonData}}`);
} catch (e) {
createStream.write(`{"${userId}": {}}`);
}
} else {
createStream.write(`{"${userId}": {}}`);
}
@ -149,7 +148,7 @@ function updateConfig(userId, data, resolve, reject) {
oldConfig = JSON.parse(oldData);
} catch (e) {
createUserConfigFile(userId, data);
return reject('can not parse user config file data: ' + e);
return reject(new Error('can not parse user config file data: ' + e));
}
let newConfig = Object.assign({}, oldConfig);

View File

@ -1,42 +1,43 @@
const { exec } = require('child_process');
const { isMac } = require('../../utils/misc');
const searchConfig = require('../searchConfig.js');
function checkDiskSpace(path, callback) {
function checkDiskSpace(path, resolve, reject) {
if (!path) {
return "Please provide path"
reject(new Error("Please provide path"));
return;
}
if (isMac) {
exec("df -k '" + path.replace(/'/g,"'\\''") + "'", (error, stdout, stderr) => {
if (error) {
if (stderr.indexOf("No such file or directory") !== -1) {
return callback("No such file or directory : " + error)
return reject(new Error("No such file or directory : " + error))
}
return callback("Error : " + error)
return reject(new Error("Error : " + error));
}
let data = stdout.trim().split("\n");
let disk_info_str = data[data.length - 1].replace( /[\s\n\r]+/g,' ');
let freeSpace = disk_info_str.split(' ');
return callback(null, freeSpace[3] * 1024);
let space = freeSpace[3] * 1024;
return resolve(space >= searchConfig.MINIMUM_DISK_SPACE);
});
} else {
exec(`fsutil volume diskfree ${path}`, (error, stdout, stderr) => {
if (error) {
if (stderr.indexOf("No such file or directory") !== -1) {
return callback("No such file or directory : " + error)
return reject(new Error("No such file or directory : " + error));
}
return callback("Error : " + error)
return reject(new Error("Error : " + error));
}
let data = stdout.trim().split("\n");
let disk_info_str = data[data.length - 1].split(':');
return callback(null, disk_info_str[1]);
return resolve(disk_info_str[1] >= searchConfig.MINIMUM_DISK_SPACE);
});
}
return null;
}
module.exports = {

View File

@ -311,16 +311,18 @@ function doCreateMainWindow(initialUrl, initialBounds) {
// open external links in default browser - a tag with href='_blank' or window.open
mainWindow.webContents.on('new-window', function (event, newWinUrl,
frameName, disposition, newWinOptions) {
let newWinParsedUrl = getParsedUrl(newWinUrl);
let mainWinParsedUrl = getParsedUrl(url);
let newWinHost = newWinParsedUrl && newWinParsedUrl.host;
let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host;
let emptyUrlString = 'about:blank';
// only allow window.open to succeed is if coming from same hsot,
// otherwise open in default browser.
if (disposition === 'new-window' && newWinHost === mainWinHost) {
if (disposition === 'new-window' && ((newWinHost === mainWinHost) || newWinUrl === emptyUrlString)) {
// handle: window.open
if (!frameName) {

527
tests/Search.test.js Normal file
View File

@ -0,0 +1,527 @@
const childProcess = require('child_process');
const path = require('path');
const fs = require('fs');
let executionPath = null;
let userConfigDir = null;
let searchConfig;
let SearchApi;
jest.mock('electron', function() {
return {
app: {
getPath: mockedGetPath
}
}
});
function mockedGetPath(type) {
if (type === 'exe') {
return executionPath;
}
if (type === 'userData') {
return userConfigDir
}
return '';
}
describe('Tests for Search', function() {
let userId;
let key;
let dataFolderPath;
let realTimeIndexPath;
let tempBatchPath;
let currentDate = new Date().getTime();
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll(function (done) {
childProcess.exec(`npm rebuild --target=${process.version} --build-from-source`, function(err) {
userId = 12345678910112;
key = 'jjjehdnctsjyieoalskcjdhsnahsadndfnusdfsdfsd=';
executionPath = path.join(__dirname, 'library');
userConfigDir = path.join(__dirname, '..');
searchConfig = require('../js/search/searchConfig.js');
const { Search } = require('../js/search/search.js');
SearchApi = new Search(userId, key);
realTimeIndexPath = path.join(userConfigDir, 'data', 'temp_realtime_index');
tempBatchPath = path.join(userConfigDir, 'data', 'temp_batch_indexes');
dataFolderPath = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', 'data');
if (fs.existsSync(dataFolderPath)) {
fs.unlinkSync(dataFolderPath)
}
done();
});
});
afterAll(function (done) {
setTimeout(function () {
deleteIndexFolders(dataFolderPath);
let root = path.join(searchConfig.FOLDERS_CONSTANTS.EXEC_PATH, '..', `${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}.enc`);
if (fs.existsSync(root)) {
fs.unlinkSync(root);
}
done();
}, 3000);
});
function deleteIndexFolders(location) {
if (fs.existsSync(location)) {
fs.readdirSync(location).forEach(function(file) {
let curPath = location + "/" + file;
if (fs.lstatSync(curPath).isDirectory()) {
deleteIndexFolders(curPath);
} else {
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(location);
}
}
describe('Search Initial checks', function() {
it('should be initialized', function (done) {
setTimeout(function () {
expect(SearchApi.isInitialized).toBe(true);
expect(SearchApi.indexFolderName).toBe(`${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME_PATH}_${userId}`);
expect(SearchApi.dataFolder).toBe(searchConfig.FOLDERS_CONSTANTS.INDEX_PATH);
expect(SearchApi.realTimeIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_REAL_TIME_INDEX);
expect(SearchApi.batchIndex).toBe(searchConfig.FOLDERS_CONSTANTS.TEMP_BATCH_INDEX_FOLDER);
expect(SearchApi.messageData).toEqual([]);
expect(SearchApi.isRealTimeIndexing).toBe(false);
done();
}, 3000)
});
it('should isLibInit to true', function () {
let init = SearchApi.isLibInit();
expect(init).toEqual(true);
});
it('should isLibInit to false', function () {
SearchApi.isInitialized = false;
let init = SearchApi.isLibInit();
expect(init).toEqual(false);
SearchApi.isInitialized = true;
});
it('should exist index folder', function() {
expect(fs.existsSync(path.join(userConfigDir, 'data', 'search_index_12345678910112'))).toBe(true);
expect(fs.existsSync(realTimeIndexPath)).toBe(true);
});
it('should not exist index folder', function() {
expect(fs.existsSync(tempBatchPath)).toBe(false);
});
});
describe('Batch indexing process tests', function () {
it('should index in a batch', function (done) {
let messages = [{
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "it works"
}, {
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "it works"
}, {
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "it works"
}];
const indexBatch = jest.spyOn(SearchApi, 'indexBatch');
SearchApi.indexBatch(JSON.stringify(messages)).then(function () {
expect(fs.existsSync(tempBatchPath)).toBe(true);
expect(indexBatch).toHaveBeenCalledWith(JSON.stringify(messages));
done();
});
});
it('should not batch index', function (done) {
const indexBatch = jest.spyOn(SearchApi, 'indexBatch');
SearchApi.indexBatch().catch(function (err) {
expect(indexBatch).toHaveBeenCalled();
expect(err).toBeTruthy();
done();
});
});
it('should not batch index invalid object', function (done) {
const indexBatch = jest.spyOn(SearchApi, 'indexBatch');
SearchApi.indexBatch('message').catch(function (err) {
expect(err).toBeTruthy();
expect(indexBatch).toHaveBeenCalledWith('message');
done();
});
});
it('should not batch index parse error', function (done) {
let message = {
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "it works"
};
const indexBatch = jest.spyOn(SearchApi, 'indexBatch');
SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) {
expect(err).toBeTruthy();
expect(indexBatch).toHaveBeenCalled();
done();
});
});
it('should not batch index isInitialized is false', function (done) {
SearchApi.isInitialized = false;
let message = [ {
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "it fails"
} ];
const indexBatch = jest.spyOn(SearchApi, 'indexBatch');
SearchApi.indexBatch(JSON.stringify(message)).catch(function (err) {
expect(err).toBeTruthy();
expect(indexBatch).toHaveBeenCalledWith(JSON.stringify(message));
SearchApi.isInitialized = true;
done();
});
});
it('should match messages length after batch indexing', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(0);
expect(searchQuery).toHaveBeenCalled();
done()
});
});
it('should merge batch index to user index', function (done) {
const mergeIndexBatches = jest.spyOn(SearchApi, 'mergeIndexBatches');
SearchApi.mergeIndexBatches().then(function () {
expect(fs.existsSync(tempBatchPath)).toBe(false);
expect(mergeIndexBatches).toHaveBeenCalled();
done();
});
});
it('should match messages length after batch indexing', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('it works', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(3);
expect(searchQuery).toHaveBeenCalled();
done();
});
});
});
describe('RealTime indexing process', function () {
it('should index realTime message', function () {
let message = [{
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "realtime working"
}];
const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing');
SearchApi.batchRealTimeIndexing(message);
expect(batchRealTimeIndexing).toHaveBeenCalled();
});
it('should match message length', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('realtime working', ["71811853189212"], ["Au8O2xKHyX1LtE6zW019GX///rZYegAtdA=="], '', undefined, undefined, 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(3);
expect(fs.existsSync(realTimeIndexPath)).toBe(true);
expect(searchQuery).toHaveBeenCalled();
done();
})
});
it('should not index realTime message', function (done) {
let message = [{
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "isRealTimeIndexing"
}];
const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing');
const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing');
SearchApi.isRealTimeIndexing = true;
expect(SearchApi.checkIsRealTimeIndexing()).toBe(true);
SearchApi.batchRealTimeIndexing(message);
expect(batchRealTimeIndexing).toHaveBeenCalled();
expect(realTimeIndexing).not.toBeCalled();
setTimeout(function () {
SearchApi.searchQuery('isRealTimeIndexing', [], [], '', undefined, undefined, 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(0);
expect(fs.existsSync(realTimeIndexPath)).toBe(true);
done();
});
}, 6000)
});
it('should not call the real-time index', function () {
let message = [{
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "isRealTimeIndexing"
}];
const batchRealTimeIndexing = jest.spyOn(SearchApi, 'batchRealTimeIndexing');
const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing');
SearchApi.isRealTimeIndexing = true;
SearchApi.batchRealTimeIndexing(message);
expect(batchRealTimeIndexing).toHaveBeenCalled();
expect(realTimeIndexing).not.toBeCalled();
});
it('should not realTime index invalid object', function () {
let message = [{
messageId: "Jc+4K8RtPxHJfyuDQU9atX///qN3KHYXdA==",
threadId: "Au8O2xKHyX1LtE6zW019GX///rZYegAtdA==",
ingestionDate: currentDate.toString(),
senderId: "71811853189212",
chatType: "CHATROOM",
isPublic: "false",
sendingApp: "lc",
text: "isRealTimeIndexing"
}];
const realTimeIndexing = jest.spyOn(SearchApi, 'realTimeIndexing');
expect(function () {
SearchApi.realTimeIndexing('message')
}).toThrow();
expect(function () {
SearchApi.realTimeIndexing()
}).toThrow();
SearchApi.isInitialized = false;
expect(function () {
SearchApi.realTimeIndexing(JSON.stringify(message))
}).toThrow(new Error('Library not initialized'));
SearchApi.isInitialized = true;
expect(realTimeIndexing).toHaveBeenCalled();
expect(realTimeIndexing).toHaveBeenCalledTimes(3);
});
it('should return realTime bool', function () {
const checkIsRealTimeIndexing = jest.spyOn(SearchApi, 'checkIsRealTimeIndexing');
SearchApi.isRealTimeIndexing = true;
expect(SearchApi.checkIsRealTimeIndexing()).toBe(true);
SearchApi.isRealTimeIndexing = false;
expect(SearchApi.checkIsRealTimeIndexing()).toBe(false);
expect(checkIsRealTimeIndexing).toHaveBeenCalled();
expect(checkIsRealTimeIndexing).toHaveBeenCalledTimes(2);
});
it('should delete realtime index', function () {
const deleteRealTimeFolder = jest.spyOn(SearchApi, 'deleteRealTimeFolder');
SearchApi.deleteRealTimeFolder();
expect(fs.existsSync(realTimeIndexPath)).toBe(true);
expect(deleteRealTimeFolder).toHaveBeenCalled();
});
});
describe('Test for encryption of the index', function () {
it('should encrypt user index', function (done) {
const encryptIndex = jest.spyOn(SearchApi, 'encryptIndex');
SearchApi.encryptIndex(key);
expect(encryptIndex).toHaveBeenCalled();
done();
});
it('should exist encrypted file', function (done) {
setTimeout(function () {
expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112.enc'))).toBe(true);
expect(fs.existsSync(path.join(userConfigDir, 'search_index_12345678910112.tar.lz4'))).toBe(false);
done();
}, 3000);
});
});
describe('Test for latest timestamp', function () {
it('should get the latest timestamp', function (done) {
const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp');
SearchApi.getLatestMessageTimestamp().then(function (res) {
expect(res).toEqual(currentDate.toString());
expect(getLatestMessageTimestamp).toHaveBeenCalled();
done();
});
});
it('should not get the latest timestamp', function (done) {
const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp');
SearchApi.isInitialized = false;
SearchApi.getLatestMessageTimestamp().catch(function (err) {
expect(err).toEqual(new Error('Not initialized'));
expect(getLatestMessageTimestamp).toHaveBeenCalled();
SearchApi.isInitialized = true;
done();
});
});
it('should not get the latest timestamp', function (done) {
SearchApi.indexFolderName = '';
const getLatestMessageTimestamp = jest.spyOn(SearchApi, 'getLatestMessageTimestamp');
SearchApi.getLatestMessageTimestamp().catch(function (err) {
expect(err).toEqual(new Error('Index folder does not exist.'));
SearchApi.indexFolderName = `${dataFolderPath}/${searchConfig.FOLDERS_CONSTANTS.PREFIX_NAME}_${userId}`;
expect(getLatestMessageTimestamp).toHaveBeenCalled();
expect(getLatestMessageTimestamp).toHaveBeenCalledTimes(3);
done();
});
});
});
describe('Test to decrypt the index', function () {
it('should decrypt the index', function () {
deleteIndexFolders(dataFolderPath);
const decryptAndInit = jest.spyOn(SearchApi, 'decryptAndInit');
SearchApi.decryptAndInit();
expect(decryptAndInit).toHaveBeenCalled();
});
it('should get message from the decrypted index', function (done) {
setTimeout(function () {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
let endTime = new Date().getTime();
let startTime = new Date().getTime() - (4 * 31 * 24 * 60 * 60 * 1000);
SearchApi.searchQuery('it works', [], [], '', startTime.toString(), endTime.toString(), '0', 0.2, 0.1).then(function (res) {
expect(res.messages.length).toEqual(3);
expect(searchQuery).toHaveBeenCalled();
done()
});
}, 3000)
});
});
describe('Test for search functions', function () {
it('should search fail isInitialized is false', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.isInitialized = false;
SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) {
expect(err).toEqual(new Error('Library not initialized'));
expect(searchQuery).toHaveBeenCalled();
SearchApi.isInitialized = true;
done();
});
});
it('should filter search limit ', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('works', [], [], '', '', '', 2, 0, 0).then(function (res) {
expect(res.messages.length).toBe(2);
expect(searchQuery).toHaveBeenCalledTimes(7);
expect(searchQuery).toHaveBeenCalled();
done();
});
});
it('should search fails index folder not fund', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
deleteIndexFolders(dataFolderPath);
SearchApi.searchQuery('it works', [], [], '', '', '', 25, 0, 0).catch(function (err) {
expect(err).toEqual(new Error('Index folder does not exist.'));
expect(searchQuery).toHaveBeenCalledTimes(8);
expect(searchQuery).toHaveBeenCalled();
SearchApi = undefined;
const { Search } = require('../js/search/search.js');
SearchApi = new Search(userId, key);
done();
});
});
it('should search fails query is undefined', function (done) {
setTimeout(function () {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
expect(SearchApi.isInitialized).toBe(true);
SearchApi.searchQuery(undefined, [], [], '', '', '', 25, 0, 0).catch(function (err) {
expect(err).toEqual(new Error('Search query error'));
expect(searchQuery).toHaveBeenCalled();
done();
});
}, 3000);
});
it('should search for hashtag', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('#123 "testing"', [], [], 'attachment', '', '', 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(0);
expect(searchQuery).toHaveBeenCalled();
done();
});
});
it('should search for pdf', function (done) {
const searchQuery = jest.spyOn(SearchApi, 'searchQuery');
SearchApi.searchQuery('', [], [], 'pdf', '', '', 25, 0, 0).then(function (res) {
expect(res.messages.length).toEqual(0);
expect(searchQuery).toHaveBeenCalled();
expect(searchQuery).toHaveBeenCalledTimes(3);
done();
});
});
});
});

152
tests/SearchUtils.test.js Normal file
View File

@ -0,0 +1,152 @@
const fs = require('fs');
const path = require('path');
let executionPath = null;
let userConfigDir = null;
let SearchUtilsAPI;
let searchConfig;
jest.mock('electron', function() {
return {
app: {
getPath: mockedGetPath
}
}
});
function mockedGetPath(type) {
switch (type) {
case 'exe':
return executionPath;
case 'userData':
return userConfigDir;
default:
return ''
}
}
describe('Tests for Search Utils', function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000;
beforeAll(function (done) {
executionPath = path.join(__dirname, 'library');
userConfigDir = path.join(__dirname, '..');
searchConfig = require('../js/search/searchConfig.js');
const { SearchUtils } = require('../js/search/searchUtils.js');
SearchUtilsAPI = new SearchUtils();
SearchUtilsAPI.path = userConfigDir;
if (fs.existsSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE)) {
fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE);
}
done();
});
afterAll(function (done) {
fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE);
done();
});
describe('Tests for checking disk space', function () {
it('should return free space', function (done) {
const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace');
SearchUtilsAPI.checkFreeSpace().then(function () {
expect(checkFreeSpace).toHaveBeenCalled();
done();
});
});
it('should return error', function (done) {
const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace');
SearchUtilsAPI.path = undefined;
SearchUtilsAPI.checkFreeSpace().catch(function (err) {
expect(err).toEqual(new Error("Please provide path"));
expect(checkFreeSpace).toHaveBeenCalled();
done();
});
});
it('should return error invalid path', function (done) {
const checkFreeSpace = jest.spyOn(SearchUtilsAPI, 'checkFreeSpace');
SearchUtilsAPI.path = './tp';
SearchUtilsAPI.checkFreeSpace().catch(function (err) {
expect(checkFreeSpace).toHaveBeenCalled();
expect(err).toEqual(err);
done();
});
});
});
describe('Test for search users config', function () {
it('should return null for new user config', function (done) {
SearchUtilsAPI.getSearchUserConfig(1234567891011).then(function (res) {
expect(res).toEqual(null);
done();
});
});
it('should exist users config file', function (done) {
setTimeout(function () {
expect(fs.existsSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE)).toEqual(true);
done();
}, 2000)
});
it('should exist users config file', function (done) {
setTimeout(function () {
SearchUtilsAPI.getSearchUserConfig(1234567891011).then(function (res) {
expect(res).toEqual({});
done();
});
}, 3000)
});
it('should update user config file', function (done) {
let data = {
rotationId: 0,
version: 1,
language: 'en'
};
SearchUtilsAPI.updateUserConfig(1234567891011, data).then(function (res) {
expect(res).toEqual(data);
done();
})
});
it('should modify user config file', function (done) {
let data = {
rotationId: 1,
version: 1,
language: 'en'
};
SearchUtilsAPI.updateUserConfig(1234567891011, data).then(function (res) {
expect(res.rotationId).toEqual(1);
done();
})
});
it('should create user if not exist', function (done) {
SearchUtilsAPI.getSearchUserConfig(2234567891011).catch(function (err) {
expect(err).toEqual(null);
done();
})
});
it('should create file on update', function (done) {
fs.unlinkSync(searchConfig.FOLDERS_CONSTANTS.USER_CONFIG_FILE);
let data = {
rotationId: 0,
version: 2,
language: 'en'
};
SearchUtilsAPI.updateUserConfig(2234567891011, data).catch(function (err) {
expect(err).toEqual(null);
done();
})
});
});
});