Added some tests (createOne and getAll)
This commit is contained in:
parent
ef7ca0315b
commit
9f0886541c
21 changed files with 2940 additions and 67 deletions
30
.babelrc
Normal file
30
.babelrc
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
[
|
||||||
|
"@babel/plugin-transform-runtime",
|
||||||
|
{
|
||||||
|
"regenerator": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@babel/plugin-proposal-class-properties"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"presets": [
|
||||||
|
[
|
||||||
|
"@babel/preset-env",
|
||||||
|
{
|
||||||
|
"targets": {
|
||||||
|
"node": "current"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-modules-commonjs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -20,6 +20,7 @@ coverage
|
||||||
# nyc test coverage
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
/reports/
|
/reports/
|
||||||
|
junit.xml
|
||||||
|
|
||||||
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
.grunt
|
.grunt
|
||||||
|
|
10
config/config.json
Normal file
10
config/config.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"test": {
|
||||||
|
"username": "postgres",
|
||||||
|
"password": "postgres",
|
||||||
|
"database": "test",
|
||||||
|
"host": "sequelize-middleware-db",
|
||||||
|
"dialect": "postgres",
|
||||||
|
"seederStorage": "sequelize"
|
||||||
|
}
|
||||||
|
}
|
33
docker-compose.yml
Normal file
33
docker-compose.yml
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
version: "2"
|
||||||
|
|
||||||
|
services:
|
||||||
|
sequelize-middleware:
|
||||||
|
image: "node:latest"
|
||||||
|
user: "node"
|
||||||
|
working_dir: /home/node/app
|
||||||
|
command: >
|
||||||
|
bash -c "yarn install &&
|
||||||
|
./node_modules/.bin/sequelize db:seed:undo:all &&
|
||||||
|
./node_modules/.bin/sequelize db:migrate:undo:all &&
|
||||||
|
./node_modules/.bin/sequelize db:migrate &&
|
||||||
|
./node_modules/.bin/sequelize db:seed:all &&
|
||||||
|
yarn test --ci --collectCoverage=true"
|
||||||
|
volumes:
|
||||||
|
- ./:/home/node/app
|
||||||
|
- /home/node/node_modules
|
||||||
|
ports:
|
||||||
|
- "3666:4000"
|
||||||
|
depends_on:
|
||||||
|
- sequelize-middleware-db
|
||||||
|
environment:
|
||||||
|
NODE_ENV: "test"
|
||||||
|
sequelize-middleware-db:
|
||||||
|
image: postgres:latest
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test
|
||||||
|
sequelize-middleware-adminer:
|
||||||
|
image: adminer
|
||||||
|
ports:
|
||||||
|
- 127.0.0.1:8666:8080
|
|
@ -9,8 +9,8 @@ class Middleware extends QueryBuilder {
|
||||||
*/
|
*/
|
||||||
constructor(params, models) {
|
constructor(params, models) {
|
||||||
super(params, models);
|
super(params, models);
|
||||||
this.params = params || {};
|
this.params = params;
|
||||||
this.models = models || {};
|
this.models = models;
|
||||||
this.includes = [];
|
this.includes = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ class Middleware extends QueryBuilder {
|
||||||
|
|
||||||
// Un paramètre n'est pas bon dans les params
|
// Un paramètre n'est pas bon dans les params
|
||||||
if (error) {
|
if (error) {
|
||||||
callback(new ErrorBuilder(406, error));
|
callback(new ErrorBuilder(406.1, error));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ class QueryBuilder {
|
||||||
(page === "next" && currentPage < maxPage)
|
(page === "next" && currentPage < maxPage)
|
||||||
) {
|
) {
|
||||||
let newIndex = 0;
|
let newIndex = 0;
|
||||||
|
// eslint-disable-next-line default-case
|
||||||
switch (page) {
|
switch (page) {
|
||||||
case "first":
|
case "first":
|
||||||
newIndex = 1;
|
newIndex = 1;
|
||||||
|
@ -43,9 +44,6 @@ class QueryBuilder {
|
||||||
case "next":
|
case "next":
|
||||||
newIndex = currentPage + 1;
|
newIndex = currentPage + 1;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
newIndex = currentPage;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
href: href.replace(`page=${currentPage}`, `page=${newIndex}`),
|
href: href.replace(`page=${currentPage}`, `page=${newIndex}`),
|
||||||
|
@ -117,6 +115,9 @@ class QueryBuilder {
|
||||||
case "$in":
|
case "$in":
|
||||||
newObject[Op.in] = value.split(",");
|
newObject[Op.in] = value.split(",");
|
||||||
break;
|
break;
|
||||||
|
case "$like":
|
||||||
|
newObject[Op.like] = value;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
newObject[key] = [value];
|
newObject[key] = [value];
|
||||||
break;
|
break;
|
||||||
|
@ -128,6 +129,8 @@ class QueryBuilder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("newObject:", newObject);
|
||||||
|
|
||||||
return newObject;
|
return newObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +141,12 @@ class QueryBuilder {
|
||||||
*/
|
*/
|
||||||
_haveRight(req) {
|
_haveRight(req) {
|
||||||
let allowedRole = "all";
|
let allowedRole = "all";
|
||||||
|
if (!req.user) {
|
||||||
|
req.user = {};
|
||||||
|
}
|
||||||
|
if (!req.user.role) {
|
||||||
|
req.user.role = "guest";
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line default-case
|
// eslint-disable-next-line default-case
|
||||||
switch (req.method) {
|
switch (req.method) {
|
||||||
|
@ -218,10 +227,6 @@ class QueryBuilder {
|
||||||
return override;
|
return override;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params) {
|
|
||||||
return override;
|
|
||||||
}
|
|
||||||
|
|
||||||
// On surcharge certains paramètres passé en query
|
// On surcharge certains paramètres passé en query
|
||||||
if (params.filters) {
|
if (params.filters) {
|
||||||
Object.keys(params.filters).map(column => {
|
Object.keys(params.filters).map(column => {
|
||||||
|
@ -259,6 +264,13 @@ class QueryBuilder {
|
||||||
* @return {Mixed}
|
* @return {Mixed}
|
||||||
*/
|
*/
|
||||||
_setInclusions(req, include) {
|
_setInclusions(req, include) {
|
||||||
|
if (!req.user) {
|
||||||
|
req.user = {};
|
||||||
|
}
|
||||||
|
if (!req.user.role) {
|
||||||
|
req.user.role = "guest";
|
||||||
|
}
|
||||||
|
|
||||||
const includes = [];
|
const includes = [];
|
||||||
|
|
||||||
const listOfIncludes = include || this.params.includes;
|
const listOfIncludes = include || this.params.includes;
|
||||||
|
@ -361,12 +373,16 @@ class QueryBuilder {
|
||||||
callback(null, createdItem);
|
callback(null, createdItem);
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
if (err.name === "SequelizeUniqueConstraintError") {
|
switch (err.name) {
|
||||||
callback(new ErrorBuilder(409, "Duplicate item"));
|
case "SequelizeUniqueConstraintError":
|
||||||
return false;
|
callback(new ErrorBuilder(409.1, "Duplicate item"));
|
||||||
|
break;
|
||||||
|
case "SequelizeForeignKeyConstraintError":
|
||||||
|
callback(new ErrorBuilder(406.2, "Bad foreign key"));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
callback(new ErrorBuilder(500, err));
|
||||||
}
|
}
|
||||||
callback(err);
|
|
||||||
return false;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,10 +394,13 @@ class QueryBuilder {
|
||||||
*/
|
*/
|
||||||
_formatItem(item, formatRule) {
|
_formatItem(item, formatRule) {
|
||||||
const formated = {};
|
const formated = {};
|
||||||
|
|
||||||
Object.keys(formatRule).map(key => {
|
Object.keys(formatRule).map(key => {
|
||||||
switch (typeof formatRule[key]) {
|
switch (typeof formatRule[key]) {
|
||||||
case "string":
|
case "string":
|
||||||
|
if (item) {
|
||||||
formated[key] = item[key] || null;
|
formated[key] = item[key] || null;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "object":
|
case "object":
|
||||||
if (Array.isArray(item[key])) {
|
if (Array.isArray(item[key])) {
|
||||||
|
@ -451,7 +470,7 @@ class QueryBuilder {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
callback(new ErrorBuilder(406, error));
|
callback(new ErrorBuilder(406.1, error));
|
||||||
} else {
|
} else {
|
||||||
callback(null, value);
|
callback(null, value);
|
||||||
}
|
}
|
||||||
|
@ -471,16 +490,32 @@ class QueryBuilder {
|
||||||
|
|
||||||
// On test les droits
|
// On test les droits
|
||||||
if (!this._haveRight(req)) {
|
if (!this._haveRight(req)) {
|
||||||
callback(new ErrorBuilder(401, "You're not allowed"));
|
callback(new ErrorBuilder(401.1, "You're not allowed"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// On teste la query (ou les params)
|
// On teste la query (ou les params)
|
||||||
const toValidate = req.getType === "list" ? req.query : req.params;
|
const toValidate = req.getType === "list" ? req.query : req.params;
|
||||||
|
|
||||||
|
if (!toValidate) {
|
||||||
|
callback(
|
||||||
|
new ErrorBuilder(
|
||||||
|
406.0,
|
||||||
|
`Missing ${req.getType === "list" ? "query" : "params"}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const { error, value } = this.params.validate[
|
const { error, value } = this.params.validate[
|
||||||
req.getType
|
req.getType
|
||||||
].validate(toValidate, { abortEarly: false });
|
].validate(toValidate, { abortEarly: false });
|
||||||
|
|
||||||
|
// Un paramètre n'est pas bon dans la query
|
||||||
|
if (error) {
|
||||||
|
callback(new ErrorBuilder(406.1, error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// On vire, pour le moment la liste des filtres un peu particuliers
|
// On vire, pour le moment la liste des filtres un peu particuliers
|
||||||
let listOfIgnoredFilters = [
|
let listOfIgnoredFilters = [
|
||||||
"limit",
|
"limit",
|
||||||
|
@ -512,17 +547,11 @@ class QueryBuilder {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Un paramètre n'est pas bon dans la query
|
|
||||||
if (error) {
|
|
||||||
callback(new ErrorBuilder(406, error));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.getType === "list") {
|
if (req.getType === "list") {
|
||||||
// Aucune pagination n'est passée, on set celle par défaut
|
// Aucune pagination n'est passée, on set celle par défaut
|
||||||
if (!value.page || !value.limit) {
|
if (!value.page || !value.limit) {
|
||||||
value.page = 1;
|
value.page = 1;
|
||||||
value.limit = 20;
|
value.limit = 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un tri est spécifié
|
// Un tri est spécifié
|
||||||
|
@ -542,11 +571,6 @@ class QueryBuilder {
|
||||||
const override = this._override(req, req.getType);
|
const override = this._override(req, req.getType);
|
||||||
where = Object.assign(where, override);
|
where = Object.assign(where, override);
|
||||||
|
|
||||||
// if ( value.page) {
|
|
||||||
// query.offset = ( value.page - 1 ) * value.limit;
|
|
||||||
// query.limit = value.limit;
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (order) {
|
if (order) {
|
||||||
query.order = order;
|
query.order = order;
|
||||||
}
|
}
|
||||||
|
@ -560,11 +584,6 @@ class QueryBuilder {
|
||||||
// Hack pour faire un recherche dans les nested de type hasMany
|
// Hack pour faire un recherche dans les nested de type hasMany
|
||||||
query.subQuery = false;
|
query.subQuery = false;
|
||||||
|
|
||||||
if (!value.page || !value.limit) {
|
|
||||||
value.page = 1;
|
|
||||||
value.limit = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, query, {
|
callback(null, query, {
|
||||||
start: value.page * value.limit - value.limit,
|
start: value.page * value.limit - value.limit,
|
||||||
limit: value.limit
|
limit: value.limit
|
||||||
|
|
28
migrations/20200211081052-create-brands.js
Normal file
28
migrations/20200211081052-create-brands.js
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
return queryInterface.createTable("Brands", {
|
||||||
|
id: {
|
||||||
|
allowNull: false,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
type: Sequelize.INTEGER
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
down: queryInterface => {
|
||||||
|
return queryInterface.dropTable("Brands");
|
||||||
|
}
|
||||||
|
};
|
40
migrations/20200211081344-create-cars.js
Normal file
40
migrations/20200211081344-create-cars.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface, Sequelize) => {
|
||||||
|
return queryInterface.createTable("Cars", {
|
||||||
|
id: {
|
||||||
|
allowNull: false,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
type: Sequelize.INTEGER
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Sequelize.STRING
|
||||||
|
},
|
||||||
|
year: {
|
||||||
|
type: Sequelize.INTEGER
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Sequelize.BOOLEAN
|
||||||
|
},
|
||||||
|
brandId: {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
references: {
|
||||||
|
model: "Brands",
|
||||||
|
key: "id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
allowNull: false,
|
||||||
|
type: Sequelize.DATE
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
down: queryInterface => {
|
||||||
|
return queryInterface.dropTable("Cars");
|
||||||
|
}
|
||||||
|
};
|
19
models/brands.js
Normal file
19
models/brands.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const Brands = sequelize.define(
|
||||||
|
"Brands",
|
||||||
|
{
|
||||||
|
name: DataTypes.STRING
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
Brands.associate = function(models) {
|
||||||
|
Brands.hasMany(models.Cars, {
|
||||||
|
as: "Cars",
|
||||||
|
foreignKey: "brandId",
|
||||||
|
sourceKey: "id"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Brands;
|
||||||
|
};
|
27
models/cards.js
Normal file
27
models/cards.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
module.exports = (sequelize, DataTypes) => {
|
||||||
|
const Cars = sequelize.define(
|
||||||
|
"Cars",
|
||||||
|
{
|
||||||
|
name: DataTypes.STRING,
|
||||||
|
year: DataTypes.INTEGER,
|
||||||
|
active: DataTypes.BOOLEAN,
|
||||||
|
brandId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
references: {
|
||||||
|
model: "Brands",
|
||||||
|
key: "id"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
Cars.associate = function(models) {
|
||||||
|
Cars.belongsTo(models.Brands, {
|
||||||
|
as: "Brand",
|
||||||
|
foreignKey: "brandId"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return Cars;
|
||||||
|
};
|
43
models/index.js
Normal file
43
models/index.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const Sequelize = require("sequelize");
|
||||||
|
|
||||||
|
const basename = path.basename(__filename);
|
||||||
|
const env = process.env.NODE_ENV || "development";
|
||||||
|
// eslint-disable-next-line import/no-dynamic-require
|
||||||
|
const config = require(`${__dirname}/../config/config.json`)[env];
|
||||||
|
const db = {};
|
||||||
|
|
||||||
|
let sequelize;
|
||||||
|
if (config.use_env_variable) {
|
||||||
|
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
||||||
|
} else {
|
||||||
|
sequelize = new Sequelize(
|
||||||
|
config.database,
|
||||||
|
config.username,
|
||||||
|
config.password,
|
||||||
|
config
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readdirSync(__dirname)
|
||||||
|
.filter(file => {
|
||||||
|
return (
|
||||||
|
file.indexOf(".") !== 0 && file !== basename && file.slice(-3) === ".js"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.forEach(file => {
|
||||||
|
const model = sequelize.import(path.join(__dirname, file));
|
||||||
|
db[model.name] = model;
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(db).forEach(modelName => {
|
||||||
|
if (db[modelName].associate) {
|
||||||
|
db[modelName].associate(db);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
db.sequelize = sequelize;
|
||||||
|
db.Sequelize = Sequelize;
|
||||||
|
|
||||||
|
module.exports = db;
|
18
package.json
18
package.json
|
@ -5,8 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "./node_modules/.bin/eslint . --fix",
|
"lint": "./node_modules/.bin/eslint . --fix",
|
||||||
"test": "jest --forceExit --detectOpenHandles --maxWorkers=10",
|
"test": "jest --forceExit --detectOpenHandles --maxWorkers=10"
|
||||||
"coverage": "jest --coverage"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -30,6 +29,9 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js"
|
||||||
|
],
|
||||||
"verbose": true,
|
"verbose": true,
|
||||||
"roots": [
|
"roots": [
|
||||||
"<rootDir>",
|
"<rootDir>",
|
||||||
|
@ -69,6 +71,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.8.3",
|
||||||
|
"@babel/preset-env": "^7.8.4",
|
||||||
|
"@hapi/joi": "^16.1.8",
|
||||||
"babel-eslint": "^10.0.3",
|
"babel-eslint": "^10.0.3",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-config-airbnb-base": "^14.0.0",
|
"eslint-config-airbnb-base": "^14.0.0",
|
||||||
|
@ -79,9 +85,15 @@
|
||||||
"husky": "^4.2.1",
|
"husky": "^4.2.1",
|
||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"jest-html-reporter": "^2.8.0",
|
"jest-html-reporter": "^2.8.0",
|
||||||
|
"jest-junit": "^10.0.0",
|
||||||
"lint-staged": "^10.0.7",
|
"lint-staged": "^10.0.7",
|
||||||
|
"pg": "^7.18.1",
|
||||||
|
"pg-hstore": "^2.3.3",
|
||||||
"prettier": "^1.19.1",
|
"prettier": "^1.19.1",
|
||||||
|
"sequelize": "^5.21.3",
|
||||||
|
"sequelize-cli": "^5.5.1",
|
||||||
"sinon": "^8.1.1",
|
"sinon": "^8.1.1",
|
||||||
"supertest": "^4.0.2"
|
"supertest": "^4.0.2",
|
||||||
|
"uuid": "^3.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
69
rules/Brands.js
Normal file
69
rules/Brands.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import Joi from "@hapi/joi";
|
||||||
|
|
||||||
|
const Rules = {
|
||||||
|
model: "Brands",
|
||||||
|
crud: {
|
||||||
|
read: ["admin", "user"],
|
||||||
|
write: ["admin"],
|
||||||
|
edit: ["admin"],
|
||||||
|
delete: ["admin"]
|
||||||
|
},
|
||||||
|
includes: [
|
||||||
|
{
|
||||||
|
collection: "Cars",
|
||||||
|
requiredRole: ["admin", "user"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
format: {
|
||||||
|
user: {
|
||||||
|
id: "id",
|
||||||
|
name: "name"
|
||||||
|
},
|
||||||
|
admin: {
|
||||||
|
id: "id",
|
||||||
|
name: "name",
|
||||||
|
created: "createdAt",
|
||||||
|
updated: "updatedAt",
|
||||||
|
Cars: {
|
||||||
|
id: "id",
|
||||||
|
name: "name",
|
||||||
|
year: "year",
|
||||||
|
created: "createdAt",
|
||||||
|
updated: "updatedAt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemId: "brandId",
|
||||||
|
validate: {
|
||||||
|
create: Joi.object({
|
||||||
|
name: Joi.string().required()
|
||||||
|
}),
|
||||||
|
update: Joi.object({
|
||||||
|
name: Joi.string()
|
||||||
|
}),
|
||||||
|
item: Joi.object({
|
||||||
|
brandId: Joi.number().required()
|
||||||
|
}),
|
||||||
|
list: Joi.object({
|
||||||
|
limit: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1)
|
||||||
|
.max(50),
|
||||||
|
page: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1),
|
||||||
|
sort: Joi.string()
|
||||||
|
.valid("id", "name", "createdAt", "updatedAt")
|
||||||
|
.only(),
|
||||||
|
order: Joi.string()
|
||||||
|
.valid("asc", "desc")
|
||||||
|
.only()
|
||||||
|
})
|
||||||
|
.with("limit", "page")
|
||||||
|
.with("page", "limit")
|
||||||
|
.with("sort", "order")
|
||||||
|
.with("order", "sort")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Rules;
|
125
rules/Cars.js
Normal file
125
rules/Cars.js
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
import Joi from "@hapi/joi";
|
||||||
|
|
||||||
|
const Rules = {
|
||||||
|
model: "Cars",
|
||||||
|
crud: {
|
||||||
|
read: ["admin", "user"],
|
||||||
|
write: ["admin"],
|
||||||
|
edit: ["admin"],
|
||||||
|
delete: ["admin"]
|
||||||
|
},
|
||||||
|
includes: [
|
||||||
|
{
|
||||||
|
collection: "Brand",
|
||||||
|
requiredRole: ["admin", "user"]
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// collection: "Colors",
|
||||||
|
// requiredRole: ["admin", "user"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
format: {
|
||||||
|
user: {
|
||||||
|
id: "id",
|
||||||
|
model: "model",
|
||||||
|
Brand: {
|
||||||
|
name: "name"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
itemId: "carId",
|
||||||
|
validate: {
|
||||||
|
create: Joi.object({
|
||||||
|
name: Joi.string().required(),
|
||||||
|
active: Joi.boolean(),
|
||||||
|
year: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.required(),
|
||||||
|
brandId: Joi.number().integer()
|
||||||
|
}),
|
||||||
|
update: Joi.object({
|
||||||
|
name: Joi.string()
|
||||||
|
}),
|
||||||
|
item: Joi.object({
|
||||||
|
carId: Joi.number().required()
|
||||||
|
}),
|
||||||
|
list: Joi.object({
|
||||||
|
weirdfilter: Joi.string(),
|
||||||
|
"name.lk": Joi.string(),
|
||||||
|
name: Joi.string(),
|
||||||
|
year: Joi.number().integer(),
|
||||||
|
"year.lte": Joi.number().integer(),
|
||||||
|
"year.gte": Joi.number().integer(),
|
||||||
|
limit: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1)
|
||||||
|
.max(50),
|
||||||
|
page: Joi.number()
|
||||||
|
.integer()
|
||||||
|
.min(1),
|
||||||
|
sort: Joi.string()
|
||||||
|
.valid("id", "name", "createdAt", "updatedAt")
|
||||||
|
.only(),
|
||||||
|
order: Joi.string()
|
||||||
|
.valid("asc", "desc")
|
||||||
|
.only()
|
||||||
|
})
|
||||||
|
.with("year.lte", "year.gte")
|
||||||
|
.with("year.gte", "year.lte")
|
||||||
|
.with("limit", "page")
|
||||||
|
.with("page", "limit")
|
||||||
|
.with("sort", "order")
|
||||||
|
.with("order", "sort")
|
||||||
|
},
|
||||||
|
restrictOn: {
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
roles: ["user"],
|
||||||
|
type: "raw",
|
||||||
|
field: "active",
|
||||||
|
value: "true"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
item: [
|
||||||
|
{
|
||||||
|
roles: ["user"],
|
||||||
|
type: "raw",
|
||||||
|
field: "active",
|
||||||
|
value: "true"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
override: {
|
||||||
|
list: {
|
||||||
|
filters: {
|
||||||
|
weirdfilter: {
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
active: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: 2003
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name.lk": {
|
||||||
|
name: {
|
||||||
|
$like: "%_TERM_%"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"year.lte": {
|
||||||
|
year: {
|
||||||
|
$lte: "_TERM_"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"year.gte": {
|
||||||
|
year: {
|
||||||
|
$gte: "_TERM_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Rules;
|
177
test/createOne.test.js
Normal file
177
test/createOne.test.js
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/* eslint-disable jest/no-test-callback */
|
||||||
|
import uuid from "uuid/v4";
|
||||||
|
import { truncate } from "./utils/common";
|
||||||
|
|
||||||
|
import models from "../models";
|
||||||
|
import Brands from "../rules/Brands";
|
||||||
|
import Cars from "../rules/Cars";
|
||||||
|
import Middelware from "../index";
|
||||||
|
|
||||||
|
let createdBrand = null;
|
||||||
|
|
||||||
|
describe("createOne", () => {
|
||||||
|
afterAll(done => {
|
||||||
|
truncate(["Brands", "Cars"], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return a 401 when guest tries to create new item", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "POST",
|
||||||
|
user: null,
|
||||||
|
body: {
|
||||||
|
name: uuid()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.createOne(req, (err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(401.1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return a 401 when unauthorized role tries to create new item", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name: uuid()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(401.1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return a 406 when authorized role tries to create new item with missing required values", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
body: {}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(406.1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return new item when authorized role create new item", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const name = uuid();
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("id");
|
||||||
|
expect(res).toHaveProperty("created");
|
||||||
|
expect(res).toHaveProperty("updated");
|
||||||
|
expect(res).not.toHaveProperty("createdAt");
|
||||||
|
expect(res).not.toHaveProperty("updatedAt");
|
||||||
|
expect(res.name).toBe(name);
|
||||||
|
|
||||||
|
createdBrand = res;
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return errorCode 409.1 when authorized role tries to create new item with same unique field", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name: createdBrand.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(409.1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return errorCode 406.2 when authorized role tries to create new item with bad foreign id", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const name = uuid();
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name,
|
||||||
|
year: 2004,
|
||||||
|
brandId: createdBrand.id + 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(406.2);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return errorCode 500 when authorized role tries to create new item with too long value", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
|
||||||
|
middleware.createOne(
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
name:
|
||||||
|
"ThohWaigohhieHogahthoxohpheeDah0geetai0cieNgu1The2foQueeloochoH9eulizieshuf1nivohkied8jei5oph2Lajem6ohviijai6booTh8ienaic9eipheixa4ki1iek2pheihe7een7nei7epahngaerieghoe3ahbeil3yied0ievee1moh8jeeN5quoh6uiph6HaeZ0Eiyohshafohniewaer7gaegiefi5eiquiequoow5ohtheiw6ZeihieMoM8Ejoh7leiNeavi7uapheiwoophitoi3queiBeVeip5too8cah9Ohpaetaogahw1tei0eibuyaef3aht8aighuma6ahK4huP4cew6ohd0aiSh2umeeng7Hizahtoo6xoocePhu4ahtheex3jaijooph9iexaiqu3Nu0Ebeich6iTe",
|
||||||
|
year: 2004
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(500.0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
436
test/getAll.complex.test.js
Normal file
436
test/getAll.complex.test.js
Normal file
|
@ -0,0 +1,436 @@
|
||||||
|
/* eslint-disable jest/no-test-callback */
|
||||||
|
import { createBrands, createBrand, createCar, truncate } from "./utils/common";
|
||||||
|
|
||||||
|
import models from "../models";
|
||||||
|
import Cars from "../rules/Cars";
|
||||||
|
import Brands from "../rules/Brands";
|
||||||
|
import Middelware from "../index";
|
||||||
|
|
||||||
|
const createdCars = [];
|
||||||
|
|
||||||
|
describe("getAll (with inclusions)", () => {
|
||||||
|
beforeAll(done => {
|
||||||
|
createBrands(6, err => {
|
||||||
|
if (err) {
|
||||||
|
done(err);
|
||||||
|
} else {
|
||||||
|
createBrand((errBrand, createdBrand) => {
|
||||||
|
if (errBrand) {
|
||||||
|
done(errBrand);
|
||||||
|
} else {
|
||||||
|
createCar(createdBrand.id, true, 2004, (errCar, car) => {
|
||||||
|
if (errCar) {
|
||||||
|
done(errCar);
|
||||||
|
}
|
||||||
|
createdCars.push(car);
|
||||||
|
createCar(createdBrand.id, false, 2004, (errCar2, car2) => {
|
||||||
|
if (errCar2) {
|
||||||
|
done(errCar2);
|
||||||
|
}
|
||||||
|
createdCars.push(car2);
|
||||||
|
createCar(createdBrand.id, true, 2003, (errCar3, car3) => {
|
||||||
|
if (errCar3) {
|
||||||
|
done(errCar3);
|
||||||
|
}
|
||||||
|
createdCars.push(car3);
|
||||||
|
createCar(createdBrand.id, true, 1998, (errCar4, car4) => {
|
||||||
|
if (errCar4) {
|
||||||
|
done(errCar4);
|
||||||
|
}
|
||||||
|
createdCars.push(car4);
|
||||||
|
createCar(createdBrand.id, false, 1998, (errCar5, car5) => {
|
||||||
|
if (errCar5) {
|
||||||
|
done(errCar5);
|
||||||
|
}
|
||||||
|
createdCars.push(car5);
|
||||||
|
createBrands(2, errLastBrand => {
|
||||||
|
if (errLastBrand) {
|
||||||
|
done(errLastBrand);
|
||||||
|
} else {
|
||||||
|
models.Cars.create({
|
||||||
|
name: "la renault fuego",
|
||||||
|
year: 1980,
|
||||||
|
active: false
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done();
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterAll(done => {
|
||||||
|
truncate(["Brands", "Cars"], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return all items when allowed role call getAll without filters (except inactive)", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(3);
|
||||||
|
expect(res.total).toBe(3);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
expect(res.paging.first).toBeUndefined();
|
||||||
|
expect(res.paging.prev).toBeUndefined();
|
||||||
|
expect(res.paging.next).toBeUndefined();
|
||||||
|
expect(res.paging.last).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return all items when allowed role call getAll without filters", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(6);
|
||||||
|
expect(res.total).toBe(6);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
expect(res.paging.first).toBeUndefined();
|
||||||
|
expect(res.paging.prev).toBeUndefined();
|
||||||
|
expect(res.paging.next).toBeUndefined();
|
||||||
|
expect(res.paging.last).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 2 items when set complex filters", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
"year.lte": 2005,
|
||||||
|
"year.gte": 2004
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(2);
|
||||||
|
expect(res.total).toBe(2);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 1 item when set complex filters", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
"year.lte": 2005,
|
||||||
|
"year.gte": 2004
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(1);
|
||||||
|
expect(res.total).toBe(1);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 1 item when get all brands", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(9);
|
||||||
|
expect(res.total).toBe(9);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
expect(res.data[6].Cars.length).toBe(5);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 1 item when get all brands (without cars)", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(9);
|
||||||
|
expect(res.total).toBe(9);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
expect(res.data[6].Cars).not.toHaveProperty("Cars");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 3 items when admin send weirdfilter", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc",
|
||||||
|
weirdfilter: "letsgo"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(3);
|
||||||
|
expect(res.total).toBe(3);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return errorCode 406.1 when trying to send unallowed filter", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc",
|
||||||
|
id: 65
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(406.1);
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return one item when trying to get item with name contains 'renault'", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc",
|
||||||
|
"name.lk": "renault"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res.data.length).toBe(1);
|
||||||
|
expect(res.total).toBe(1);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return one item when trying to get item filtered by name", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc",
|
||||||
|
name: "la renault fuego"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res.data.length).toBe(1);
|
||||||
|
expect(res.total).toBe(1);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return empty data when trying to get item filtered by name", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
sort: "id",
|
||||||
|
order: "asc",
|
||||||
|
name: "Clio"
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeUndefined();
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
142
test/getAll.test.js
Normal file
142
test/getAll.test.js
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
/* eslint-disable jest/no-test-callback */
|
||||||
|
import { createBrands, truncate } from "./utils/common";
|
||||||
|
|
||||||
|
import models from "../models";
|
||||||
|
import Brands from "../rules/Brands";
|
||||||
|
import Cars from "../rules/Cars";
|
||||||
|
import Middelware from "../index";
|
||||||
|
|
||||||
|
describe("getAll", () => {
|
||||||
|
beforeAll(done => {
|
||||||
|
createBrands(40, done);
|
||||||
|
});
|
||||||
|
afterAll(done => {
|
||||||
|
truncate(["Brands", "Cars"], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return a 401 when guest tries to get all item", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: null,
|
||||||
|
params: {},
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(401.1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return a empty result allowed role get items from empty collection", async done => {
|
||||||
|
const middleware = new Middelware(Cars, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeUndefined();
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return all items when allowed role call getAll without filters", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(40);
|
||||||
|
expect(res.total).toBe(40);
|
||||||
|
expect(res.maxPage).toBe(1);
|
||||||
|
|
||||||
|
expect(res.paging.first).toBeUndefined();
|
||||||
|
expect(res.paging.prev).toBeUndefined();
|
||||||
|
expect(res.paging.next).toBeUndefined();
|
||||||
|
expect(res.paging.last).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return 5 items when allowed role call getAll with limit filter", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {
|
||||||
|
limit: 5,
|
||||||
|
page: 3
|
||||||
|
},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/?page=3&limit=5"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getAll(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
expect(res).toHaveProperty("data");
|
||||||
|
expect(res).toHaveProperty("paging");
|
||||||
|
expect(res).toHaveProperty("total");
|
||||||
|
expect(res).toHaveProperty("maxPage");
|
||||||
|
|
||||||
|
expect(res.data.length).toBe(5);
|
||||||
|
expect(res.total).toBe(40);
|
||||||
|
expect(res.maxPage).toBe(8);
|
||||||
|
|
||||||
|
expect(res.data[0]).toHaveProperty("id");
|
||||||
|
expect(res.data[0]).toHaveProperty("name");
|
||||||
|
expect(res.data[0]).not.toHaveProperty("createdAt");
|
||||||
|
expect(res.data[0]).not.toHaveProperty("created");
|
||||||
|
expect(res.data[0]).not.toHaveProperty("updatedAt");
|
||||||
|
expect(res.data[0]).not.toHaveProperty("updated");
|
||||||
|
|
||||||
|
expect(res.paging.first.href).toBe(
|
||||||
|
"http://internal.test/v1/?page=1&limit=5"
|
||||||
|
);
|
||||||
|
expect(res.paging.prev.href).toBe(
|
||||||
|
"http://internal.test/v1/?page=2&limit=5"
|
||||||
|
);
|
||||||
|
expect(res.paging.next.href).toBe(
|
||||||
|
"http://internal.test/v1/?page=4&limit=5"
|
||||||
|
);
|
||||||
|
expect(res.paging.last.href).toBe(
|
||||||
|
"http://internal.test/v1/?page=8&limit=5"
|
||||||
|
);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
166
test/getOne.test.js
Normal file
166
test/getOne.test.js
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
/* eslint-disable jest/no-test-callback */
|
||||||
|
import { createBrands, createCar, truncate } from "./utils/common";
|
||||||
|
|
||||||
|
import models from "../models";
|
||||||
|
import Brands from "../rules/Brands";
|
||||||
|
import Middelware from "../index";
|
||||||
|
|
||||||
|
const createdCars = [];
|
||||||
|
let createdBrand = {};
|
||||||
|
|
||||||
|
describe("getOne", () => {
|
||||||
|
beforeAll(done => {
|
||||||
|
createBrands(4, (err, res) => {
|
||||||
|
if (err) {
|
||||||
|
done(err);
|
||||||
|
} else {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
createdBrand = res[1];
|
||||||
|
createCar(createdBrand.id, true, 2004, (errCar, car) => {
|
||||||
|
if (errCar) {
|
||||||
|
done(errCar);
|
||||||
|
}
|
||||||
|
createdCars.push(car);
|
||||||
|
createCar(createdBrand.id, false, 2004, (errCar2, car2) => {
|
||||||
|
if (errCar2) {
|
||||||
|
done(errCar2);
|
||||||
|
}
|
||||||
|
createdCars.push(car2);
|
||||||
|
createCar(createdBrand.id, true, 2003, (errCar3, car3) => {
|
||||||
|
if (errCar3) {
|
||||||
|
done(errCar3);
|
||||||
|
}
|
||||||
|
createdCars.push(car3);
|
||||||
|
createCar(createdBrand.id, true, 1998, (errCar4, car4) => {
|
||||||
|
if (errCar4) {
|
||||||
|
done(errCar4);
|
||||||
|
}
|
||||||
|
createdCars.push(car4);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterAll(done => {
|
||||||
|
truncate(["Brands", "Cars"], done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return one item with missing part", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "user"
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
brandId: createdBrand.id
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getOne(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
|
||||||
|
expect(res.id).toBe(createdBrand.id);
|
||||||
|
expect(res.name).toBe(createdBrand.name);
|
||||||
|
expect(res).not.toHaveProperty("Cars");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return one item with all part", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
brandId: createdBrand.id
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getOne(req, (err, res) => {
|
||||||
|
expect(err).toBeNull();
|
||||||
|
|
||||||
|
expect(res.id).toBe(createdBrand.id);
|
||||||
|
expect(res.name).toBe(createdBrand.name);
|
||||||
|
expect(res).toHaveProperty("Cars");
|
||||||
|
expect(res.Cars.length).toBe(4);
|
||||||
|
expect(res.Cars[0]).toHaveProperty("id");
|
||||||
|
expect(res.Cars[0]).toHaveProperty("name");
|
||||||
|
expect(res.Cars[0]).toHaveProperty("year");
|
||||||
|
expect(res.Cars[0]).toHaveProperty("created");
|
||||||
|
expect(res.Cars[0]).toHaveProperty("updated");
|
||||||
|
expect(res.Cars[0]).not.toHaveProperty("createdAt");
|
||||||
|
expect(res.Cars[0]).not.toHaveProperty("updatedAt");
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return errorCode 406.1 when missing itemId", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getOne(req, (err, res) => {
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(406.1);
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("It should return empty result when item not found", async done => {
|
||||||
|
const middleware = new Middelware(Brands, models);
|
||||||
|
const req = {
|
||||||
|
method: "GET",
|
||||||
|
user: {
|
||||||
|
role: "admin"
|
||||||
|
},
|
||||||
|
params: {
|
||||||
|
brandId: createdBrand.id + 666
|
||||||
|
},
|
||||||
|
query: {},
|
||||||
|
protocol: "http",
|
||||||
|
get: () => {
|
||||||
|
return "internal.test/";
|
||||||
|
},
|
||||||
|
originalUrl: "v1/"
|
||||||
|
};
|
||||||
|
|
||||||
|
middleware.getOne(req, (err, res) => {
|
||||||
|
expect(parseFloat(err.errorCode)).toBe(404.0);
|
||||||
|
expect(res).toBeUndefined();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
88
test/utils/common.js
Normal file
88
test/utils/common.js
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import uuid from "uuid/v4";
|
||||||
|
import models from "../../models";
|
||||||
|
|
||||||
|
const _createCar = (brandId, active, year, done) => {
|
||||||
|
models.Cars.create({
|
||||||
|
name: uuid(),
|
||||||
|
year,
|
||||||
|
active,
|
||||||
|
brandId
|
||||||
|
})
|
||||||
|
.then(item => {
|
||||||
|
done(null, item);
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _createBrand = done => {
|
||||||
|
models.Brands.create({
|
||||||
|
name: uuid()
|
||||||
|
})
|
||||||
|
.then(item => {
|
||||||
|
done(null, item);
|
||||||
|
})
|
||||||
|
.catch(done);
|
||||||
|
};
|
||||||
|
|
||||||
|
const _createBrands = (total, done) => {
|
||||||
|
const created = [];
|
||||||
|
const next = () => {
|
||||||
|
if (total === created.length) {
|
||||||
|
done(null, created);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (let i = 0; i < total; i += 1) {
|
||||||
|
_createBrand((err, res) => {
|
||||||
|
if (!err) {
|
||||||
|
created.push(res);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const _truncateCars = (tables, done) => {
|
||||||
|
if (tables.indexOf("Cars") === -1) {
|
||||||
|
done(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
models.Cars.destroy({
|
||||||
|
where: {}
|
||||||
|
})
|
||||||
|
.then(() => done(null))
|
||||||
|
.catch(err => done(err));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const _truncateBrands = (tables, done) => {
|
||||||
|
if (tables.indexOf("Brands") === -1) {
|
||||||
|
done(null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
models.Brands.destroy({
|
||||||
|
where: {}
|
||||||
|
})
|
||||||
|
.then(() => done(null))
|
||||||
|
.catch(err => done(err));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const _truncate = (tables, done) => {
|
||||||
|
_truncateCars(tables, errCars => {
|
||||||
|
if (errCars) {
|
||||||
|
done(errCars);
|
||||||
|
} else {
|
||||||
|
_truncateBrands(tables, errBrands => {
|
||||||
|
done(errBrands);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createBrand = _createBrand;
|
||||||
|
export const createBrands = _createBrands;
|
||||||
|
export const createCar = _createCar;
|
||||||
|
export const truncate = _truncate;
|
5
test/utils/setup.js
Normal file
5
test/utils/setup.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
jest.setTimeout(3000);
|
||||||
|
|
||||||
|
afterAll(done => {
|
||||||
|
done();
|
||||||
|
});
|
Loading…
Reference in a new issue