2015-08-03 16:25:52 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
|
|
* Administration. All rights reserved.
|
|
|
|
*
|
|
|
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
|
|
* "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.
|
|
|
|
*
|
|
|
|
* Open MCT Web includes source code licensed under additional open source
|
|
|
|
* 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.
|
|
|
|
*****************************************************************************/
|
2015-10-20 22:31:33 +00:00
|
|
|
/*global define,describe,it,expect,beforeEach,jasmine,Promise,spyOn,waitsFor,
|
|
|
|
runs*/
|
2015-08-03 16:25:52 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
"use strict";
|
|
|
|
|
|
|
|
describe('GenericSearchProvider', function () {
|
|
|
|
var $q,
|
|
|
|
$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,
|
|
|
|
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']
|
|
|
|
);
|
|
|
|
modelService.getModels.andReturn(Promise.resolve(models));
|
|
|
|
workerService = jasmine.createSpyObj(
|
|
|
|
'workerService',
|
|
|
|
['run']
|
|
|
|
);
|
|
|
|
worker = jasmine.createSpyObj(
|
|
|
|
'worker',
|
|
|
|
[
|
|
|
|
'postMessage',
|
|
|
|
'addEventListener'
|
|
|
|
]
|
|
|
|
);
|
|
|
|
workerService.run.andReturn(worker);
|
|
|
|
topic = jasmine.createSpy('topic');
|
|
|
|
mutationTopic = jasmine.createSpyObj(
|
|
|
|
'mutationTopic',
|
|
|
|
['listen']
|
|
|
|
);
|
|
|
|
topic.andReturn(mutationTopic);
|
|
|
|
ROOTS = [
|
|
|
|
'mine'
|
|
|
|
];
|
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,
|
|
|
|
ROOTS
|
|
|
|
);
|
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));
|
|
|
|
});
|
|
|
|
|
2015-10-23 19:14:46 +00:00
|
|
|
it('reschedules indexing when mutation occurs', function () {
|
|
|
|
var mockDomainObject =
|
|
|
|
jasmine.createSpyObj('domainObj', ['getId']);
|
|
|
|
mockDomainObject.getId.andReturn("some-id");
|
|
|
|
mutationTopic.listen.mostRecentCall.args[0](mockDomainObject);
|
|
|
|
expect(provider.scheduleForIndexing).toHaveBeenCalledWith('some-id');
|
|
|
|
});
|
|
|
|
|
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');
|
|
|
|
worker.addEventListener.mostRecentCall.args[1]('mymessage');
|
|
|
|
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 () {
|
|
|
|
provider.scheduleForIndexing.andCallThrough();
|
|
|
|
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 () {
|
|
|
|
spyOn(provider, 'beginIndexRequest').andCallThrough();
|
|
|
|
provider.pendingRequests = 9;
|
|
|
|
provider.idsToIndex = ['a', 'b', 'c'];
|
|
|
|
provider.MAX_CONCURRENT_REQUESTS = 10;
|
|
|
|
provider.keepIndexing();
|
|
|
|
expect(provider.beginIndexRequest).toHaveBeenCalled();
|
|
|
|
expect(provider.beginIndexRequest.calls.length).toBe(1);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('calls beginIndexRequest for all ids to index', function () {
|
|
|
|
spyOn(provider, 'beginIndexRequest').andCallThrough();
|
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();
|
2015-10-21 14:39:59 +00:00
|
|
|
expect(provider.beginIndexRequest.calls.length).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',
|
|
|
|
model = {composition: ['abc', 'def']};
|
2015-10-01 00:23:52 +00:00
|
|
|
|
2015-10-20 20:12:04 +00:00
|
|
|
provider.index(id, model);
|
|
|
|
expect(provider.scheduleForIndexing)
|
|
|
|
.toHaveBeenCalledWith('abc');
|
|
|
|
expect(provider.scheduleForIndexing)
|
|
|
|
.toHaveBeenCalledWith('def');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
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);
|
|
|
|
waitsFor(function () {
|
|
|
|
return provider.pendingRequests === 0;
|
|
|
|
});
|
|
|
|
runs(function () {
|
|
|
|
expect(provider.pendingRequests).toBe(0);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('indexes objects', function () {
|
|
|
|
provider.beginIndexRequest();
|
|
|
|
waitsFor(function () {
|
|
|
|
return provider.pendingRequests === 0;
|
2015-10-20 22:31:33 +00:00
|
|
|
});
|
2015-10-20 20:12:04 +00:00
|
|
|
runs(function () {
|
|
|
|
expect(provider.index)
|
|
|
|
.toHaveBeenCalledWith('abc', models.abc);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
it('can dispatch searches to worker', function () {
|
|
|
|
spyOn(provider, 'makeQueryId').andReturn(428);
|
|
|
|
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: {}};
|
|
|
|
spyOn(provider, 'dispatchSearch').andReturn(303);
|
|
|
|
$q.defer.andReturn(deferred);
|
|
|
|
|
|
|
|
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
|
|
|
|
}]
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(provider.pendingQueries[143]).not.toBeDefined();
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
2015-10-17 00:33:23 +00:00
|
|
|
});
|