/* * Copyright (C) 2015 Centro de Computacao Cientifica e Software Livre * Departamento de Informatica - Universidade Federal do Parana * * This file is part of blendb. * * blendb is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * blendb 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with blendb. If not, see . */ import { expect } from "chai"; import { Engine } from "./engine"; import { Metric } from "./metric"; import { Filter, FilterOperator } from "./filter"; import { Clause } from "./clause"; import { View } from "./view"; import { engineScenario } from "../../test/scenario"; describe("engine class", () => { const engine = new Engine(); const met = engineScenario.metrics; const dim = engineScenario.dimensions; const subdim = engineScenario.subDimensions; const views = engineScenario.views; met.forEach((item) => engine.addMetric(item)); dim.forEach((item) => engine.addDimension(item)); subdim.forEach((item) => engine.addDimension(item)); views.forEach((view) => engine.addView(view)); it("should be create a fill that cover all metrics and dimensions", () => { let query = { metrics : met.slice(0) , dimensions : dim.slice(0) }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); expect(optimalView).to.have.property("dimensions"); expect(optimalView).to.have.property("childViews"); expect(optimalView.metrics).to.be.an("array"); expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(12); expect(optimalView.dimensions).to.have.length(13); }); it("should throw an exception, query with non-existent metric", () => { let error: boolean = false; try { engine.query({metrics: [engineScenario.wrongMet], dimensions: [dim[0]]}); } catch (e){ error = true; expect(e.message).to.be.equal("Engine views cannot cover the query"); } expect(error); }); it("should throw an exception, query with non-existent dimension", () => { let error: boolean = false; try { engine.query({metrics: [met[0]], dimensions: [engineScenario.wrongDim]}); } catch (e){ error = true; expect(e.message).to.be.equal("Engine views cannot cover the query"); } expect(error); }); it("should be create a fill that cover the query, that match perfectly with a existent view", () => { let query = { metrics : [met[0], met[1], met[2], met[10]] , dimensions : [dim[0], dim[7]] }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); expect(optimalView).to.have.property("dimensions"); expect(optimalView).to.have.property("childViews"); expect(optimalView.metrics).to.be.an("array"); expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(4); expect(optimalView.dimensions).to.have.length(2); expect(optimalView.childViews).to.have.length(0); expect(optimalView.id).to.be.equal(views[0].id); }); it("should be create a fill that cover the query, that match perfectly with a existent view, with clauses", () => { const clause = new Clause({ filters: [new Filter({ target: dim[2], operator: FilterOperator.NOTEQUAL, value: "1" })] }); let query = { metrics : [met[5], met[6], met[7]] , dimensions : [dim[2]] , clauses: [clause] }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); expect(optimalView).to.have.property("dimensions"); expect(optimalView).to.have.property("childViews"); expect(optimalView).to.have.property("materialized"); expect(optimalView.metrics).to.be.an("array"); expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(3); expect(optimalView.dimensions).to.have.length(1); expect(optimalView.childViews).to.have.length(0); expect(optimalView.materialized).to.be.true; expect(optimalView.id).to.be.equal(views[9].id); }); it("should be create a fill that cover the query, using sub dimensions", () => { let emptyMetrics: Metric[] = []; let query = { metrics : emptyMetrics , dimensions : subdim.slice(0, 2) }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); expect(optimalView).to.have.property("dimensions"); expect(optimalView).to.have.property("childViews"); expect(optimalView.metrics).to.be.an("array"); expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(0); expect(optimalView.dimensions).to.have.length(2); expect(optimalView.childViews).to.have.length(3); expect(optimalView).satisfy((optView: View) => { return optView.dimensions.some((item) => item.name === subdim[0].name); }); expect(optimalView).satisfy((optView: View) => { return optView.dimensions.some((item) => item.name === subdim[1].name); }); }); it("should be create a fill that cover the query, using the parents of sub dimensions", () => { let emptyMetrics: Metric[] = []; let query = { metrics : emptyMetrics , dimensions : [subdim[2], subdim[4]] }; let optimalView = engine.query(query); expect(optimalView).to.be.an("object"); expect(optimalView).to.have.property("metrics"); expect(optimalView).to.have.property("dimensions"); expect(optimalView).to.have.property("childViews"); expect(optimalView.metrics).to.be.an("array"); expect(optimalView.dimensions).to.be.an("array"); expect(optimalView.childViews).to.be.an("array"); expect(optimalView.metrics).to.have.length(0); expect(optimalView.dimensions).to.have.length(2); expect(optimalView.childViews).to.have.length(3); expect(optimalView).satisfy((optView: View) => { return optView.dimensions.some((item) => item.name === subdim[2].name); }); expect(optimalView).satisfy((optView: View) => { return optView.dimensions.some((item) => item.name === subdim[4].name); }); }); it("should throw an exception, sub-dimension with non-existent parent", () => { let error: boolean = false; try { engine.query({metrics: [met[0]], dimensions: [subdim[3]]}); } catch (e){ error = true; expect(e.message).to.be.equal("Engine views cannot cover the query"); } expect(error).to.be.true; }); it("should parse a clause, with target as dimension and operator as ==", () => { const strFilter = "dim:0==0"; const clause = engine.parseClause(strFilter); expect(clause).to.be.an("object"); expect(clause).to.have.property("filters"); expect(clause).to.have.property("id"); expect(clause.filters).to.be.an("array"); expect(clause.filters).to.have.length(1); expect(clause.filters[0]).to.have.property("id"); expect(clause.filters[0]).to.have.property("target"); expect(clause.filters[0]).to.have.property("value"); expect(clause.filters[0]).to.have.property("operator"); expect(clause.filters[0].target).to.be.equal(dim[0]); expect(clause.filters[0].value).to.be.equal("0"); expect(clause.filters[0].operator).to.be.equal(FilterOperator.EQUAL); }); it("should parse a clause, with target as metric and operator as !=", () => { const strFilter = "met:0!=0"; const clause = engine.parseClause(strFilter); expect(clause).to.be.an("object"); expect(clause).to.have.property("filters"); expect(clause).to.have.property("id"); expect(clause.filters).to.be.an("array"); expect(clause.filters).to.have.length(1); expect(clause.filters[0]).to.have.property("id"); expect(clause.filters[0]).to.have.property("target"); expect(clause.filters[0]).to.have.property("operator"); expect(clause.filters[0]).to.have.property("value"); expect(clause.filters[0].target).to.be.equal(met[0]); expect(clause.filters[0].value).to.be.equal("0"); expect(clause.filters[0].operator).to.be.equal(FilterOperator.NOTEQUAL); }); it("should throw an exception, when a dimension is not found", () => { let error: boolean = false; const strFilter = "dim:-1==0"; const exeption = "Filter could not be created: \"dim:-1\" was not found"; try { engine.parseClause(strFilter); } catch (e){ error = true; expect(e.message).to.be.equal(exeption); } expect(error).to.be.true; }); it("should throw an exception, when a metric is not found", () => { let error: boolean = false; const strFilter = "met:-1==0"; const exeption = "Filter could not be created: \"met:-1\" was not found"; try { engine.parseClause(strFilter); } catch (e){ error = true; expect(e.message).to.be.equal(exeption); } expect(error).to.be.true; }); it("should throw an exception, when a operator is not found", () => { let error: boolean = false; let strFilter = "met:-1=?0"; let exeption = "Filter could not be created: Operator on \"" + strFilter + "\" could not be extracted"; try { engine.parseClause(strFilter); } catch (e){ error = true; expect(e.message).to.be.equal(exeption); } expect(error).to.be.true; error = false; strFilter = "met:-1!?0"; exeption = "Filter could not be created: Operator on \"" + strFilter + "\" could not be extracted"; try { engine.parseClause(strFilter); } catch (e){ error = true; expect(e.message).to.be.equal(exeption); } expect(error).to.be.true; }); it("should throw an exception, when a operator does not suit", () => { const operators = [">", "<", "<=", ">="]; for (let i = 0; i < operators.length; ++i) { let error: boolean = false; let strFilter = "dim:3" + operators[i] + "joao"; let exeption = "Filter could not be created: Operator \"" + operators[i] + "\" is invalid for target \"dim:3\""; try { engine.parseClause(strFilter); } catch (e){ error = true; expect(e.message).to.be.equal(exeption); } } }); it("should parse clauses with several operators for dates and integers", () => { const operators: {[key: string]: FilterOperator} = { ">": FilterOperator.GREATER, "<": FilterOperator.LOWER, "<=": FilterOperator.LOWEREQ, ">=": FilterOperator.GREATEREQ, "==": FilterOperator.EQUAL, "!=": FilterOperator.NOTEQUAL }; for (let op of Object.keys(operators)) { const strFilter = "dim:0" + op + "0"; const clause = engine.parseClause(strFilter); expect(clause).to.be.an("object"); expect(clause).to.have.property("filters"); expect(clause).to.have.property("id"); expect(clause.filters).to.be.an("array"); expect(clause.filters).to.have.length(1); expect(clause.filters[0]).to.have.property("id"); expect(clause.filters[0]).to.have.property("target"); expect(clause.filters[0]).to.have.property("operator"); expect(clause.filters[0]).to.have.property("value"); expect(clause.filters[0].target).to.be.equal(dim[0]); expect(clause.filters[0].value).to.be.equal("0"); expect(clause.filters[0].operator).to.be.equal(operators[op]); } for (let op of Object.keys(operators)) { const strFilter = "dim:2" + op + "0"; const clause = engine.parseClause(strFilter); expect(clause).to.be.an("object"); expect(clause).to.have.property("filters"); expect(clause).to.have.property("id"); expect(clause.filters).to.be.an("array"); expect(clause.filters).to.have.length(1); expect(clause.filters[0]).to.have.property("id"); expect(clause.filters[0]).to.have.property("target"); expect(clause.filters[0]).to.have.property("operator"); expect(clause.filters[0]).to.have.property("value"); expect(clause.filters[0].target).to.be.equal(dim[2]); expect(clause.filters[0].value).to.be.equal("0"); expect(clause.filters[0].operator).to.be.equal(operators[op]); } }); });