2015-08-03 16:25:52 +00:00
|
|
|
/*****************************************************************************
|
2020-09-14 18:17:31 +00:00
|
|
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
2015-08-03 16:25:52 +00:00
|
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
|
|
* Administration. All rights reserved.
|
|
|
|
*
|
2016-07-12 23:21:58 +00:00
|
|
|
* Open MCT is licensed under the Apache License, Version 2.0 (the
|
2015-08-03 16:25:52 +00:00
|
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
|
|
|
*
|
2016-07-12 23:21:58 +00:00
|
|
|
* Open MCT includes source code licensed under additional open source
|
2015-08-03 16:25:52 +00:00
|
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
|
|
* this source code distribution or the Licensing information page available
|
|
|
|
* at runtime from the About dialog for additional information.
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SearchSpec. Created by shale on 07/31/2015.
|
|
|
|
*/
|
2015-10-17 00:33:23 +00:00
|
|
|
define([
|
|
|
|
"../../src/services/GenericSearchProvider"
|
2015-10-20 20:12:04 +00:00
|
|
|
], function (
|
|
|
|
GenericSearchProvider
|
|
|
|
) {
|
2015-10-17 00:33:23 +00:00
|
|
|
|
2019-10-29 22:49:55 +00:00
|
|
|
xdescribe('GenericSearchProvider', function () {
|
2016-04-08 23:28:35 +00:00
|
|
|
var $q,
|
2015-10-17 00:33:23 +00:00
|
|
|
$log,
|
|
|
|
modelService,
|
2015-10-20 20:12:04 +00:00
|
|
|
models,
|
2015-10-17 00:33:23 +00:00
|
|
|
workerService,
|
2015-10-20 20:12:04 +00:00
|
|
|
worker,
|
2015-10-17 00:33:23 +00:00
|
|
|
topic,
|
2015-10-20 20:12:04 +00:00
|
|
|
mutationTopic,
|
|
|
|
ROOTS,
|
2017-05-18 15:26:42 +00:00
|
|
|
compositionProvider,
|
|
|
|
openmct,
|
2015-10-20 20:12:04 +00:00
|
|
|
provider;
|
2015-10-17 00:33:23 +00:00
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
$q = jasmine.createSpyObj(
|
|
|
|
'$q',
|
|
|
|
['defer']
|
|
|
|
);
|
2015-10-20 20:12:04 +00:00
|
|
|
$log = jasmine.createSpyObj(
|
|
|
|
'$log',
|
|
|
|
['warn']
|
|
|
|
);
|
|
|
|
models = {};
|
|
|
|
modelService = jasmine.createSpyObj(
|
|
|
|
'modelService',
|
|
|
|
['getModels']
|
|
|
|
);
|
2018-06-30 00:32:59 +00:00
|
|
|
modelService.getModels.and.returnValue(Promise.resolve(models));
|
2015-10-20 20:12:04 +00:00
|
|
|
workerService = jasmine.createSpyObj(
|
|
|
|
'workerService',
|
|
|
|
['run']
|
|
|
|
);
|
|
|
|
worker = jasmine.createSpyObj(
|
|
|
|
'worker',
|
|
|
|
[
|
|
|
|
'postMessage',
|
|
|
|
'addEventListener'
|
|
|
|
]
|
|
|
|
);
|
2018-06-30 00:32:59 +00:00
|
|
|
workerService.run.and.returnValue(worker);
|
2015-10-20 20:12:04 +00:00
|
|
|
topic = jasmine.createSpy('topic');
|
|
|
|
mutationTopic = jasmine.createSpyObj(
|
|
|
|
'mutationTopic',
|
|
|
|
['listen']
|
|
|
|
);
|
2018-06-30 00:32:59 +00:00
|
|
|
topic.and.returnValue(mutationTopic);
|
2015-10-20 20:12:04 +00:00
|
|
|
ROOTS = [
|
|
|
|
'mine'
|
|
|
|
];
|
2017-05-18 15:26:42 +00:00
|
|
|
compositionProvider = jasmine.createSpyObj(
|
|
|
|
'compositionProvider',
|
|
|
|
['load', 'appliesTo']
|
|
|
|
);
|
2018-06-30 00:32:59 +00:00
|
|
|
compositionProvider.load.and.callFake(function (domainObject) {
|
2017-05-18 15:26:42 +00:00
|
|
|
return Promise.resolve(domainObject.composition);
|
|
|
|
});
|
2018-06-30 00:32:59 +00:00
|
|
|
compositionProvider.appliesTo.and.callFake(function (domainObject) {
|
2020-07-31 19:11:03 +00:00
|
|
|
return Boolean(domainObject.composition);
|
2017-05-18 15:26:42 +00:00
|
|
|
});
|
|
|
|
openmct = {
|
|
|
|
composition: {
|
|
|
|
registry: [compositionProvider]
|
|
|
|
}
|
|
|
|
};
|
2015-08-03 16:25:52 +00:00
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
spyOn(GenericSearchProvider.prototype, 'scheduleForIndexing');
|
2015-08-03 16:25:52 +00:00
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
provider = new GenericSearchProvider(
|
|
|
|
$q,
|
|
|
|
$log,
|
|
|
|
modelService,
|
|
|
|
workerService,
|
|
|
|
topic,
|
2017-05-18 15:26:42 +00:00
|
|
|
ROOTS,
|
|
|
|
openmct
|
2015-10-20 20:12:04 +00:00
|
|
|
);
|
2015-10-17 00:33:23 +00:00
|
|
|
});
|
2015-10-01 00:23:52 +00:00
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
it('listens for general mutation', function () {
|
|
|
|
expect(topic).toHaveBeenCalledWith('mutation');
|
|
|
|
expect(mutationTopic.listen)
|
|
|
|
.toHaveBeenCalledWith(jasmine.any(Function));
|
|
|
|
});
|
|
|
|
|
2016-03-07 22:42:40 +00:00
|
|
|
it('re-indexes when mutation occurs', function () {
|
2015-10-23 19:14:46 +00:00
|
|
|
var mockDomainObject =
|
2016-03-11 22:54:08 +00:00
|
|
|
jasmine.createSpyObj('domainObj', [
|
|
|
|
'getId',
|
|
|
|
'getModel',
|
|
|
|
'getCapability'
|
|
|
|
]),
|
2016-03-07 22:42:40 +00:00
|
|
|
testModel = { some: 'model' };
|
2018-06-30 00:32:59 +00:00
|
|
|
mockDomainObject.getId.and.returnValue("some-id");
|
|
|
|
mockDomainObject.getModel.and.returnValue(testModel);
|
|
|
|
spyOn(provider, 'index').and.callThrough();
|
|
|
|
mutationTopic.listen.calls.mostRecent().args[0](mockDomainObject);
|
2016-03-07 22:42:40 +00:00
|
|
|
expect(provider.index).toHaveBeenCalledWith('some-id', testModel);
|
2015-10-23 19:14:46 +00:00
|
|
|
});
|
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
it('starts indexing roots', function () {
|
|
|
|
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('mine');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('runs a worker', function () {
|
|
|
|
expect(workerService.run)
|
|
|
|
.toHaveBeenCalledWith('genericSearchWorker');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('listens for messages from worker', function () {
|
|
|
|
expect(worker.addEventListener)
|
|
|
|
.toHaveBeenCalledWith('message', jasmine.any(Function));
|
|
|
|
spyOn(provider, 'onWorkerMessage');
|
2018-06-30 00:32:59 +00:00
|
|
|
worker.addEventListener.calls.mostRecent().args[1]('mymessage');
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.onWorkerMessage).toHaveBeenCalledWith('mymessage');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('has a maximum number of concurrent requests', function () {
|
|
|
|
expect(provider.MAX_CONCURRENT_REQUESTS).toBe(100);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('scheduleForIndexing', function () {
|
|
|
|
beforeEach(function () {
|
2018-06-30 00:32:59 +00:00
|
|
|
provider.scheduleForIndexing.and.callThrough();
|
2015-10-20 20:12:04 +00:00
|
|
|
spyOn(provider, 'keepIndexing');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('tracks ids to index', function () {
|
2015-10-20 22:31:33 +00:00
|
|
|
expect(provider.indexedIds.a).not.toBeDefined();
|
|
|
|
expect(provider.pendingIndex.a).not.toBeDefined();
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.idsToIndex).not.toContain('a');
|
|
|
|
provider.scheduleForIndexing('a');
|
2015-10-20 22:31:33 +00:00
|
|
|
expect(provider.indexedIds.a).toBeDefined();
|
|
|
|
expect(provider.pendingIndex.a).toBeDefined();
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.idsToIndex).toContain('a');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('calls keep indexing', function () {
|
|
|
|
provider.scheduleForIndexing('a');
|
|
|
|
expect(provider.keepIndexing).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('keepIndexing', function () {
|
2015-10-21 14:39:59 +00:00
|
|
|
it('calls beginIndexRequest until at maximum', function () {
|
2018-06-30 00:32:59 +00:00
|
|
|
spyOn(provider, 'beginIndexRequest').and.callThrough();
|
2015-10-21 14:39:59 +00:00
|
|
|
provider.pendingRequests = 9;
|
|
|
|
provider.idsToIndex = ['a', 'b', 'c'];
|
|
|
|
provider.MAX_CONCURRENT_REQUESTS = 10;
|
|
|
|
provider.keepIndexing();
|
|
|
|
expect(provider.beginIndexRequest).toHaveBeenCalled();
|
2018-06-30 00:32:59 +00:00
|
|
|
expect(provider.beginIndexRequest.calls.count()).toBe(1);
|
2015-10-21 14:39:59 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('calls beginIndexRequest for all ids to index', function () {
|
2018-06-30 00:32:59 +00:00
|
|
|
spyOn(provider, 'beginIndexRequest').and.callThrough();
|
2015-10-20 20:12:04 +00:00
|
|
|
provider.pendingRequests = 0;
|
2015-10-21 14:39:59 +00:00
|
|
|
provider.idsToIndex = ['a', 'b', 'c'];
|
2015-10-20 20:12:04 +00:00
|
|
|
provider.MAX_CONCURRENT_REQUESTS = 10;
|
|
|
|
provider.keepIndexing();
|
|
|
|
expect(provider.beginIndexRequest).toHaveBeenCalled();
|
2018-06-30 00:32:59 +00:00
|
|
|
expect(provider.beginIndexRequest.calls.count()).toBe(3);
|
2015-10-20 20:12:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not index when at capacity', function () {
|
|
|
|
spyOn(provider, 'beginIndexRequest');
|
|
|
|
provider.pendingRequests = 10;
|
2015-10-21 14:39:59 +00:00
|
|
|
provider.idsToIndex.push('a');
|
|
|
|
provider.MAX_CONCURRENT_REQUESTS = 10;
|
|
|
|
provider.keepIndexing();
|
|
|
|
expect(provider.beginIndexRequest).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not index when no ids to index', function () {
|
|
|
|
spyOn(provider, 'beginIndexRequest');
|
|
|
|
provider.pendingRequests = 0;
|
2015-10-20 20:12:04 +00:00
|
|
|
provider.MAX_CONCURRENT_REQUESTS = 10;
|
|
|
|
provider.keepIndexing();
|
|
|
|
expect(provider.beginIndexRequest).not.toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('index', function () {
|
|
|
|
it('sends index message to worker', function () {
|
|
|
|
var id = 'anId',
|
|
|
|
model = {};
|
|
|
|
|
|
|
|
provider.index(id, model);
|
|
|
|
expect(worker.postMessage).toHaveBeenCalledWith({
|
|
|
|
request: 'index',
|
|
|
|
id: id,
|
|
|
|
model: model
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('schedules composed ids for indexing', function () {
|
|
|
|
var id = 'anId',
|
2017-05-18 15:26:42 +00:00
|
|
|
model = {composition: ['abc', 'def']},
|
2018-06-30 00:32:59 +00:00
|
|
|
resolve,
|
|
|
|
promise = new Promise(function (r) {
|
|
|
|
resolve = r;
|
|
|
|
});
|
|
|
|
|
|
|
|
provider.scheduleForIndexing.and.callFake(resolve);
|
2015-10-01 00:23:52 +00:00
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
provider.index(id, model);
|
2017-05-18 15:26:42 +00:00
|
|
|
|
|
|
|
expect(compositionProvider.appliesTo).toHaveBeenCalledWith({
|
2020-07-31 19:11:03 +00:00
|
|
|
identifier: {
|
|
|
|
key: 'anId',
|
|
|
|
namespace: ''
|
|
|
|
},
|
2017-05-18 15:26:42 +00:00
|
|
|
composition: [jasmine.any(Object), jasmine.any(Object)]
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(compositionProvider.load).toHaveBeenCalledWith({
|
2020-07-31 19:11:03 +00:00
|
|
|
identifier: {
|
|
|
|
key: 'anId',
|
|
|
|
namespace: ''
|
|
|
|
},
|
2017-05-18 15:26:42 +00:00
|
|
|
composition: [jasmine.any(Object), jasmine.any(Object)]
|
|
|
|
});
|
|
|
|
|
2018-06-30 00:32:59 +00:00
|
|
|
return promise.then(function () {
|
2017-05-18 15:26:42 +00:00
|
|
|
expect(provider.scheduleForIndexing)
|
|
|
|
.toHaveBeenCalledWith('abc');
|
|
|
|
expect(provider.scheduleForIndexing)
|
|
|
|
.toHaveBeenCalledWith('def');
|
2018-06-30 00:32:59 +00:00
|
|
|
|
2017-05-18 15:26:42 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('does not index ROOT, but checks composition', function () {
|
|
|
|
var id = 'ROOT',
|
|
|
|
model = {};
|
|
|
|
|
|
|
|
provider.index(id, model);
|
|
|
|
expect(worker.postMessage).not.toHaveBeenCalled();
|
|
|
|
expect(compositionProvider.appliesTo).toHaveBeenCalledWith({
|
2020-07-31 19:11:03 +00:00
|
|
|
identifier: {
|
|
|
|
key: 'ROOT',
|
|
|
|
namespace: ''
|
|
|
|
}
|
2017-05-18 15:26:42 +00:00
|
|
|
});
|
2015-10-20 20:12:04 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('beginIndexRequest', function () {
|
|
|
|
|
|
|
|
beforeEach(function () {
|
|
|
|
provider.pendingRequests = 0;
|
|
|
|
provider.pendingIds = {'abc': true};
|
|
|
|
provider.idsToIndex = ['abc'];
|
|
|
|
models.abc = {};
|
|
|
|
spyOn(provider, 'index');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('removes items from queue', function () {
|
|
|
|
provider.beginIndexRequest();
|
|
|
|
expect(provider.idsToIndex.length).toBe(0);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('tracks number of pending requests', function () {
|
|
|
|
provider.beginIndexRequest();
|
|
|
|
expect(provider.pendingRequests).toBe(1);
|
2018-06-30 00:32:59 +00:00
|
|
|
|
|
|
|
return waitsFor(function () {
|
2015-10-20 20:12:04 +00:00
|
|
|
return provider.pendingRequests === 0;
|
2018-06-30 00:32:59 +00:00
|
|
|
}).then(function () {
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.pendingRequests).toBe(0);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('indexes objects', function () {
|
|
|
|
provider.beginIndexRequest();
|
2018-06-30 00:32:59 +00:00
|
|
|
|
|
|
|
return waitsFor(function () {
|
2015-10-20 20:12:04 +00:00
|
|
|
return provider.pendingRequests === 0;
|
2018-06-30 00:32:59 +00:00
|
|
|
}).then(function () {
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.index)
|
|
|
|
.toHaveBeenCalledWith('abc', models.abc);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2018-06-30 00:32:59 +00:00
|
|
|
function waitsFor(latchFunction) {
|
|
|
|
return new Promise(function (resolve, reject) {
|
|
|
|
var maxWait = 2000;
|
|
|
|
var start = Date.now();
|
|
|
|
|
|
|
|
checkLatchFunction();
|
|
|
|
|
|
|
|
function checkLatchFunction() {
|
|
|
|
var now = Date.now();
|
|
|
|
var elapsed = now - start;
|
|
|
|
|
|
|
|
if (latchFunction()) {
|
|
|
|
resolve();
|
|
|
|
} else if (elapsed >= maxWait) {
|
|
|
|
reject("Timeout waiting for latch function to be true");
|
|
|
|
} else {
|
|
|
|
setTimeout(checkLatchFunction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2015-10-20 20:12:04 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('can dispatch searches to worker', function () {
|
2018-06-30 00:32:59 +00:00
|
|
|
spyOn(provider, 'makeQueryId').and.returnValue(428);
|
2015-10-20 20:12:04 +00:00
|
|
|
expect(provider.dispatchSearch('searchTerm', 100))
|
|
|
|
.toBe(428);
|
|
|
|
|
|
|
|
expect(worker.postMessage).toHaveBeenCalledWith({
|
|
|
|
request: 'search',
|
|
|
|
input: 'searchTerm',
|
|
|
|
maxResults: 100,
|
|
|
|
queryId: 428
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('can generate queryIds', function () {
|
|
|
|
expect(provider.makeQueryId()).toEqual(jasmine.any(Number));
|
|
|
|
});
|
|
|
|
|
|
|
|
it('can query for terms', function () {
|
|
|
|
var deferred = {promise: {}};
|
2018-06-30 00:32:59 +00:00
|
|
|
spyOn(provider, 'dispatchSearch').and.returnValue(303);
|
|
|
|
$q.defer.and.returnValue(deferred);
|
2015-10-20 20:12:04 +00:00
|
|
|
|
|
|
|
expect(provider.query('someTerm', 100)).toBe(deferred.promise);
|
|
|
|
expect(provider.pendingQueries[303]).toBe(deferred);
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('onWorkerMessage', function () {
|
|
|
|
var pendingQuery;
|
|
|
|
beforeEach(function () {
|
|
|
|
pendingQuery = jasmine.createSpyObj(
|
|
|
|
'pendingQuery',
|
|
|
|
['resolve']
|
|
|
|
);
|
|
|
|
provider.pendingQueries[143] = pendingQuery;
|
|
|
|
});
|
|
|
|
|
|
|
|
it('resolves pending searches', function () {
|
|
|
|
provider.onWorkerMessage({
|
|
|
|
data: {
|
|
|
|
request: 'search',
|
|
|
|
total: 2,
|
|
|
|
results: [
|
|
|
|
{
|
|
|
|
item: {
|
|
|
|
id: 'abc',
|
|
|
|
model: {id: 'abc'}
|
|
|
|
},
|
|
|
|
matchCount: 4
|
|
|
|
},
|
|
|
|
{
|
|
|
|
item: {
|
|
|
|
id: 'def',
|
|
|
|
model: {id: 'def'}
|
|
|
|
},
|
|
|
|
matchCount: 2
|
|
|
|
}
|
|
|
|
],
|
|
|
|
queryId: 143
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(pendingQuery.resolve)
|
|
|
|
.toHaveBeenCalledWith({
|
|
|
|
total: 2,
|
|
|
|
hits: [{
|
|
|
|
id: 'abc',
|
|
|
|
model: {id: 'abc'},
|
|
|
|
score: 4
|
|
|
|
}, {
|
|
|
|
id: 'def',
|
|
|
|
model: {id: 'def'},
|
|
|
|
score: 2
|
|
|
|
}]
|
|
|
|
});
|
|
|
|
|
2016-05-19 18:29:13 +00:00
|
|
|
expect(provider.pendingQueries[143]).not.toBeDefined();
|
2015-10-20 20:12:04 +00:00
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
2015-10-17 00:33:23 +00:00
|
|
|
});
|