517 lines
16 KiB
JavaScript
517 lines
16 KiB
JavaScript
|
var _ = require('underscore');
|
||
|
var Backbone = require('backbone');
|
||
|
var AnalysisModel = require('../../../src/analysis/analysis-model.js');
|
||
|
var AnalysisService = require('../../../src/analysis/analysis-service.js');
|
||
|
|
||
|
var fakeCamshaftReference = {
|
||
|
getSourceNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['source1', 'source2'],
|
||
|
'trade-area': ['source'],
|
||
|
'estimated-population': ['source'],
|
||
|
'sql-function': ['source', 'target']
|
||
|
};
|
||
|
return map[analysisType];
|
||
|
},
|
||
|
getParamNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['attribute1', 'attribute2'],
|
||
|
'trade-area': ['kind', 'time'],
|
||
|
'estimated-population': ['columnName']
|
||
|
};
|
||
|
|
||
|
return map[analysisType];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var createFakeEngine = function () {
|
||
|
var engine = new Backbone.Model();
|
||
|
engine.reload = jasmine.createSpy('reload');
|
||
|
return engine;
|
||
|
};
|
||
|
|
||
|
var createFakeAnalysis = function (attrs, engine) {
|
||
|
return new AnalysisModel(attrs, {
|
||
|
engine: engine,
|
||
|
camshaftReference: fakeCamshaftReference
|
||
|
});
|
||
|
};
|
||
|
|
||
|
describe('src/analysis/analysis-model.js', function () {
|
||
|
var engineMock;
|
||
|
|
||
|
beforeEach(function () {
|
||
|
engineMock = createFakeEngine();
|
||
|
this.analysisModel = createFakeAnalysis({
|
||
|
type: 'analysis-type-1',
|
||
|
attribute1: 'value1',
|
||
|
attribute2: 'value2'
|
||
|
}, engineMock);
|
||
|
});
|
||
|
|
||
|
describe('.url', function () {
|
||
|
it('should append the api_key param if present (and not use the authToken)', function () {
|
||
|
this.analysisModel.set({
|
||
|
url: 'http://example.com',
|
||
|
apiKey: 'THE_API_KEY',
|
||
|
authToken: 'THE_AUTH_TOKEN'
|
||
|
});
|
||
|
|
||
|
expect(this.analysisModel.url()).toEqual('http://example.com?api_key=THE_API_KEY');
|
||
|
});
|
||
|
|
||
|
it('should append the auth_token param if present (and not use the authToken)', function () {
|
||
|
this.analysisModel.set({
|
||
|
url: 'http://example.com',
|
||
|
authToken: 'THE_AUTH_TOKEN'
|
||
|
});
|
||
|
|
||
|
expect(this.analysisModel.url()).toEqual('http://example.com?auth_token=THE_AUTH_TOKEN');
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('bindings', function () {
|
||
|
describe('on params change', function () {
|
||
|
it('should reload the map', function () {
|
||
|
this.analysisModel.set({
|
||
|
attribute1: 'newValue1'
|
||
|
});
|
||
|
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
engineMock.reload.calls.reset();
|
||
|
|
||
|
this.analysisModel.set({
|
||
|
attribute2: 'newValue2'
|
||
|
});
|
||
|
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
engineMock.reload.calls.reset();
|
||
|
|
||
|
this.analysisModel.set({
|
||
|
attribute900: 'something'
|
||
|
});
|
||
|
|
||
|
expect(engineMock.reload).not.toHaveBeenCalled();
|
||
|
});
|
||
|
|
||
|
it('should be marked as failed if request to reload the map fails', function () {
|
||
|
this.analysisModel.set({
|
||
|
attribute1: 'newValue1',
|
||
|
status: AnalysisModel.STATUS.READY
|
||
|
});
|
||
|
|
||
|
// Request to the Maps API fails and error callback is invoked...
|
||
|
engineMock.reload.calls.argsFor(0)[0].error('something bad just happened');
|
||
|
|
||
|
expect(this.analysisModel.get('status')).toEqual(AnalysisModel.STATUS.FAILED);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('on type change', function () {
|
||
|
it('should unbind old params and bind new params', function () {
|
||
|
spyOn(this.analysisModel, '_initBinds').and.callThrough();
|
||
|
spyOn(this.analysisModel, 'unbind').and.callThrough();
|
||
|
this.analysisModel.set('type', 'new!');
|
||
|
expect(this.analysisModel.unbind).toHaveBeenCalled();
|
||
|
expect(this.analysisModel._initBinds).toHaveBeenCalled();
|
||
|
});
|
||
|
|
||
|
it('should reload the map', function () {
|
||
|
this.analysisModel.set('type', 'something');
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
});
|
||
|
|
||
|
it('should keep listening type change again', function () {
|
||
|
this.analysisModel.set('type', 'something');
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
engineMock.reload.calls.reset();
|
||
|
this.analysisModel.set('type', 'something else');
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('on status change', function () {
|
||
|
var createAnalysisModelNoStatusNoReferences = function (engine) {
|
||
|
var analysisModel = createFakeAnalysis({ id: 'a0' }, engine);
|
||
|
return analysisModel;
|
||
|
};
|
||
|
|
||
|
var createAnalysisModelNoStatusWithReferences = function (engine) {
|
||
|
var analysisModel = createFakeAnalysis({ id: 'a0' }, engine);
|
||
|
analysisModel.markAsSourceOf(new Backbone.Model());
|
||
|
return analysisModel;
|
||
|
};
|
||
|
|
||
|
var createAnalysisModelWithStatusNoReferences = function (engine) {
|
||
|
var analysisModel = createFakeAnalysis({ id: 'a0', status: 'foo' }, engine);
|
||
|
return analysisModel;
|
||
|
};
|
||
|
|
||
|
var createAnalysisModelWithStatusWithReferences = function (engine) {
|
||
|
var analysisModel = createFakeAnalysis({ id: 'a0', status: 'foo' }, engine);
|
||
|
analysisModel.markAsSourceOf(new Backbone.Model());
|
||
|
return analysisModel;
|
||
|
};
|
||
|
|
||
|
var testCases = [
|
||
|
{
|
||
|
testName: 'analysis with no previous status and no references',
|
||
|
createAnalysisFn: createAnalysisModelNoStatusNoReferences,
|
||
|
expectedVisReloadWhenStatusIn: [] // no relaod is expected
|
||
|
},
|
||
|
{
|
||
|
testName: 'analysis with no previous status and some references',
|
||
|
createAnalysisFn: createAnalysisModelNoStatusWithReferences,
|
||
|
expectedVisReloadWhenStatusIn: [] // no reload is expected
|
||
|
},
|
||
|
{
|
||
|
testName: 'analysis with previous status and no references',
|
||
|
createAnalysisFn: createAnalysisModelWithStatusNoReferences,
|
||
|
expectedVisReloadWhenStatusIn: [] // no reload is expected
|
||
|
},
|
||
|
{
|
||
|
testName: 'analysis with previous status and references',
|
||
|
createAnalysisFn: createAnalysisModelWithStatusWithReferences,
|
||
|
expectedVisReloadWhenStatusIn: [ AnalysisModel.STATUS.READY ]
|
||
|
}
|
||
|
];
|
||
|
|
||
|
_.forEach(testCases, function (testCase) {
|
||
|
var testName = testCase.testName;
|
||
|
var createAnalysisFn = testCase.createAnalysisFn;
|
||
|
var expectedVisReloadWhenStatusIn = testCase.expectedVisReloadWhenStatusIn;
|
||
|
var notExpectedVisReloadWhenStatusIn = [];
|
||
|
|
||
|
describe(testName, function () {
|
||
|
var analysisModel;
|
||
|
var engineMock;
|
||
|
|
||
|
beforeEach(function () {
|
||
|
engineMock = createFakeEngine();
|
||
|
analysisModel = createAnalysisFn(engineMock);
|
||
|
});
|
||
|
|
||
|
_.forEach(AnalysisModel.STATUS, function (status) {
|
||
|
if (expectedVisReloadWhenStatusIn.indexOf(status) < 0) {
|
||
|
notExpectedVisReloadWhenStatusIn.push(status);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
_.each(expectedVisReloadWhenStatusIn, function (status) {
|
||
|
it("should reload the engine if analysis is now '" + status + "'", function () {
|
||
|
expect(engineMock.reload).not.toHaveBeenCalled();
|
||
|
analysisModel.set('status', status);
|
||
|
expect(engineMock.reload).toHaveBeenCalled();
|
||
|
});
|
||
|
}, this);
|
||
|
|
||
|
_.each(notExpectedVisReloadWhenStatusIn, function (status) {
|
||
|
it("should NOT reload the engine if analysis is now '" + status + "'", function () {
|
||
|
expect(engineMock.reload).not.toHaveBeenCalled();
|
||
|
analysisModel.set('status', status);
|
||
|
expect(engineMock.reload).not.toHaveBeenCalled();
|
||
|
});
|
||
|
}, this);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.findAnalysisById', function () {
|
||
|
it('should find a node in the graph', function () {
|
||
|
var fakeCamshaftReference = {
|
||
|
getSourceNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['source1', 'source2'],
|
||
|
'analysis-type-2': [],
|
||
|
'analysis-type-3': ['source3'],
|
||
|
'analysis-type-4': [],
|
||
|
'analysis-type-5': ['source4', 'source5']
|
||
|
};
|
||
|
return map[analysisType];
|
||
|
},
|
||
|
getParamNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['a'],
|
||
|
'analysis-type-2': [],
|
||
|
'analysis-type-3': [],
|
||
|
'analysis-type-4': ['a4'],
|
||
|
'analysis-type-5': []
|
||
|
};
|
||
|
|
||
|
return map[analysisType];
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var analysisService = new AnalysisService({
|
||
|
engine: engineMock,
|
||
|
camshaftReference: fakeCamshaftReference
|
||
|
});
|
||
|
var analysisModel = analysisService.analyse({
|
||
|
id: 'a1',
|
||
|
type: 'analysis-type-1',
|
||
|
params: {
|
||
|
a: 1,
|
||
|
source1: {
|
||
|
id: 'a2',
|
||
|
type: 'analysis-type-2',
|
||
|
params: {
|
||
|
a2: 2
|
||
|
}
|
||
|
},
|
||
|
source2: {
|
||
|
id: 'a3',
|
||
|
type: 'analysis-type-3',
|
||
|
params: {
|
||
|
source3: {
|
||
|
id: 'a5',
|
||
|
type: 'analysis-type-5',
|
||
|
params: {
|
||
|
source4: {
|
||
|
id: 'a4',
|
||
|
type: 'analysis-type-4',
|
||
|
params: {
|
||
|
a4: 4
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
expect(analysisModel.findAnalysisById('a1')).toEqual(analysisModel);
|
||
|
expect(analysisModel.findAnalysisById('a2').get('id')).toEqual('a2');
|
||
|
expect(analysisModel.findAnalysisById('a3').get('id')).toEqual('a3');
|
||
|
expect(analysisModel.findAnalysisById('a5').get('id')).toEqual('a5');
|
||
|
expect(analysisModel.findAnalysisById('b9')).toBeUndefined();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.toJSON', function () {
|
||
|
it('should serialize the graph', function () {
|
||
|
var fakeCamshaftReference = {
|
||
|
getSourceNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['source1', 'source2'],
|
||
|
'analysis-type-2': [],
|
||
|
'analysis-type-3': ['source3'],
|
||
|
'analysis-type-4': [],
|
||
|
'analysis-type-5': ['source4', 'source5']
|
||
|
};
|
||
|
return map[analysisType];
|
||
|
},
|
||
|
getParamNamesForAnalysisType: function (analysisType) {
|
||
|
var map = {
|
||
|
'analysis-type-1': ['a'],
|
||
|
'analysis-type-2': ['a2'],
|
||
|
'analysis-type-3': [],
|
||
|
'analysis-type-4': ['a4'],
|
||
|
'analysis-type-5': []
|
||
|
};
|
||
|
|
||
|
return map[analysisType];
|
||
|
},
|
||
|
isSourceNameOptionalForAnalysisType: function (analysisType, sourceName) {
|
||
|
return (analysisType === 'analysis-type-5' && sourceName === 'source5');
|
||
|
}
|
||
|
};
|
||
|
|
||
|
var analysisService = new AnalysisService({
|
||
|
engine: engineMock,
|
||
|
camshaftReference: fakeCamshaftReference
|
||
|
});
|
||
|
var analysisModel = analysisService.analyse({
|
||
|
id: 'a1',
|
||
|
type: 'analysis-type-1',
|
||
|
params: {
|
||
|
a: 1,
|
||
|
source1: {
|
||
|
id: 'a2',
|
||
|
type: 'analysis-type-2',
|
||
|
params: {
|
||
|
a2: 2
|
||
|
}
|
||
|
},
|
||
|
source2: {
|
||
|
id: 'a3',
|
||
|
type: 'analysis-type-3',
|
||
|
params: {
|
||
|
source3: {
|
||
|
id: 'a4',
|
||
|
type: 'analysis-type-4',
|
||
|
params: {
|
||
|
a4: {
|
||
|
id: 'a5',
|
||
|
type: 'analysis-type-5',
|
||
|
params: {
|
||
|
source4: {
|
||
|
id: 'a6',
|
||
|
type: 'analysis-type-2',
|
||
|
params: {
|
||
|
a2: 2
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
expect(analysisModel.toJSON()).toEqual({
|
||
|
id: 'a1',
|
||
|
type: 'analysis-type-1',
|
||
|
params: {
|
||
|
a: 1,
|
||
|
source1: {
|
||
|
id: 'a2',
|
||
|
type: 'analysis-type-2',
|
||
|
params: {
|
||
|
a2: 2
|
||
|
}
|
||
|
},
|
||
|
source2: {
|
||
|
id: 'a3',
|
||
|
type: 'analysis-type-3',
|
||
|
params: {
|
||
|
source3: {
|
||
|
id: 'a4',
|
||
|
type: 'analysis-type-4',
|
||
|
params: {
|
||
|
a4: {
|
||
|
id: 'a5',
|
||
|
type: 'analysis-type-5',
|
||
|
params: {
|
||
|
source4: {
|
||
|
id: 'a6',
|
||
|
type: 'analysis-type-2',
|
||
|
params: {
|
||
|
a2: 2
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.isDone', function () {
|
||
|
it('should return true if analysis has been calculated', function () {
|
||
|
this.analysisModel.set('status', AnalysisModel.STATUS.READY);
|
||
|
expect(this.analysisModel.isDone()).toEqual(true);
|
||
|
|
||
|
this.analysisModel.set('status', AnalysisModel.STATUS.FAILED);
|
||
|
expect(this.analysisModel.isDone()).toEqual(true);
|
||
|
});
|
||
|
|
||
|
it('should return false if analysis has NOT been calculated', function () {
|
||
|
this.analysisModel.set('status', AnalysisModel.STATUS.PENDING);
|
||
|
expect(this.analysisModel.isDone()).toEqual(false);
|
||
|
|
||
|
this.analysisModel.set('status', AnalysisModel.STATUS.WAITING);
|
||
|
expect(this.analysisModel.isDone()).toEqual(false);
|
||
|
|
||
|
this.analysisModel.set('status', AnalysisModel.STATUS.RUNNING);
|
||
|
expect(this.analysisModel.isDone()).toEqual(false);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.setOk', function () {
|
||
|
it('should unset error attribute', function () {
|
||
|
this.analysisModel.set('error', 'error');
|
||
|
this.analysisModel.setOk();
|
||
|
expect(this.analysisModel.get('error')).toBeUndefined();
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.setError', function () {
|
||
|
it('should set error attribute', function () {
|
||
|
this.analysisModel.setError('wadus');
|
||
|
|
||
|
expect(this.analysisModel.get('error')).toEqual('wadus');
|
||
|
});
|
||
|
|
||
|
it('should set analyis as failed', function () {
|
||
|
this.analysisModel.setError('wadus');
|
||
|
|
||
|
expect(this.analysisModel.get('status')).toEqual(AnalysisModel.STATUS.FAILED);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('.getNodes', function () {
|
||
|
var analysisService;
|
||
|
beforeEach(function () {
|
||
|
analysisService = new AnalysisService({
|
||
|
engine: engineMock,
|
||
|
camshaftReference: fakeCamshaftReference
|
||
|
});
|
||
|
});
|
||
|
it('Should return a list of nodes from an analysis', function () {
|
||
|
var analysis = analysisService.analyse(
|
||
|
{
|
||
|
id: 'a2',
|
||
|
type: 'estimated-population',
|
||
|
params: {
|
||
|
columnName: 'estimated_people',
|
||
|
source: {
|
||
|
id: 'a1',
|
||
|
type: 'trade-area',
|
||
|
params: {
|
||
|
kind: 'walk',
|
||
|
time: 300,
|
||
|
source: {
|
||
|
id: 'a0',
|
||
|
type: 'source',
|
||
|
params: {
|
||
|
query: 'SELECT * FROM subway_stops'
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
);
|
||
|
var actual = analysis.getNodes();
|
||
|
expect(actual.length).toEqual(3);
|
||
|
});
|
||
|
});
|
||
|
|
||
|
describe('references tracking', function () {
|
||
|
it('should allow keeping track of models that reference this object', function () {
|
||
|
var model1 = new Backbone.Model();
|
||
|
var model2 = new Backbone.Model();
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(false);
|
||
|
|
||
|
this.analysisModel.markAsSourceOf(model1);
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(true);
|
||
|
|
||
|
this.analysisModel.markAsSourceOf(model1);
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(true);
|
||
|
|
||
|
this.analysisModel.markAsSourceOf(model2);
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(true);
|
||
|
|
||
|
this.analysisModel.unmarkAsSourceOf(model1);
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(true);
|
||
|
|
||
|
this.analysisModel.unmarkAsSourceOf(model2);
|
||
|
|
||
|
expect(this.analysisModel.isSourceOfAnyModel()).toBe(false);
|
||
|
});
|
||
|
});
|
||
|
});
|