Insert with relationships in AdonisJS with transaction - postgresql

I'm new to this JS Word, I read the Adonis and Knex doc, but I really don't understand what I'm doing wrong here.
Adonis: https://preview.adonisjs.com/guides/database/transactions and https://adonisjs.com/docs/4.1/lucid#_transactions
knex: http://knexjs.org/#Transactions
In the case when nothing goes wrong all the data are stored with no problem, but when I do a error on propose just to test the transaction I can notice that the "empresas" table had a record and the other tables are empty, I supose that when occurs an error all the tables need to be empty, by the transaction's rollback() function. Can someone enlighten me here?
Using: Adonis with postgres
this are my migrations:
table empresas:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class EmpresaSchema extends Schema {
up () {
this.create('empresas', (table) => {
table.increments()
table.text('codigo').notNullable()
table.text('tipo').notNullable()
table.text('origem').notNullable()
table.bigInteger('grupo_id').notNullable()
table.text('nome_fantasia')
table.text('razao_social')
table.timestamps()
})
}
down () {
this.drop('empresas')
}
}
module.exports = EmpresaSchema
table contatos:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class ContatosSchema extends Schema {
up () {
this.create('contatos', (table) => {
table.increments()
table
.integer('tipo_id')
.unsigned()
.notNullable()
.references('id')
.inTable('contato_tipos')
table.text('nome').notNullable()
table.text('dado').notNullable()
table.timestamps()
})
}
down () {
this.drop('contatos')
}
}
module.exports = ContatosSchema
table empresa_contatos:
'use strict'
/** #type {import('#adonisjs/lucid/src/Schema')} */
const Schema = use('Schema')
class EmpresaContatosSchema extends Schema {
up () {
this.create('empresa_contatos', (table) => {
table.increments()
table
.integer('empresa_id')
.unsigned()
.references('id')
.inTable('empresas')
.onUpdate('CASCADE')
.onDelete('CASCADE')
.index()
table
.integer('contato_id')
.unsigned()
.references('id')
.inTable('contatos')
.onUpdate('CASCADE')
.onDelete('CASCADE')
.index()
})
}
down () {
this.drop('empresa_contatos')
}
}
module.exports = EmpresaContatosSchema
My models:
Empresa model:
'use strict'
/** #type {typeof import('#adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Empresa extends Model {
contatos () {
return this.belongsToMany('App/Models/Contato').pivotTable('empresa_contatos')
}
}
module.exports = Empresa
Contato model:
'use strict'
/** #type {typeof import('#adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Contato extends Model {
static get table () {
return 'contatos'
}
empresa () {
return this.belongsToMany('App/Models/Empresa').pivotTable('empresa_contatos')
}
}
module.exports = Contato
And this is a part of my EmpresaController:
'use strict'
const Database = use('Database')
const Empresa = use('App/Models/Empresa')
const Contato = use('App/Models/Contato')
const Sequence = use('App/Controllers/Http/SequenceController')
class EmpresaController {
async customCreate ({ request, response, auth }) {
const trx = await Database.beginTransaction()
try {
const { enderecos, contatos, ...data } = request.all()
if (data.endereco.logradouro !== '') {
enderecos.push(data.endereco)
}
if (data.contato.nome !== '') {
contatos.push(data.contato)
}
const codigo = await Sequence.gerarNovoCodigoCliente(auth.user.grupo_id, trx)
let empresa = null
await Empresa.create({
codigo: codigo,
grupo_id: 1,
tipo: data.tipo,
origem: data.origem,
nome_fantasia: data.nome_fantasia,
razao_social: data.razao_social
}, trx)
.then(response => {
empresa = response
})
.catch(error => {
console.log('error empresa:')
console.log(error)
})
contatos.forEach(async contato => {
let novosContato = null
await Contato.create({
nome: contato.nome,
dado: contato.dado,
tipo_id: contato.tipo_contato.id
}, trx)
.then(response => {
novosContato = response
console.log('contato ok')
})
.catch(error => {
console.log('error contato:')
console.log(error)
})
await empresa.contatos().attach(novosContato.id, null, trx)
.then(response => {
console.log('attach ok')
})
.catch(error => {
console.log('error attach:')
console.log(error)
})
})
await trx.commit()
return response.ok(empresa)
} catch (error) {
await trx.rollback()
return response.badRequest(error.message)
}
}
}
module.exports = EmpresaController
I already tried put each async function on a const and resolve all with promise.all approach, and get the same problem, I really don't know but I guess that "const empresa = await Empresa.create(.......)" is commiting the transaction and running the everything else after it.
(edit)log:
error contato:
Error: Transaction query already complete, run with DEBUG=knex:tx for more info
at completedError (/app/node_modules/knex/lib/transaction.js:261:9)
at /app/node_modules/knex/lib/transaction.js:231:22
From previous event:
at Client_PG.trxClient.query (/app/node_modules/knex/lib/transaction.js:229:33)
at Runner.<anonymous> (/app/node_modules/knex/lib/runner.js:138:36)
From previous event:
at /app/node_modules/knex/lib/runner.js:47:21
at processImmediate (internal/timers.js:461:21)
From previous event:
at Runner.run (/app/node_modules/knex/lib/runner.js:33:30)
at Builder.Target.then (/app/node_modules/knex/lib/interface.js:23:43)
warning:
WARNING: Adonis has detected an unhandled promise rejection, which may
cause undesired behavior in production.
To stop this warning, use catch() on promises or wrap await
calls inside try/catch.
TypeError: Cannot read property 'id' of null
at /app/app/Controllers/Http/EmpresaController.js:165:54

You need to pass transaction reference to create also...
Just add trx here:
const empresa = await Empresa.create({
codigo: codigo,
grupo_id: 1,
tipo: data.tipo,
origem: data.origem,
nome_fantasia: data.nome_fantasia,
razao_social: data.razao_social
}, trx) <-- adding trx to create!
More info here: https://adonisjs.com/docs/4.1/lucid#_transactions

Related

How to set run time variable to postgresql in typeorm and nest js

Iam using the row level security in supabase with nest.js, So how can I set runtime variables safely to the DB so that I can be sure that the variables sync with each app user (due to the http request triggered the execution)?
I saw that it is possible to set local variables in a transaction but I wouldn't like to wrap all the queries with transactions.
Thanks & Regards
I tried to execute this with subscribers in nestjs it working fine . but it wont have a function like beforeSelect or beforeLoad , so i drop it
import { Inject, Injectable, Scope } from '#nestjs/common';
import { InjectDataSource } from '#nestjs/typeorm';
import { ContextService } from 'src/context/context.service';
import { DataSource, EntityManager, LoadEvent, RecoverEvent, TransactionRollbackEvent, TransactionStartEvent } from 'typeorm';
import {
EventSubscriber,
EntitySubscriberInterface,
InsertEvent,
UpdateEvent,
RemoveEvent,
} from 'typeorm';
#Injectable()
#EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {
constructor(
#InjectDataSource() dataSource: DataSource,
private context: ContextService,
) {
dataSource.subscribers.push(this);
}
async setUserId(mng: EntityManager, userId: string) {
await mng.query(
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
}
async beforeInsert(event: InsertEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeTransactionRollback(event: TransactionRollbackEvent) {
console.log('hello')
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeUpdate(event: UpdateEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeRemove(event: RemoveEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
}
After i get to know that we can use query runner instead of subscriber . but its not working ,
also i need a common method to use all the queries
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { DataSource, EntityManager, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private em: EntityManager,
) {}
getAllUsers(userId: string) {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
// like this we can set the variable
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
// after setting config variable the query should return only one user by userId
res = await queryRunner.query('SELECT * FROM users');
// but it reurns every user
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET request.jwt.claim.sub`);
await queryRunner.release();
resolve(res);
}
});
}
}
Thanks in advance....
Sorry to say, bro. But in currently state of development TypeORM does not have a feature that let us set conection variables. The roundabout for your problem is to do something like this.
/**
* Note: Set current_tenant session var and executes a query on repository.
* Usage:
* const itens = = await tenantTransactionWrapper( manager => {
* return manager.getRepository(Entity).find();
* });
*
* #param {function} callback - a function thar receives an Entity Manager and returns a method to be executed by tenantTransactionWrapper
* #param {string} providedTenantId - optional tenantId, otherwise tenant will be taken from localStorage
*/
async function tenantWrapper<R>(
callback: (manager: EntityManager) => Promise<R>,
providedTenantId?: string,
) {
const tenantId = providedTenantId || tenantStorage.get();
let response: R;
await AppDataSource.transaction(async manager => {
await manager.query(`SET LOCAL smsystem.current_tenant='${tenantId}';`);
response = await callback(manager);
});
return response;
}
Then create a custom repository to make use of the wraper a little bit simple.
const customRepository = <T>(entity: EntityTarget<T>) => ({
find: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng => mng.getRepository(entity).find(options))(),
findAndCount: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).findAndCount(options),
)(),
save: (entities: DeepPartial<T>[], options?: SaveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).save(entities, options),
)(),
findOne: (options: FindOneOptions<T>) =>
tenantTransactionWrapper(async mng =>
mng.getRepository(entity).findOne(options),
)(),
remove: (entities: T[], options?: RemoveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).remove(entities, options),
)(),
createQueryBuilder: () => {
throw new Error(
'Cannot create queryBuilder for that repository type, instead use: tenantWrapper',
);
},
tenantTransactionWrapper,
});
And finally use our customRepository :
class PersonsRepository implements IPersonsRepository {
private ormRepository: Repository<Person>;
constructor() {
this.ormRepository = AppDataSource.getRepository<Person>(Person).extend(
customRepository(Person),
);
}
public async create(data: ICreatePersonDTO): Promise<Person> {
const newPerson = this.ormRepository.create(data);
await this.ormRepository.save(newPerson);
return newPerson;
}
public async getAll(relations: string[] = []): Promise<Person[]> {
return this.ormRepository.find({ relations });
}
I hope this may help someone and will be very glad if someone provides a better solution.
First you have to create a custom class for wrapping your userId or any stuff
custome_service.ts ==>
#Injectable()
export class UserIdWrapper {
constructor(private dataSource: DataSource) {}
userIdWrapper = (callback: (mng: QueryRunner) => Promise<any>, userId: string) => {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
`SELECT set_config('your_variable_name', '${userId}', false)`,
);
//here is your funciton your calling in the service
res = await callback(queryRunner);
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET your_variable_name`);
await queryRunner.release();
resolve(res);
}
});
};
}
Now here you have to call the function inside user service
user.service.ts ==>
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { UserIdWrapper } from 'src/common/local-settup/userId_wrapper';
import { DataSource, EntityManager, QueryRunner, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private userIdWrapper: UserIdWrapper
) {}
async getAllUsers(userId: string) {
//This is your call back funciton that have to pass
const findOne = async (queryRunner: QueryRunner) => {
const res = await queryRunner.query('SELECT * FROM public.users');
return res;
};
try {
//hear we are passing the function in to the class funciton
return this.userIdWrapper.userIdWrapper(findOne, userId);
} catch (err) {
console.log(err);
}
}
}
Dont forgot to provide the custom class service inside the provider of user service.

Mongoose !: statics this undefined

import mongoose, { Schema, Document, Model } from "mongoose";
import { IUser } from "./interface/User.interface";
const userSchema = new Schema<IUserDoc>({
...
});
// Problem
userSchema.statics.findUser = async function (
email: Pick<IUser, "email">
): Promise<IUser | null> {
console.log("this", this); // undefined
try {
// Cannot read properties of undefined (reading 'findOne')
const user = await this.findOne({ email }).exec();
return user;
} catch (err: any) {
return err;
}
};
interface IUserDoc extends IUser, Document {
_id: string;
}
interface IUserModel extends Model<IUserDoc> {
findUser: (email: string) => Promise<IUser>;
}
const User = mongoose.model<IUserDoc, IUserModel>("User", userSchema);
export { User };
// Working
const findUserTest = async (email: string): Promise<IUser | null> => {
try {
const user = await User.findOne({ email }).exec();
return user;
} catch (err: any) {
return err;
}
};
mongoose -v 6.5.1
findUser does not work, but findUserTest works well. I think the problem is with userSchema, but I don't know what the problem is. I'd appreciate it if you could give me a hint.

Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in

I am getting ** Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in 5f8425a33a14f026f80133ed** where 5f8425a33a14f026f80133ed is the id passed to the axios url
I want to display the services based on the user id. My url works perfectly in postman but when i access it from the veux store it gives an error.
services.js (store)
import axios from 'axios';
const state = {
services : {},
status: '',
error: null
};
const getters = {
services : state => { return state.services }
};
const actions = {
async fetchServices({commit}, userId) {
let res = await axios.get('http://localhost:5000/api/services/displayUser' , userId)
commit('setProducts', res.data)
return res;
}
};
const mutations = {
setProducts (state, items) {
state.services= items
},
};
export default {
state,
actions,
mutations,
getters
};
This is how I am calling the action :
computed: {
...mapGetters(["services"]),
},
methods: {
...mapActions(["fetchServices"]),
getData(){
this.fetchServices(this.user._id)
},
},
async created() {
await this.getProfile();
await this.getData();
}
The axios route is defined as
router.get('/displayUser', (req,res) => {
const query = user = req.body ;
Services.find(query)
.exec((err, services) => res.json(services))
})
the error screenshot :
Error screenshot
GET request should not have a body. Either use query params, indicate an id in a path, or use POST request.
In case of query params this may look like this:
let res = await axios.get('http://localhost:5000/api/services/displayUser' , { params: { userId })
router.get('/displayUser', (req,res) => {
const query = user = req.query;
Services.find(query)
.exec((err, services) => res.json(services))
})
This worked for me too:
In front end: Vue Js
let res = axios.get("http://localhost:3000/api/v1/role/getRoleByName",
{ params: { roleName: "name of role you want to send as params" },
});
In back end: Node Js
router.get('/getRoleByName', (req,res)=>{
let roleName = req.query.roleName;
roleModule.getRoleByName(roleName).then(data =>{
response.json(res,data)
}
).catch(err=> {
response.badRequest(res, err);
})
});
it's a silly mistake axios.post req.
async addTodo({ commit }, title) {
try {
const res = await axios.post(BASE_URL, { title, complete: false });
commit("newTodo", res.data);
} catch (err) {
console.log(err.message);
}
},

Inserting a record from a mongoose model.statics function

I want to create a static function on a mongoose "log" module, which would allow me to write a message as a log entry.
How do I access the model from within the static function? Can I use this.model like below? I don't want to simply use native MongoDB insert command, because I want the model to validate the input, etc.
// ... schema defined above...
var Log = mongoose.model('Log', LogModelSchema)
Log.statics.log = function(message) {
var x = new this.model({message: message})
x.save()
.then(() => { .. do something .. }
.catch((err) => { .. handle err .. }
}
Is this the way it's supposed to be done?
You can make it work like this using this.create:
const mongoose = require("mongoose");
const logSchema = new mongoose.Schema({
message: String
});
logSchema.statics.log = function(message) {
this.create({ message: message })
.then(doc => console.log(doc))
.catch(err => console.log(err));
};
module.exports = mongoose.model("Log", logSchema);
Now you can use this in your routes like this:
Log.log("test");
or just return promise from statics:
logSchema.statics.log = function(message) {
return this.create({ message: message });
};
And use like this:
const Log = require("../models/log");
router.get("/log", (req, res) => {
Log.log("test")
.then(result => {
console.log(result);
res.send("ok");
})
.catch(err => {
console.log(err);
res.status(500).send("not ok");
});
});

How to test node.js-mongodb app using Mocha, Chai and Sinon? I am having difficulty with Sinon part

I am new to testing and having difficulty with Sinon stubs and mocks.
How can I write test for 'quote.list_quote' for following senario.
Here is the routes file : quotes.js
const express = require('express');
const request = require('request');
const async = require('async');
const validator = require('validator');
const quote_router = express.Router();
const confg = require("../../confg/confg");
const quote = require("../models/mquotes");
const quotes_model = quote.quotes;
// host name - needs to be set up using the environment variable
const hostname = confg.hostname;
// route for "quotes/"
quote_router.route("/")
// get route : display the random quote
.get((req, res) => {
// display random quote
async.waterfall([
(callback) => {callback(null, {res});},
quote.count_quotes
], check_quote_exist
);
})
// post route : create a new quote
.post((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.create_quote};
add_edit_quote(params);
})
// put route : edit the quote
.put((req, res) => {
const doc_json = {author : validator.escape(req.body.quote_author), quote_text : validator.escape(req.body.quote_text)};
const params = {res, doc_json, quote_action : quote.update_quote, qid : req.body.quote_id};
add_edit_quote(params);
})
// delete quote : delete the quote
.delete((req, res) => {
const qid = req.body.qid;
const condition = {_id : qid};
async.waterfall([
(callback) => {callback(null, {res, condition});},
quote.delete_quote
], request_quote_list
);
});
// route for "quotes/list" : display quotes list
quote_router.get("/list/", (req, res) => {
// mention the main operation
let operation;
if(req.body.operation != 'undefined') {
operation = req.body.operation;
} else {
operation = "list_quotes";
}
async.waterfall([
(callback) => {callback(null, {res, operation});},
quote.list_quote
], display_quotes_list
);
});
// display the quotes list
const display_quotes_list = (err, params, quotes_list) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = params.operation;
const header_msg = "List of all the quotes";
let alert_msg;
if(operation == "list_quotes") {
alert_msg = null;
} else if(operation == "delete_quote") {
alert_msg = "Quote has been deleted";
}
const params_out = {
page: "quote_list",
title: 'Quotes Manager',
host: hostname,
header_msg,
alert_msg,
quotes_list
};
res.render('index', params_out);
};
// send http request for quote list page
const request_quote_list = (err, params) => {
if (err) {return console.log(err);}
const res = params.res;
const operation = "delete_quote";
request.get('http://' + hostname + '/quotes/list/', {json:{operation}},
(error, response, body) => {
if (!error && response.statusCode == 200) {
res.send(body);
}
});
};
module.exports = quote_router;
This is not complete file. I have included only a portion of it.
And her is the model file : mquotes.js
const mongoose = require('mongoose');
// Define quote schema
const quoteSchema = new mongoose.Schema({
author: String,
quote_text: {type: String, required: true}
},
{timestamps: true}
);
const quote = {};
// Define quotes model
quote.quotes = mongoose.model('quotes', quoteSchema);
// error handler
error_handler = (callback, err, params, return_value) => {
if(err) { return callback(err);}
else {callback(null, params, return_value);}
};
// add quote - create
quote.create_quote = (params, callback) => {
const res = params.res;
const doc_json = params.doc_json;
quote.quotes.create(doc_json, (err, quotes_detail) => {
error_handler(callback, err, {res, operation : 'create_quote'}, quotes_detail);
});
};
// count the number of quotes
quote.count_quotes = (params, callback) => {
quote.quotes.count({}, (err, quotes_count) => {
error_handler(callback, err, params, quotes_count);
});
};
// delete quote - delete - id
quote.delete_quote = (params, callback) => {
quote.quotes.remove(params.condition, (err, query) => {
error_handler(callback, err, params);
});
};
// list quote - find
quote.list_quote = (params, callback) => {
quote.quotes.find({}, (err, quotes_list) => {
error_handler(callback, err, params, quotes_list);
});
};
// find quote by id
quote.quote_by_id = (params, callback) => {
quote.quotes.findById(params.qid, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
});
};
// returns the detail of random quote
quote.random_qoute = (params, callback) => {
const random_number = params.random_number;
// select one quote after skipping random_number of times
quote.quotes.findOne({}, (err, quotes_detail) => {
error_handler(callback, err, params, quotes_detail);
}).skip(random_number);
};
// update quote - update - id
quote.update_quote = (params, callback) => {
const options = {new: true};
const qid = params.qid;
const update_json = params.doc_json;
quote.quotes.findByIdAndUpdate(qid, {$set: update_json}, options, (err, quotes_detail) => {
params.operation = 'update_quote';
error_handler(callback, err, params, quotes_detail);
});
};
module.exports = quote;
I have installed mocha globally. Now, I want to test the model. Lets take the quote.list_quote function for example.
const mongoose = require('mongoose');
const chai = require('chai');
const sinon = require('sinon');
const expect = chai.expect; // use the "expect" style of Chai
const mquotes = require('./../../app/models/mquotes');
describe('Tests for quote models', () => {
describe("List quote", () => {
it('list_quote() should return list of quotes', () => {
});
});
});
Can anyone tell me about my coding practice too. I mean the way I use functions and modules.
First of all, you should try to use statics methods. And after that, you should use sinon-mongoose and sinon-as-promised if you want to use Promise in mongoose.
And this is my sample code and test with mocha, chai, and sinon. Hope useful for you.
model.js
var Schema = new mongoose.Schema({
name: String,
created_at: {
type: Date,
default: Date.now
},
updated_at: {
type: Date,
default: Date.now
}
});
Schema.statics.findByName = function(name, cb) {
this.findOne({
name: name
})
.exec()
.then(function getTemplate(template) {
if (!template) {
var error = new Error('Not found template by name: "' + name + '"');
error.status = 404;
return cb(error);
}
return cb(null, template);
})
.catch(function catchErrorWhenFindByTemplateName(error) {
error.status = 500;
return cb(error);
});
}
module.exports = mongoose.model('model', Schema);
test.js
var expect = require('chai').expect,
sinon = require('sinon'),
mongoose = require('mongoose');
require('sinon-as-promised');
require('sinon-mongoose');
var Model = require('../../app/models/model');
describe('Model', function () {
describe('static methods', function () {
describe('#findByName', function () {
var ModelMock;
beforeEach(function () {
ModelMock = sinon.mock(Model);
});
afterEach(function () {
ModelMock.restore();
});
it('should get error status 404 if not found template', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.status).to.eql(404);
ModelMock.verify();
done();
});
});
it('should get message not found template if name is not existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves(null);
Model.findByName(name, function (error) {
expect(error.message).to.match(/Not found template by name/gi);
ModelMock.verify();
done();
});
});
it('should get template when name is existed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.resolves('SUCCESS');
Model.findByName(name, function (error) {
expect(error).to.be.null;
ModelMock.verify();
done();
});
});
it('should get error status 500 when model crashed', function (done) {
var name = 'temp';
ModelMock
.expects('findOne').withArgs({name: name})
.chain('exec')
.rejects(new Error('Oops! Crashed'));
Model.findByName(name, function (error) {
expect(error.status).to.eql(500);
ModelMock.verify();
done();
});
});
});
});
});