test: add missing storage and notification tests

Closes #31
This commit is contained in:
Jovan Krunić
2020-10-23 11:27:59 +02:00
committed by Rainer Killinger
parent fe7dd09d7e
commit 5eefa3c2e1
11 changed files with 2118 additions and 10 deletions

View File

@@ -0,0 +1,442 @@
/*
* Copyright (C) 2020 StApps
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
SCConfigFile,
SCSearchBooleanFilter,
SCSearchFilter,
SCSearchQuery,
SCSearchSort,
SCThingType
} from '@openstapps/core';
import { expect } from 'chai';
import {configFile} from '../../../src/common';
import {
ElasticsearchConfig, ESBooleanFilter, ESDucetSort, ESGeoDistanceFilter,
ESGeoDistanceSort,
ESTermFilter,
ScriptSort
} from '../../../src/storage/elasticsearch/common';
import {buildBooleanFilter, buildFilter, buildQuery, buildSort} from '../../../src/storage/elasticsearch/query';
describe('Query', function () {
describe('buildBooleanFilter', function () {
const booleanFilter: SCSearchBooleanFilter = {
arguments: {
operation: 'and',
filters: [
{
type: 'value',
arguments: {
field: 'type',
value: SCThingType.Catalog
}
},
{
type: 'value',
arguments: {
field: 'type',
value: SCThingType.Building
}
}
]
},
type: 'boolean'
};
const booleanFilters: {[key: string]: SCSearchBooleanFilter} = {
and: booleanFilter,
or: {...booleanFilter, arguments: {...booleanFilter.arguments, operation: 'or'}},
not: {...booleanFilter, arguments: {...booleanFilter.arguments, operation: 'not'}},
};
const expectedEsFilters: Array<ESTermFilter> = [
{
term: {
'type.raw': 'catalog'
}
},
{
term: {
'type.raw': 'building'
}
}
];
it('should create appropriate elasticsearch "and" filter argument', function () {
const {must} = buildBooleanFilter(booleanFilters.and);
expect(must).to.be.eql(expectedEsFilters);
});
it('should create appropriate elasticsearch "or" filter argument', function () {
const {should, minimum_should_match} = buildBooleanFilter(booleanFilters.or);
expect(should).to.be.eql(expectedEsFilters);
expect(minimum_should_match).to.be.equal(1);
});
it('should create appropriate elasticsearch "not" filter argument', function () {
const {must_not} = buildBooleanFilter(booleanFilters.not);
expect(must_not).to.be.eql(expectedEsFilters);
});
});
describe('buildQuery', function () {
const params: SCSearchQuery = {
query: 'mathematics',
from: 30,
size: 5,
sort: [
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'name'
}
},
{
type: 'ducet',
order: 'desc',
arguments:
{
field: 'categories'
}
},
],
filter: {
type: 'value',
arguments: {
field: 'type',
value: SCThingType.AcademicEvent
}
}
};
let esConfig: ElasticsearchConfig = {
name: 'elasticsearch',
version: '123',
query: {
minMatch: '75%',
queryType: 'dis_max',
matchBoosting: 1.3,
fuzziness: 'AUTO',
cutoffFrequency: 0.0,
tieBreaker: 0,
},
};
const query = {
minMatch: '75%',
queryType: 'dis_max',
matchBoosting: 1.3,
fuzziness: 'AUTO',
cutoffFrequency: 0.0,
tieBreaker: 0,
}
const config: SCConfigFile = {
...configFile
};
beforeEach(function () {
esConfig = {
name: 'elasticsearch',
version: '123'
};
});
// TODO: check parts of received elasticsearch query for each test case
it('should build query that includes sorting when query is undefined', function () {
expect(buildQuery(params, config, esConfig)).to.be.an('object');
});
it('should build query that includes sorting when query type is query_string', function () {
esConfig.query = {...query, queryType: 'query_string'};
expect(buildQuery(params, config, esConfig)).to.be.an('object');
});
it('should build query that includes sorting when query type is dis_max', function () {
esConfig.query = {...query, queryType: 'dis_max'};
expect(buildQuery(params, config, esConfig)).to.be.an('object');
});
it('should build query that includes sorting when query type is dis_max', function () {
esConfig.query = {...query, queryType: 'dis_max'};
expect(buildQuery(params, config, esConfig)).to.be.an('object');
});
it('should reject (throw an error) if provided query type is not supported', function () {
// @ts-ignore
esConfig.query = {...query, queryType: 'invalid_query_type'};
expect(() => buildQuery(params, config, esConfig)).to.throw('query type');
});
});
describe('buildFilter', function () {
const searchFilters: {[key: string]: SCSearchFilter} = {
value: {
type: 'value',
arguments: {
field: 'type',
value: SCThingType.Dish
}
},
availability: {
type: 'availability',
arguments: {
time: '2017-01-30T12:05:00.000Z',
fromField: 'offers.availabilityStarts',
toField: 'offers.availabilityEnds'
}
},
distance: {
type: 'distance',
arguments: {
distance: 1000,
field: 'geo.point.coordinates',
position: [50.123, 8.123],
}
},
boolean: {
type: 'boolean',
arguments: {
operation: 'and',
filters: [
{
type: 'value',
arguments: {
field: 'type',
value: SCThingType.Dish,
}
},
{
type: 'availability',
arguments: {
fromField: 'offers.availabilityStarts',
toField: 'offers.availabilityEnds'
}
}
]
}
},
};
it('should build value filter', function () {
const filter = buildFilter(searchFilters.value);
const expectedFilter: ESTermFilter = {
term: {
'type.raw': SCThingType.Dish
}
};
expect(filter).to.be.eql(expectedFilter);
});
it('should build availability filter', function () {
const filter = buildFilter(searchFilters.availability);
const expectedFilter: ESBooleanFilter<any> = {
bool: {
should: [
{
bool: {
must: [
{
range: {
'offers.availabilityStarts': {
lte: '2017-01-30T12:05:00.000Z'
}
}
},
{
range: {
'offers.availabilityEnds': {
gte: '2017-01-30T12:05:00.000Z'
}
}
}
]
}
},
{
bool: {
must_not: [
{
exists: {
field: 'offers.availabilityStarts'
}
},
{
exists: {
field: 'offers.availabilityEnds'
}
}
]
}
}
]
}
};
expect(filter).to.be.eql(expectedFilter);
});
it('should build distance filter', function () {
const filter = buildFilter(searchFilters.distance);
const expectedFilter: ESGeoDistanceFilter = {
geo_distance: {
distance: '1000m',
'geo.point.coordinates': {
lat: 8.123,
lon: 50.123
}
}
};
expect(filter).to.be.eql(expectedFilter);
});
it('should build boolean filter', function () {
const filter = buildFilter(searchFilters.boolean);
const expectedFilter: ESBooleanFilter<any> = {
bool: {
minimum_should_match: 0,
must: [
{
term: {
'type.raw': 'dish'
}
},
{
bool: {
should: [
{
bool: {
must: [
{
range: {
'offers.availabilityStarts': {
lte: 'now'
}
}
},
{
range: {
'offers.availabilityEnds': {
gte: 'now'
}
}
}
]
}
},
{
bool: {
must_not: [
{
exists: {
field: 'offers.availabilityStarts'
}
},
{
exists: {
field: 'offers.availabilityEnds'
}
}
]
}
}
]
}
}
],
must_not: [],
should: []
}
}
expect(filter).to.be.eql(expectedFilter);
});
});
describe('buildSort', function () {
const searchSCSearchSort: Array<SCSearchSort> = [
{
type: 'ducet',
order: 'desc',
arguments: {
field: 'name'
},
},
{
type: 'distance',
order: 'desc',
arguments: {
field: 'geo.point',
position: [8.123, 50.123]
},
},
{
type: 'price',
order: 'asc',
arguments: {
universityRole: 'student',
field: 'offers.prices',
}
},
];
let sorts: Array<ESDucetSort | ESGeoDistanceSort | ScriptSort> = [];
const expectedSorts: {[key: string]: ESDucetSort | ESGeoDistanceSort | ScriptSort} = {
ducet: {
'name.sort': 'desc'
},
distance: {
_geo_distance: {
mode: 'avg',
order: 'desc',
unit: 'm',
'geo.point': {
lat: 50.123,
lon: 8.123
}
}
},
price: {
_script: {
order: 'asc',
script: '\n // foo price sort script',
type: 'number'
}
}
};
before(function () {
sorts = buildSort(searchSCSearchSort);
});
it('should build ducet sort', function () {
expect(sorts[0]).to.be.eql(expectedSorts.ducet);
});
it('should build distance sort', function () {
expect(sorts[1]).to.be.eql(expectedSorts.distance);
});
it('should build price sort', function () {
const priceSortNoScript = {...sorts[2], _script: {...(sorts[2] as ScriptSort)._script, script: (expectedSorts.price as ScriptSort)._script.script}}
expect(priceSortNoScript).to.be.eql(expectedSorts.price);
});
});
});