Added some tests (createOne and getAll)

This commit is contained in:
dbroqua 2020-02-11 15:38:28 +01:00
parent ef7ca0315b
commit 9f0886541c
21 changed files with 2940 additions and 67 deletions

30
.babelrc Normal file
View 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
View file

@ -20,6 +20,7 @@ coverage
# nyc test coverage
.nyc_output
/reports/
junit.xml
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

10
config/config.json Normal file
View 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
View 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

View file

@ -9,8 +9,8 @@ class Middleware extends QueryBuilder {
*/
constructor(params, models) {
super(params, models);
this.params = params || {};
this.models = models || {};
this.params = params;
this.models = models;
this.includes = [];
}
@ -157,7 +157,7 @@ class Middleware extends QueryBuilder {
// Un paramètre n'est pas bon dans les params
if (error) {
callback(new ErrorBuilder(406, error));
callback(new ErrorBuilder(406.1, error));
return false;
}

View file

@ -30,6 +30,7 @@ class QueryBuilder {
(page === "next" && currentPage < maxPage)
) {
let newIndex = 0;
// eslint-disable-next-line default-case
switch (page) {
case "first":
newIndex = 1;
@ -43,9 +44,6 @@ class QueryBuilder {
case "next":
newIndex = currentPage + 1;
break;
default:
newIndex = currentPage;
break;
}
return {
href: href.replace(`page=${currentPage}`, `page=${newIndex}`),
@ -117,6 +115,9 @@ class QueryBuilder {
case "$in":
newObject[Op.in] = value.split(",");
break;
case "$like":
newObject[Op.like] = value;
break;
default:
newObject[key] = [value];
break;
@ -128,6 +129,8 @@ class QueryBuilder {
});
}
console.log("newObject:", newObject);
return newObject;
}
@ -138,6 +141,12 @@ class QueryBuilder {
*/
_haveRight(req) {
let allowedRole = "all";
if (!req.user) {
req.user = {};
}
if (!req.user.role) {
req.user.role = "guest";
}
// eslint-disable-next-line default-case
switch (req.method) {
@ -218,10 +227,6 @@ class QueryBuilder {
return override;
}
if (!params) {
return override;
}
// On surcharge certains paramètres passé en query
if (params.filters) {
Object.keys(params.filters).map(column => {
@ -259,6 +264,13 @@ class QueryBuilder {
* @return {Mixed}
*/
_setInclusions(req, include) {
if (!req.user) {
req.user = {};
}
if (!req.user.role) {
req.user.role = "guest";
}
const includes = [];
const listOfIncludes = include || this.params.includes;
@ -361,12 +373,16 @@ class QueryBuilder {
callback(null, createdItem);
})
.catch(err => {
if (err.name === "SequelizeUniqueConstraintError") {
callback(new ErrorBuilder(409, "Duplicate item"));
return false;
switch (err.name) {
case "SequelizeUniqueConstraintError":
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) {
const formated = {};
Object.keys(formatRule).map(key => {
switch (typeof formatRule[key]) {
case "string":
formated[key] = item[key] || null;
if (item) {
formated[key] = item[key] || null;
}
break;
case "object":
if (Array.isArray(item[key])) {
@ -451,7 +470,7 @@ class QueryBuilder {
});
if (error) {
callback(new ErrorBuilder(406, error));
callback(new ErrorBuilder(406.1, error));
} else {
callback(null, value);
}
@ -471,16 +490,32 @@ class QueryBuilder {
// On test les droits
if (!this._haveRight(req)) {
callback(new ErrorBuilder(401, "You're not allowed"));
callback(new ErrorBuilder(401.1, "You're not allowed"));
return false;
}
// On teste la query (ou les 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[
req.getType
].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
let listOfIgnoredFilters = [
"limit",
@ -512,17 +547,11 @@ class QueryBuilder {
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") {
// Aucune pagination n'est passée, on set celle par défaut
if (!value.page || !value.limit) {
value.page = 1;
value.limit = 20;
value.limit = 50;
}
// Un tri est spécifié
@ -542,11 +571,6 @@ class QueryBuilder {
const override = this._override(req, req.getType);
where = Object.assign(where, override);
// if ( value.page) {
// query.offset = ( value.page - 1 ) * value.limit;
// query.limit = value.limit;
// }
if (order) {
query.order = order;
}
@ -560,11 +584,6 @@ class QueryBuilder {
// Hack pour faire un recherche dans les nested de type hasMany
query.subQuery = false;
if (!value.page || !value.limit) {
value.page = 1;
value.limit = 50;
}
callback(null, query, {
start: value.page * value.limit - value.limit,
limit: value.limit

View 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");
}
};

View 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
View 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
View 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
View 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;

View file

@ -5,8 +5,7 @@
"main": "index.js",
"scripts": {
"lint": "./node_modules/.bin/eslint . --fix",
"test": "jest --forceExit --detectOpenHandles --maxWorkers=10",
"coverage": "jest --coverage"
"test": "jest --forceExit --detectOpenHandles --maxWorkers=10"
},
"repository": {
"type": "git",
@ -30,6 +29,9 @@
]
},
"jest": {
"moduleFileExtensions": [
"js"
],
"verbose": true,
"roots": [
"<rootDir>",
@ -69,6 +71,10 @@
},
"dependencies": {},
"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",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.0.0",
@ -79,9 +85,15 @@
"husky": "^4.2.1",
"jest": "^25.1.0",
"jest-html-reporter": "^2.8.0",
"jest-junit": "^10.0.0",
"lint-staged": "^10.0.7",
"pg": "^7.18.1",
"pg-hstore": "^2.3.3",
"prettier": "^1.19.1",
"sequelize": "^5.21.3",
"sequelize-cli": "^5.5.1",
"sinon": "^8.1.1",
"supertest": "^4.0.2"
"supertest": "^4.0.2",
"uuid": "^3.4.0"
}
}
}

69
rules/Brands.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,5 @@
jest.setTimeout(3000);
afterAll(done => {
done();
});

1459
yarn.lock

File diff suppressed because it is too large Load diff