Added some new tests

This commit is contained in:
dbroqua 2020-02-13 22:36:14 +01:00
parent 0039a8b4ec
commit 918f873fea
15 changed files with 887 additions and 114 deletions

View file

@ -21,6 +21,7 @@ testing:
script: script:
- yarn install - yarn install
- ./node_modules/.bin/sequelize db:migrate - ./node_modules/.bin/sequelize db:migrate
- ./node_modules/.bin/sequelize db:seed:all
- yarn test --ci --collectCoverage=true --coverage - yarn test --ci --collectCoverage=true --coverage
coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/ coverage: /All files[^|]*\|[^|]*\s+([\d\.]+)/
artifacts: artifacts:

View file

@ -22,20 +22,20 @@ class Middleware extends QueryBuilder {
*/ */
createOne(req, callback) { createOne(req, callback) {
// On test les droits // On test les droits
if (!this._haveRight(req)) { if (!this.haveRight(req)) {
callback(new ErrorBuilder(401.1, "You're not allowed")); callback(new ErrorBuilder(401.1, "You're not allowed"));
return false; return false;
} }
// On teste le body // On teste le body
this._checkCreateOneValues(req, (err, value) => { this.checkCreateOneValues(req, (err, value) => {
// Il manque un paramètre dans le body // Il manque un paramètre dans le body
if (err) { if (err) {
callback(err, value); callback(err, value);
return false; return false;
} }
this._insertItem(req, value, callback); this.insertItem(req, value, callback);
return true; return true;
}); });
return true; return true;
@ -50,7 +50,7 @@ class Middleware extends QueryBuilder {
// On spécifie le type de requête (list ou item) // On spécifie le type de requête (list ou item)
req.getType = "list"; req.getType = "list";
this._createQuery(req, (err, query, limit) => { this.createQuery(req, (err, query, limit) => {
if (err) { if (err) {
callback(err, query); callback(err, query);
return false; return false;
@ -82,8 +82,8 @@ class Middleware extends QueryBuilder {
const values = rows.slice(limit.start, limit.start + limit.limit); const values = rows.slice(limit.start, limit.start + limit.limit);
callback(null, { callback(null, {
data: this._formatItems(req, values), data: this.formatItems(req, values),
...QueryBuilder._createPagination( ...QueryBuilder.createPagination(
req, req,
total, total,
limit.limit, limit.limit,
@ -108,7 +108,7 @@ class Middleware extends QueryBuilder {
// On spécifie le type de requête (list ou item) // On spécifie le type de requête (list ou item)
req.getType = "item"; req.getType = "item";
this._createQuery(req, (err, query) => { this.createQuery(req, (err, query) => {
if (err) { if (err) {
callback(err, query); callback(err, query);
return false; return false;
@ -122,13 +122,16 @@ class Middleware extends QueryBuilder {
return false; return false;
} }
let formatedItem = item; let formatedItem = item;
if (this.params.format) { if (req.method === "GET") {
const formatRules = this.params.format[req.user.role]; if (this.params.format) {
const formatRules = this.params.format[req.user.role];
if (formatRules) { if (formatRules) {
formatedItem = this._formatItem(item, formatRules); formatedItem = this.formatItem(item, formatRules);
}
} }
} }
callback(null, formatedItem); callback(null, formatedItem);
return true; return true;
}) })
@ -145,7 +148,7 @@ class Middleware extends QueryBuilder {
*/ */
patchOne(req, callback) { patchOne(req, callback) {
// On test les droits // On test les droits
if (!this._haveRight(req)) { if (!this.haveRight(req)) {
callback(new ErrorBuilder(401.1, "You're not allowed")); callback(new ErrorBuilder(401.1, "You're not allowed"));
return false; return false;
} }
@ -170,7 +173,45 @@ class Middleware extends QueryBuilder {
item item
.update(value) .update(value)
.then(updated => { .then(updated => {
callback(null, updated); if (this.params.belongsToMany) {
const needCreate = [];
let created = 0;
const _next = errNext => {
if (errNext) {
// TODO: break and revert
} else {
created += 1;
if (created === needCreate.length) {
callback(null, updated);
}
}
};
Object.keys(this.params.belongsToMany).map(key => {
if (value[key]) {
needCreate.push(key);
}
return true;
});
if (needCreate.length > 0) {
for (let i = 0; i < needCreate.length; i += 1) {
const currentKey = needCreate[i];
const currentValue = value[currentKey];
this.createBelonsTo(
updated,
currentKey,
currentValue,
"add",
_next
);
}
} else {
callback(null, updated);
}
} else {
callback(null, updated);
}
}) })
.catch(callback); .catch(callback);
return true; return true;
@ -186,7 +227,7 @@ class Middleware extends QueryBuilder {
*/ */
deleteOne(req, callback) { deleteOne(req, callback) {
// On test les droits // On test les droits
if (!this._haveRight(req)) { if (!this.haveRight(req)) {
callback(new ErrorBuilder(401.1, "You're not allowed")); callback(new ErrorBuilder(401.1, "You're not allowed"));
return false; return false;
} }
@ -194,16 +235,53 @@ class Middleware extends QueryBuilder {
this.getOne(req, (err, item) => { this.getOne(req, (err, item) => {
if (err) { if (err) {
callback(err, item); callback(err, item);
return false; } else {
} let deletedBelongs = 0;
const needToDeleteBelongs = [];
item const _deleteItem = () => {
.destroy() item
.then(() => { .destroy()
callback(null, {}); .then(() => {
}) callback(null, {});
.catch(callback); })
return true; .catch(callback);
};
const _next = () => {
deletedBelongs += 1;
if (deletedBelongs === needToDeleteBelongs.length) {
_deleteItem();
}
};
if (this.params.belongsToMany) {
Object.keys(this.params.belongsToMany).map(key => {
const collection = this.params.belongsToMany[key];
if (item[collection].length) {
needToDeleteBelongs.push(key);
}
return true;
});
if (needToDeleteBelongs.length > 0) {
for (let i = 0; i < needToDeleteBelongs.length; i += 1) {
const currentKey = needToDeleteBelongs[i];
const collection = this.params.belongsToMany[currentKey];
const valuesToDelete = [];
for (let j = 0; j < item[collection].length; j += 1) {
valuesToDelete.push(item[collection][j].id);
}
this.deleteBelongsTo(item, currentKey, valuesToDelete, _next);
}
} else {
_deleteItem();
}
} else {
_deleteItem();
}
}
}); });
return true; return true;
} }

View file

@ -22,7 +22,7 @@ class QueryBuilder {
* @param {Number} maxPage * @param {Number} maxPage
* @param {String} page * @param {String} page
*/ */
static _createLink(href, currentPage, maxPage, page) { static createLink(href, currentPage, maxPage, page) {
if ( if (
(page === "first" && currentPage > 1) || (page === "first" && currentPage > 1) ||
(page === "last" && currentPage < maxPage) || (page === "last" && currentPage < maxPage) ||
@ -61,17 +61,17 @@ class QueryBuilder {
* @param {Number} skip * @param {Number} skip
* @return {Object} * @return {Object}
*/ */
static _createPagination(req, total, limit, skip) { static createPagination(req, total, limit, skip) {
const maxPage = Math.ceil(total / limit); // Nombre total de pages const maxPage = Math.ceil(total / limit); // Nombre total de pages
const currentPage = Math.ceil(skip / limit) + 1; // Numéro de la page courante const currentPage = Math.ceil(skip / limit) + 1; // Numéro de la page courante
const href = `${req.protocol}://${req.get("host")}${req.originalUrl}`; // Lien vers la page actuelle const href = `${req.protocol}://${req.get("host")}${req.originalUrl}`; // Lien vers la page actuelle
return { return {
paging: { paging: {
first: QueryBuilder._createLink(href, currentPage, maxPage, "first"), first: QueryBuilder.createLink(href, currentPage, maxPage, "first"),
prev: QueryBuilder._createLink(href, currentPage, maxPage, "prev"), prev: QueryBuilder.createLink(href, currentPage, maxPage, "prev"),
next: QueryBuilder._createLink(href, currentPage, maxPage, "next"), next: QueryBuilder.createLink(href, currentPage, maxPage, "next"),
last: QueryBuilder._createLink(href, currentPage, maxPage, "last") last: QueryBuilder.createLink(href, currentPage, maxPage, "last")
}, },
total, total,
maxPage maxPage
@ -83,7 +83,7 @@ class QueryBuilder {
* @param {Object} obj * @param {Object} obj
* @return {Object} * @return {Object}
*/ */
_replaceKeys(obj) { replaceKeys(obj) {
const { Op } = this.models.Sequelize; const { Op } = this.models.Sequelize;
let newObject = {}; let newObject = {};
@ -91,13 +91,13 @@ class QueryBuilder {
newObject = []; newObject = [];
for (let i = 0; i < obj.length; i += 1) { for (let i = 0; i < obj.length; i += 1) {
const value = const value =
typeof obj[i] === "object" ? this._replaceKeys(obj[i]) : obj[i]; typeof obj[i] === "object" ? this.replaceKeys(obj[i]) : obj[i];
newObject.push(value); newObject.push(value);
} }
} else { } else {
Object.keys(obj).map(key => { Object.keys(obj).map(key => {
const value = const value =
typeof obj[key] === "object" ? this._replaceKeys(obj[key]) : obj[key]; typeof obj[key] === "object" ? this.replaceKeys(obj[key]) : obj[key];
if (key.indexOf("$") === 0 && key.slice(-1) !== "$") { if (key.indexOf("$") === 0 && key.slice(-1) !== "$") {
switch (key) { switch (key) {
case "$or": case "$or":
@ -129,8 +129,6 @@ class QueryBuilder {
}); });
} }
console.log("newObject:", newObject);
return newObject; return newObject;
} }
@ -139,7 +137,7 @@ class QueryBuilder {
* @param {Object} req * @param {Object} req
* @return {Boolean} * @return {Boolean}
*/ */
_haveRight(req) { haveRight(req) {
let allowedRole = "all"; let allowedRole = "all";
if (!req.user) { if (!req.user) {
req.user = {}; req.user = {};
@ -173,7 +171,7 @@ class QueryBuilder {
* @param {Object} req * @param {Object} req
* @return {Object} * @return {Object}
*/ */
_restrictOn(req) { restrictOn(req) {
const where = {}; const where = {};
if (!this.params || !this.params.restrictOn) { if (!this.params || !this.params.restrictOn) {
return where; return where;
@ -220,11 +218,15 @@ class QueryBuilder {
* @param {String} method * @param {String} method
* @return {Object} * @return {Object}
*/ */
_override(req, method) { override(req, method) {
let override = {}; let overrideValues = {};
const params = this.params.override ? this.params.override[method] : {};
if (!this.params.override) { if (!this.params.override) {
return override; return overrideValues;
}
const params = this.params.override ? this.params.override[method] : {};
if (!params) {
return overrideValues;
} }
// On surcharge certains paramètres passé en query // On surcharge certains paramètres passé en query
@ -239,7 +241,10 @@ class QueryBuilder {
value value
) )
); );
override = Object.assign(override, this._replaceKeys(query)); overrideValues = Object.assign(
overrideValues,
this.replaceKeys(query)
);
} }
return true; return true;
}); });
@ -249,12 +254,12 @@ class QueryBuilder {
if (params.params) { if (params.params) {
for (let i = 0; i < params.params.length; i += 1) { for (let i = 0; i < params.params.length; i += 1) {
const currentParam = params.params[i]; const currentParam = params.params[i];
override[currentParam.append] = overrideValues[currentParam.append] =
req[currentParam.from][currentParam.value]; req[currentParam.from][currentParam.value];
} }
} }
return override; return overrideValues;
} }
/** /**
@ -263,7 +268,7 @@ class QueryBuilder {
* @param {Array} include * @param {Array} include
* @return {Mixed} * @return {Mixed}
*/ */
_setInclusions(req, include) { setInclusions(req, include) {
if (!req.user) { if (!req.user) {
req.user = {}; req.user = {};
} }
@ -299,10 +304,7 @@ class QueryBuilder {
currentInclude.where = {}; currentInclude.where = {};
if (current.include) { if (current.include) {
currentInclude.include = this._setInclusions( currentInclude.include = this.setInclusions(req, current.include);
req,
current.include
);
} }
// On parcours la liste des règles d'inclusion pour ce modèle // On parcours la liste des règles d'inclusion pour ce modèle
@ -338,14 +340,55 @@ class QueryBuilder {
return true; return true;
} }
/**
* Fonction permettant de peupler une table de jointure (belongsToMany)
* @param {Object} item
* @param {String} key
* @param {Array} values
* @param {String} mode
* @param {Function} callback
*/
createBelonsTo(item, key, values, mode, callback) {
const collection = this.params.belongsToMany[key];
const action = `${mode}${collection}`;
item[action](values)
.then(() => {
callback();
})
.catch(callback);
}
deleteBelongsTo(item, key, values, callback) {
const collection = this.params.belongsToMany[key];
const action = `remove${collection}`;
item[action](values)
.then(() => {
callback();
})
.catch(callback);
}
/** /**
* Méthode interne permettant de créer un item * Méthode interne permettant de créer un item
* @param {Object} req * @param {Object} req
* @param {Object} value * @param {Object} value
* @param {Function} callback * @param {Function} callback
*/ */
_insertItem(req, value, callback) { insertItem(req, value, callback) {
const values = {}; const values = {};
const _done = item => {
let createdItem = item;
if (this.params.format) {
const formatRules = req.user ? this.params.format[req.user.role] : null;
if (formatRules) {
createdItem = this.formatItem(item, formatRules);
}
}
callback(null, createdItem);
};
// On converti les 'null' en null (formData par exemple) // On converti les 'null' en null (formData par exemple)
Object.keys(value).map(key => { Object.keys(value).map(key => {
if (value[key] === "null") { if (value[key] === "null") {
@ -360,17 +403,44 @@ class QueryBuilder {
this.models[this.params.model] this.models[this.params.model]
.create(values) .create(values)
.then(item => { .then(item => {
let createdItem = item; if (this.params.belongsToMany) {
if (this.params.format) { const needCreate = [];
const formatRules = req.user let created = 0;
? this.params.format[req.user.role] const _next = err => {
: null; if (err) {
// TODO: break and revert
} else {
created += 1;
if (formatRules) { if (created === needCreate.length) {
createdItem = this._formatItem(item, formatRules); _done(item);
}
}
};
Object.keys(this.params.belongsToMany).map(key => {
if (values[key]) {
needCreate.push(key);
}
return true;
});
if (needCreate.length > 0) {
for (let i = 0; i < needCreate.length; i += 1) {
const currentKey = needCreate[i];
this.createBelonsTo(
item,
currentKey,
values[currentKey],
"set",
_next
);
}
} else {
_done(item);
} }
} else {
_done(item);
} }
callback(null, createdItem);
}) })
.catch(err => { .catch(err => {
switch (err.name) { switch (err.name) {
@ -392,10 +462,11 @@ class QueryBuilder {
* @param {Object} formatRule * @param {Object} formatRule
* @return {Object} * @return {Object}
*/ */
_formatItem(item, formatRule) { formatItem(item, formatRule) {
const formated = {}; const formated = {};
Object.keys(formatRule).map(key => { Object.keys(formatRule).map(key => {
// TODO: revoir ce switch
switch (typeof formatRule[key]) { switch (typeof formatRule[key]) {
case "string": case "string":
if (item) { if (item) {
@ -407,11 +478,11 @@ class QueryBuilder {
formated[key] = []; formated[key] = [];
for (let i = 0; i < item[key].length; i += 1) { for (let i = 0; i < item[key].length; i += 1) {
formated[key].push( formated[key].push(
this._formatItem(item[key][i], formatRule[key]) this.formatItem(item[key][i], formatRule[key])
); );
} }
} else { } else {
formated[key] = this._formatItem(item[key], formatRule[key]); formated[key] = this.formatItem(item[key], formatRule[key]);
} }
break; break;
default: default:
@ -429,7 +500,7 @@ class QueryBuilder {
* @param {Object} items * @param {Object} items
* @return {Object} * @return {Object}
*/ */
_formatItems(req, items) { formatItems(req, items) {
if (!this.params.format) { if (!this.params.format) {
return items; return items;
} }
@ -442,7 +513,7 @@ class QueryBuilder {
const formated = []; const formated = [];
for (let i = 0; i < items.length; i += 1) { for (let i = 0; i < items.length; i += 1) {
formated.push(this._formatItem(items[i], formatRules)); formated.push(this.formatItem(items[i], formatRules));
} }
return formated; return formated;
@ -453,7 +524,7 @@ class QueryBuilder {
* @param {Object} req * @param {Object} req
* @param {Function} callback * @param {Function} callback
*/ */
_checkCreateOneValues(req, callback) { checkCreateOneValues(req, callback) {
// On regarde s'il faut surcharger les valeurs du body avec des valeurs dérivées (req.user, req.params...) // On regarde s'il faut surcharger les valeurs du body avec des valeurs dérivées (req.user, req.params...)
if (this.params.override && this.params.override.create) { if (this.params.override && this.params.override.create) {
if (this.params.override.create.body) { if (this.params.override.create.body) {
@ -482,14 +553,14 @@ class QueryBuilder {
* @param {Function} callback * @param {Function} callback
* @return {Boolean} * @return {Boolean}
*/ */
_createQuery(req, callback) { createQuery(req, callback) {
this._setInclusions(req); this.setInclusions(req);
const query = {}; const query = {};
let where = {}; let where = {};
let order = []; let order = [];
// On test les droits // On test les droits
if (!this._haveRight(req)) { if (!this.haveRight(req)) {
callback(new ErrorBuilder(401.1, "You're not allowed")); callback(new ErrorBuilder(401.1, "You're not allowed"));
return false; return false;
} }
@ -564,11 +635,11 @@ class QueryBuilder {
} }
// S'il y a des restrictions (genre un utilisateur n'a le droit de voir que tel ou tel items) // S'il y a des restrictions (genre un utilisateur n'a le droit de voir que tel ou tel items)
const restrict = this._restrictOn(req); const restrict = this.restrictOn(req);
where = Object.assign(where, restrict); where = Object.assign(where, restrict);
// On regarde s'il n'y a pas des valeurs à overrider // On regarde s'il n'y a pas des valeurs à overrider
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 (order) { if (order) {

View file

@ -0,0 +1,26 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable("Colors", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: queryInterface => {
return queryInterface.dropTable("Colors");
}
};

View file

@ -0,0 +1,39 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable("CarsColors", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
CarId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: "Cars",
key: "id"
}
},
ColorId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: "Colors",
key: "id"
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: queryInterface => {
return queryInterface.dropTable("CarsColors");
}
};

View file

@ -0,0 +1,15 @@
module.exports = {
up: queryInterface => {
return queryInterface.addConstraint("CarsColors", ["CarId", "ColorId"], {
type: "unique",
name: "CarsColors_unique_carId_colorId"
});
},
down: queryInterface => {
return queryInterface.removeConstraint(
"CarsColors",
"CarsColors_unique_carId_colorId"
);
}
};

View file

@ -21,6 +21,7 @@ module.exports = (sequelize, DataTypes) => {
as: "Brand", as: "Brand",
foreignKey: "brandId" foreignKey: "brandId"
}); });
Cars.belongsToMany(models.Colors, { through: models.CarsColors });
}; };
return Cars; return Cars;

24
models/carscolors.js Normal file
View file

@ -0,0 +1,24 @@
module.exports = (sequelize, DataTypes) => {
const CarsColors = sequelize.define(
"CarsColors",
{
CarId: {
type: DataTypes.INTEGER,
references: {
model: "Cars",
key: "id"
}
},
ColorId: {
type: DataTypes.INTEGER,
references: {
model: "Colors",
key: "id"
}
}
},
{}
);
return CarsColors;
};

11
models/colors.js Normal file
View file

@ -0,0 +1,11 @@
module.exports = (sequelize, DataTypes) => {
const Colors = sequelize.define(
"Colors",
{
name: DataTypes.STRING
},
{}
);
return Colors;
};

View file

@ -12,10 +12,9 @@ const Rules = {
{ {
collection: "Brand", collection: "Brand",
requiredRole: ["admin", "user"] requiredRole: ["admin", "user"]
// }, },
// { {
// collection: "Colors", collection: "Colors"
// requiredRole: ["admin", "user"]
} }
], ],
format: { format: {
@ -31,6 +30,7 @@ const Rules = {
validate: { validate: {
create: Joi.object({ create: Joi.object({
name: Joi.string().required(), name: Joi.string().required(),
colorsId: Joi.array().items(Joi.number().integer()),
active: Joi.boolean(), active: Joi.boolean(),
year: Joi.number() year: Joi.number()
.integer() .integer()
@ -38,7 +38,9 @@ const Rules = {
brandId: Joi.number().integer() brandId: Joi.number().integer()
}), }),
update: Joi.object({ update: Joi.object({
name: Joi.string() name: Joi.string(),
year: Joi.number().integer(),
colorsId: Joi.array().items(Joi.number().integer())
}), }),
item: Joi.object({ item: Joi.object({
carId: Joi.number().required() carId: Joi.number().required()
@ -119,6 +121,9 @@ const Rules = {
} }
} }
} }
},
belongsToMany: {
colorsId: "Colors"
} }
}; };

View file

@ -0,0 +1,47 @@
module.exports = {
up: queryInterface => {
return queryInterface.bulkInsert(
"Colors",
[
{
name: "Red",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "Miami blue",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "Sirius yellow",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "Sunflower yellow",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "Royal blue",
createdAt: new Date(),
updatedAt: new Date()
},
{
name: "White",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: queryInterface => {
return Promise.all([
queryInterface.bulkDelete("CarsColors", null, {}),
queryInterface.bulkDelete("Colors", null, {})
]);
}
};

286
test/belongsToMany.test.js Normal file
View file

@ -0,0 +1,286 @@
/* eslint-disable jest/no-test-callback */
import uuid from "uuid/v4";
import { truncate } from "./utils/common";
import models from "../models";
import Cars from "../rules/Cars";
import Middelware from "../index";
const { Op } = models.Sequelize;
let colors = [];
const colorsId = [];
let car = {};
describe("createOne", () => {
beforeAll(done => {
models.Colors.findAll({
where: {
[Op.or]: [
{
name: "White"
},
{
name: "Miami blue"
},
{
name: "Sunflower yellow"
}
]
}
})
.then(items => {
colors = items;
colorsId.push(items[0].id);
colorsId.push(items[1].id);
done();
})
.catch(done);
});
afterAll(done => {
truncate(["Cars"], done);
});
test("It should return new item after creation", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "POST",
user: {
role: "admin"
},
body: {
name: uuid(),
year: 2004,
active: true,
colorsId
}
};
middleware.createOne(req, (err, res) => {
expect(err).toBeNull();
expect(res.name).toBe(req.body.name);
expect(res.year).toBe(2004);
expect(res.active).toBe(true);
car = res;
done();
});
});
test("It should return item with relations", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "POST",
user: {
role: "admin"
},
params: {
carId: car.id
}
};
middleware.getOne(req, (err, res) => {
expect(err).toBeNull();
expect(res.name).toBe(car.name);
expect(res.Colors.length).toBe(2);
expect(res.year).toBe(2004);
expect(res.Colors[0].id).toBe(colorsId[0]);
expect(res.Colors[1].id).toBe(colorsId[1]);
done();
});
});
test("It should return all items ", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "POST",
user: {
role: "admin"
},
params: {},
query: {},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.getAll(req, (err, res) => {
expect(err).toBeNull();
expect(res.data[0].name).toBe(car.name);
expect(res.data[0].Colors.length).toBe(2);
expect(res.data[0].Colors[0].id).toBe(colorsId[0]);
expect(res.data[0].Colors[1].id).toBe(colorsId[1]);
done();
});
});
test("It should return ok when update item", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "PATCH",
user: {
role: "admin"
},
params: {
carId: car.id
},
query: {},
body: {
year: 1998,
colorsId: [colors[2].id]
},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(err).toBeNull();
expect(res.name).toBe(car.name);
expect(res.year).toBe(1998);
done();
});
});
test("It should return updated item", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "PATCH",
user: {
role: "admin"
},
params: {
carId: car.id
},
query: {},
body: {},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.getOne(req, (err, res) => {
expect(err).toBeNull();
expect(res.name).toBe(car.name);
expect(res.year).toBe(1998);
expect(res.Colors.length).toBe(3);
done();
});
});
test("It should return empty object after delete", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "DELETE",
user: {
role: "admin"
},
params: {
carId: car.id
},
query: {},
body: {},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.deleteOne(req, (err, res) => {
expect(err).toBeNull();
expect(res).toEqual({});
models.CarsColors.findAll({
where: {
CarId: car.id
}
})
.then(carsColors => {
expect(carsColors.length).toBe(0);
models.Cars.findAll({
where: {
id: car.id
}
}).then(items => {
expect(items.length).toBe(0);
done();
});
})
.catch(done);
});
});
test("It should return empty item when delete item with empty belongsTo collection", async done => {
const middleware = new Middelware(Cars, models);
const req = {
method: "POST",
user: {
role: "admin"
},
body: {
name: uuid(),
year: 2004,
active: true
}
};
middleware.createOne(req, (err, newCar) => {
expect(err).toBeNull();
expect(newCar.name).toBe(req.body.name);
expect(newCar.year).toBe(2004);
expect(newCar.active).toBe(true);
const reqItem = {
method: "PATCH",
user: {
role: "admin"
},
params: {
carId: newCar.id
},
query: {},
body: {
year: 1998
},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(reqItem, errPatch => {
expect(errPatch).toBeNull();
reqItem.method = "DELETE";
middleware.deleteOne(reqItem, errDelete => {
expect(errDelete).toBeNull();
done();
});
});
});
});
});

163
test/patchOne.test.js Normal file
View file

@ -0,0 +1,163 @@
/* eslint-disable jest/no-test-callback */
import { createBrand, truncate } from "./utils/common";
import models from "../models";
import Brands from "../rules/Brands";
import Middelware from "../index";
let brand = {};
describe("patchOne", () => {
beforeAll(done => {
createBrand((err, item) => {
if (err) {
done(err);
} else {
brand = item;
done();
}
});
});
afterAll(done => {
truncate(["Brands"], done);
});
test("It should return 401.1 if guest tries to update item", async done => {
const middleware = new Middelware(Brands, models);
const req = {
method: "PATCH",
user: null,
params: {
brandId: brand.id
},
query: {},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(res).toBeUndefined();
expect(parseFloat(err.errorCode)).toBe(401.1);
done();
});
});
test("It should return 401.1 if bad role tries to update item", async done => {
const middleware = new Middelware(Brands, models);
const req = {
method: "PATCH",
user: {
role: "user"
},
params: {
brandId: brand.id
},
query: {},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(res).toBeUndefined();
expect(parseFloat(err.errorCode)).toBe(401.1);
done();
});
});
test("It should return 406.1 if allowed user tries to patch item with unallowed values", async done => {
const middleware = new Middelware(Brands, models);
const req = {
method: "PATCH",
user: {
role: "admin"
},
params: {
brandId: brand.id
},
query: {},
body: {
created: "test",
name: "TEST"
},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(res).toBeUndefined();
expect(parseFloat(err.errorCode)).toBe(406.1);
done();
});
});
test("It should return updated item if allowed user patch item", async done => {
const middleware = new Middelware(Brands, models);
const req = {
method: "PATCH",
user: {
role: "admin"
},
params: {
brandId: brand.id
},
query: {},
body: {
name: "TEST"
},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(err).toBeNull();
expect(res.name).toBe("TEST");
done();
});
});
test("It should return 404.1 if allowed user tries to patch not found item", async done => {
const middleware = new Middelware(Brands, models);
const req = {
method: "PATCH",
user: {
role: "admin"
},
params: {
brandId: brand.id + 1
},
query: {},
body: {
name: "TEST"
},
protocol: "http",
get: () => {
return "internal.test/";
},
originalUrl: "v1/"
};
middleware.patchOne(req, (err, res) => {
expect(res).toBeUndefined();
expect(parseInt(err.errorCode, 10)).toBe(404);
done();
});
});
});

View file

@ -1,5 +1,6 @@
import uuid from "uuid/v4"; import uuid from "uuid/v4";
import models from "../../models"; import models from "../../models";
import truncateDefault from "./truncate";
const _createCar = (brandId, active, year, done) => { const _createCar = (brandId, active, year, done) => {
models.Cars.create({ models.Cars.create({
@ -42,47 +43,7 @@ const _createBrands = (total, done) => {
} }
}; };
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 createBrand = _createBrand;
export const createBrands = _createBrands; export const createBrands = _createBrands;
export const createCar = _createCar; export const createCar = _createCar;
export const truncate = _truncate; export const truncate = truncateDefault;

45
test/utils/truncate.js Normal file
View file

@ -0,0 +1,45 @@
import models from "../../models";
const _truncateCars = (tables, done) => {
if (tables.indexOf("Cars") === -1) {
done(null);
return true;
}
models.CarsColors.destroy({
where: {}
})
.then(() => {
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;
};
export default (tables, done) => {
_truncateCars(tables, errCars => {
if (errCars) {
done(errCars);
} else {
_truncateBrands(tables, errBrands => {
done(errBrands);
});
}
});
};