@apantle/awsome-factory-associator
Version:
Provides a syntax to define factories with any kind of association.
712 lines (654 loc) • 24.4 kB
JavaScript
/* global factory, Passenger, Ticket, Sale, Salesman, Store */
const Promise = require('bluebird');
const should = require('should');
const faker = require('faker');
describe('Factories test', function () {
describe('Creation', function () {
it('should define a factory', () => {
return new Promise((resolve) => {
factory.define('passenger', Passenger).attr('name', 'Pedro');
factory.definitions.passenger.name.should.equal('passenger');
factory.definitions.passenger.config.attr.name.should.equal('Pedro');
return resolve();
});
});
it('should return error defining repeated name', () => {
try {
factory.define('passengerRepeated', Passenger);
factory.define('passengerRepeated', Passenger);
true.should.be.false();
} catch (err) {
should.exist(err);
}
});
it('should build', () => {
return factory
.build('passenger')
.then((passenger) => {
(passenger instanceof Passenger).should.be.true();
passenger.name.should.eql('Pedro');
passenger.is_disabled.should.eql(false);
return Passenger.findAndCountAll({ where: { name: 'Pedro' } });
})
.then((result) => {
result.count.should.equal(0);
});
});
it('should create', () => {
return factory
.create('passenger')
.then((passenger) => {
(passenger instanceof Passenger).should.be.true();
passenger.name.should.eql('Pedro');
passenger.is_disabled.should.eql(false);
return Passenger.findAndCountAll({ where: { name: 'Pedro' } });
})
.then((result) => {
result.count.should.equal(1);
});
});
it('should error using invalid factory name', () => {
try {
return factory.create('Wrong').then(() => {
true.should.be.false();
});
} catch (err) {
should.exist(err);
}
});
});
describe('Attributes', function () {
it('should create using function as attribute', () => {
factory
.define('passengerFunction', Passenger)
.attr('name', faker.lorem.word);
return factory
.create('passengerFunction')
.then((passenger) => {
(passenger instanceof Passenger).should.be.true();
(typeof passenger.name).should.eql('string');
return Passenger.findAndCountAll({ where: { name: passenger.name } });
})
.then((result) => {
result.count.should.equal(1);
});
});
it('should create using function already called as attribute', () => {
factory
.define('saleCancelled', Sale)
.attr('total', faker.random.number)
.attr('cancelled_at', faker.date.future(1));
return factory.create('saleCancelled').then((sale) => {
should.exist(sale.cancelled_at);
should.exist(sale.total);
});
});
it('should create using null as attribute', () => {
factory
.define('saleNotCancelled', Sale)
.attr('total', faker.random.number)
.attr('cancelled_at', null);
return factory.create('saleNotCancelled').then((sale) => {
should.not.exist(sale.cancelled_at);
should.exist(sale.total);
});
});
it('should create auto increment for string', () => {
factory
.define('passengerAutoIncrement', Passenger)
.attr('name', faker.lorem.word, { auto_increment: 2 });
return factory
.create('passengerAutoIncrement')
.then((passenger) => {
(typeof passenger.name).should.eql('string');
passenger.name.slice(-1).should.eql('1');
return factory.create('passengerAutoIncrement');
})
.then((passenger2) => {
passenger2.name.slice(-1).should.eql('3');
});
});
it('should overwrite value incremented', () => {
return factory
.create('passengerAutoIncrement', { name: 'nameString' })
.then((passenger) => {
(typeof passenger.name).should.eql('string');
passenger.name.should.eql('nameString');
});
});
it('should create auto increment for integer', () => {
let ticket1;
factory
.define('ticketIncrementCode', Ticket)
.attr('price', faker.random.number)
.attr('seat', 'A24')
.attr('code', 1, { auto_increment: 1 });
return factory
.create('ticketIncrementCode')
.then((ticket) => {
ticket1 = ticket;
(typeof ticket.price).should.eql('number');
ticket.seat.should.eql('A24');
ticket.code.should.eql(1);
return factory.create('ticketIncrementCode');
})
.then((ticket2) => {
ticket2.code.should.not.eql(ticket1.price);
ticket2.code.should.eql(2);
});
});
it('should share increment with children and alter attributes', () => {
factory
.define('ticketIncrementCodeChild', Ticket)
.parent('ticketIncrementCode')
.attr('seat', 'X');
return factory.create('ticketIncrementCodeChild').then((ticket) => {
(typeof ticket.price).should.eql('number');
ticket.seat.should.eql('X');
ticket.code.should.eql(3);
});
});
});
describe('Assoc', function () {
it('should define a ticket associated to a sale', () => {
factory.define('saleDef', Sale).attr('total', 120);
factory
.define('ticketDef', Ticket)
.attr('seat', '22A')
.attr('price', 16)
.assoc('Sale', 'saleDef', { total: 16 });
factory.definitions.ticketDef.config.assoc.should.have.property('Sale');
factory.definitions.ticketDef.config.assoc.Sale.should.have.property(
'factoryName',
'saleDef'
);
factory.definitions.ticketDef.config.assoc.Sale.should.have.property(
'options',
{ total: 16 }
);
factory.definitions.ticketDef.config.assoc.Sale.should.have.property(
'foreignKey',
'sale_key'
);
});
it('should get associations from parent', () => {
factory.define('ticketAFree').parent('ticketA').attr('price', 0);
factory.definitions.ticketAFree.config.assoc.should.have.property('Sale');
factory.definitions.ticketAFree.config.assoc.Sale.should.have.property(
'factoryName',
'saleA'
);
factory.definitions.ticketAFree.config.assoc.Sale.should.have.property(
'options',
{ total: 30 }
);
factory.definitions.ticketAFree.config.assoc.Sale.should.have.property(
'foreignKey',
'sale_key'
);
});
it('should get associations from parent and overwrite associations', () => {
factory.define('saleFree', Sale).attr('total', 0);
factory
.define('ticketAFreeSale')
.parent('ticketAFree')
.assoc('Sale', 'saleFree');
factory.definitions.ticketAFreeSale.config.assoc.should.have.property(
'Sale'
);
factory.definitions.ticketAFreeSale.config.assoc.Sale.should.have.property(
'factoryName',
'saleFree'
);
factory.definitions.ticketAFreeSale.config.assoc.Sale.should.have.property(
'options',
undefined
);
factory.definitions.ticketAFreeSale.config.assoc.Sale.should.have.property(
'foreignKey',
'sale_key'
);
});
it('should return error defining with invalid association name', () => {
try {
factory
.define('ticketWrongAss', Ticket)
.attr('seat', '22A')
.attr('price', 16)
.assoc('InvalidAs', 'saleDef', { total: 16 });
true.should.be.false();
} catch (err) {
should.exist(err);
}
});
it('should return error defining with invalid association type', () => {
try {
factory
.define('ticketWrongAss', Ticket)
.attr('seat', '22A')
.attr('price', 16)
.assoc('MainDiscount', 'discountFact'); //Using assoc and should be assocAfter
true.should.be.false();
} catch (err) {
should.exist(err);
}
});
it('should return error defining without model', () => {
try {
factory
.define('ticketWrongModel')
.attr('seat', '22A')
.attr('price', 16)
.assoc('InvalidAs', 'saleDef', { total: 16 });
true.should.be.false();
} catch (err) {
should.exist(err);
}
});
it('should create a belongs to association', () => {
return factory.create('ticketA').then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', 30);
ticket.Sale.should.have.property('id', ticket.sale_key);
});
});
it('should create a belongs to association from id', () => {
let sale;
return factory
.create('saleA')
.then((saleCreated) => {
sale = saleCreated;
return factory.create('ticketA', { Sale: sale.id });
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', sale.total);
ticket.Sale.should.have.property('id', sale.id);
});
});
it('should create a belongs to association with options', () => {
return factory
.create('ticketA', { Sale: { total: 0 } })
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', 0);
ticket.Sale.should.have.property('id', ticket.sale_key);
});
});
it('should create a belongs to association but overwrite if foreign key sent', () => {
let sale;
return factory
.create('saleA')
.then((saleCreated) => {
sale = saleCreated;
return factory.create('ticketA', { sale_key: sale.id });
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', sale.total);
ticket.Sale.should.have.property('id', sale.id);
});
});
it('should create a belongs to association with save option', () => {
return factory
.create('ticketA', { Sale: { $: 'saleSaved' }, $: 'ticketCreated' })
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', 120);
ticket.Sale.should.have.property('id', ticket.sale_key);
ticket.should.have.property('$');
ticket.$.should.have.property('saleSaved');
ticket.$.should.have.property('ticketCreated');
ticket.$.saleSaved.should.have.property('id', ticket.Sale.id);
ticket.$.ticketCreated.should.have.property('id', ticket.id);
});
});
it('should use saved definition attribute as attribute', () => {
return factory
.create('ticketA', {
Sale: { $: 'saleSaved' },
price: '$saleSaved.total'
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', ticket.price);
ticket.Sale.should.have.property('id', ticket.sale_key);
ticket.should.have.property('$');
});
});
it('should use saved definition function as attribute', () => {
return factory
.create('ticketA', {
Sale: { $: 'saleSaved' },
price: '$saleSaved.getTotal(3)'
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.should.have.property('price', 3);
});
});
it('should use saved definition function as attribute without attributes', () => {
return factory
.create('ticketA', {
Sale: { $: 'saleSaved' },
price: '$saleSaved.getTotal()'
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.should.have.property('price', 0);
});
});
it('should use saved definition function as attribute without attributes', () => {
return factory
.create('ticketA', {
Sale: { $: 'saleSaved' },
price: '$saleSaved.getTotal()'
})
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.should.have.property('price', 0);
});
});
it('should create a belongs to association overwriting the factory name', () => {
factory.define('saleFacName', Sale).attr('total', 9);
return factory
.create('ticketA', { Sale: { _factoryName: 'saleFacName' } })
.then((ticket) => {
ticket.should.have.property('seat', '22A');
ticket.should.have.property('Sale');
ticket.Sale.should.have.property('total', 9);
ticket.Sale.should.have.property('id', ticket.sale_key);
});
});
});
describe('AssocMany', function () {
it('should define a salesman associated to many stores', () => {
factory.define('storeDef', Store).attr('city', 'London');
factory
.define('salesmanWithStores', Salesman)
.attr('name', 'Sus')
.assocMany('StoreHired', 'storeDef', [
{ city: 'Paris' },
{ city: 'Barcelona' }
]);
const assocManyConfig =
factory.definitions.salesmanWithStores.config.assocMany;
assocManyConfig.should.have.property('StoreHired');
assocManyConfig.StoreHired.should.have.property(
'factoryName',
'storeDef'
);
assocManyConfig.StoreHired.should.have.property('options', [
{ city: 'Paris' },
{ city: 'Barcelona' }
]);
assocManyConfig.StoreHired.should.have.property('plural', 'StoreHired');
});
it('should define a store associated to many salesman using default as', () => {
factory.define('salesmanOne', Salesman).attr('name', 'Pedro');
factory
.define('storeWithSalesmans', Store)
.attr('city', 'London')
.assocMany('Salesmans', 'salesmanOne', [{}]);
const assocManyConfig =
factory.definitions.storeWithSalesmans.config.assocMany;
assocManyConfig.should.have.property('Salesmans');
assocManyConfig.Salesmans.should.have.property(
'factoryName',
'salesmanOne'
);
assocManyConfig.Salesmans.should.have.property('options', [{}]);
assocManyConfig.Salesmans.should.have.property('plural', 'Salesmans');
return factory.create('storeWithSalesmans').then((store) => {
store.should.have.property('Salesmans');
store.Salesmans.should.have.length(1);
store.Salesmans[0].should.have.property('name', 'Pedro');
});
});
it('should create a belongs to many association', () => {
return factory.create('salesmanA').then((salesman) => {
salesman.should.have.property('name', 'Sus');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(2);
const storeP = salesman.StoreHired.find((s) => {
return s.city === 'Paris';
});
should.exist(storeP);
const storeL = salesman.StoreHired.find((s) => {
return s.city === 'London';
});
should.exist(storeL);
});
});
it('should create a belongs to many association with id', () => {
let storeId;
return factory
.create('storeA', { city: 'Mexico' })
.then((storeCreated) => {
storeId = storeCreated.id;
return factory.create('salesmanA', { StoreHired: [storeId] });
})
.then((salesman) => {
salesman.should.have.property('name', 'Sus');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(1);
salesman.StoreHired[0].should.have.property('city', 'Mexico');
salesman.StoreHired[0].should.have.property('id', storeId);
});
});
it('should create a belongs to many overwriting factory name', () => {
factory.define('storeNew', Store).attr('city', 'Berlin');
return factory
.create('salesmanA', { StoreHired: [{ _factoryName: 'storeNew' }] })
.then((salesman) => {
salesman.should.have.property('name', 'Sus');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(1);
salesman.StoreHired[0].should.have.property('city', 'Berlin');
});
});
it('should create a belongs to many using size', () => {
return factory
.create('salesmanA', {
name: faker.name.firstName,
StoreHired: { city: 'LA', _size: 10 }
})
.then((salesman) => {
salesman.should.have.property('name');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(10);
salesman.StoreHired[0].should.have.property('city', 'LA');
});
});
it('should create a belongs to many using size and save it', () => {
return factory
.create('salesmanA', {
name: faker.name.firstName,
StoreHired: { city: 'LA', _size: 2, $: 'store-' }
})
.then((salesman) => {
salesman.should.have.property('name');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(2);
salesman.StoreHired[0].should.have.property('city', 'LA');
salesman.$.should.have.property('store-0');
salesman.$.should.have.property('store-1');
});
});
it('should save associated instance in same object and use it', () => {
factory.define('storeRandom', Store).attr('city', faker.address.city);
factory
.define('salesmanWithRandomStores', Salesman)
.attr('name', 'Sus')
.assocMany('StoreHired', 'storeRandom', [
{ $: 'store1' },
{ city: '$store1.city', $: 'store2' }
]);
return factory
.create('salesmanWithRandomStores', { name: 'Ale' })
.then((salesman) => {
salesman.should.have.property('name', 'Ale');
salesman.should.have.property('StoreHired');
salesman.StoreHired.should.have.length(2);
salesman['$'].should.have.property('store1');
salesman['$'].store1.should.have.property('city');
salesman['$'].should.have.property('store2');
salesman['$'].store2.should.have.property(
'city',
salesman['$'].store1.city
);
});
});
});
describe('hasOne', function () {
it('should define a ticket associated to one discount', () => {
factory.define('discountFact', Discount).attr('percentage', 20);
factory
.define('ticketFactDiscount', Ticket)
.assocAfter('MainDiscount', 'discountFact', { percentage: 50 });
const assocAfterConfig =
factory.definitions.ticketFactDiscount.config.assocAfter;
assocAfterConfig.should.have.property('MainDiscount');
assocAfterConfig.MainDiscount.should.have.property(
'factoryName',
'discountFact'
);
assocAfterConfig.MainDiscount.should.have.property('options', {
percentage: 50
});
assocAfterConfig.MainDiscount.should.have.property(
'foreignKey',
'ticket_key'
);
});
it('should create a has one association', () => {
return factory
.create('ticketWithDiscount', { price: 100 })
.then((ticket) => {
ticket.should.have.property('price', 100);
ticket.should.have.property('MainDiscount');
ticket.MainDiscount.should.have.property('percentage', 10);
});
});
it('should create a has one association with options', () => {
return factory
.create('ticketWithDiscount', { MainDiscount: { percentage: 80 } })
.then((ticket) => {
ticket.should.have.property('MainDiscount');
ticket.MainDiscount.should.have.property('percentage', 80);
});
});
it('should create a has one association with different factory', () => {
factory.define('discountFull', Discount).attr('percentage', 100);
return factory
.create('ticketWithDiscount', {
MainDiscount: { _factoryName: 'discountFull' }
})
.then((ticket) => {
ticket.should.have.property('MainDiscount');
ticket.MainDiscount.should.have.property('percentage', 100);
});
});
it('should respond error if id passed', () => {
return factory
.create('ticketWithDiscount', { MainDiscount: 2 })
.then((ticket) => {
ticket.should.be.undefined();
})
.catch((err) => {
err.message.should.equal(
'Ids can not be passed in MainDiscount since it is a HasOne or HasMany association.'
);
});
});
it('should save has one association', () => {
return factory
.create('ticketWithDiscount', {
MainDiscount: { percentage: '$root.price', $: 'discountSaved' }
})
.then((ticket) => {
ticket.should.have.property('MainDiscount');
ticket.MainDiscount.should.have.property('percentage', ticket.price);
ticket.$.should.have.property('discountSaved');
ticket.$.discountSaved.should.have.property(
'id',
ticket.MainDiscount.id
);
});
});
});
describe('hasMany', function () {
it('should define a sale associated to many tickets', () => {
factory.define('ticketFactNoSale', Ticket);
factory
.define('saleFactWithTickets', Sale)
.attr('total', 120)
.assocManyAfter('Tickets', 'ticketFactNoSale', [
{ price: 120 / 2 },
{ price: 120 / 2 }
]);
const assocManyAfterConfig =
factory.definitions.saleFactWithTickets.config.assocManyAfter;
assocManyAfterConfig.should.have.property('Tickets');
assocManyAfterConfig.Tickets.should.have.property(
'factoryName',
'ticketFactNoSale'
);
assocManyAfterConfig.Tickets.should.have.property('options', [
{ price: 120 / 2 },
{ price: 120 / 2 }
]);
assocManyAfterConfig.Tickets.should.have.property(
'foreignKey',
'sale_key'
);
});
it('should create has many association', () => {
return factory.create('saleB').then((ticket) => {
ticket.should.have.property('Tickets');
ticket.Tickets.should.have.length(2);
});
});
it('should create has many with options', () => {
return factory
.create('saleB', { Tickets: { _size: 3, price: '$root.total' } })
.then((sale) => {
sale.should.have.property('Tickets');
sale.Tickets.should.have.length(3);
sale.Tickets[0].should.have.property('price', sale.total);
sale.Tickets[1].should.have.property('price', sale.total);
sale.Tickets[2].should.have.property('price', sale.total);
});
});
it('should respond error if id passed', () => {
return factory
.create('saleB', { Tickets: [1] })
.then((ticket) => {
ticket.should.be.undefined();
})
.catch((err) => {
err.message.should.equal(
'Ids can not be passed in Tickets since it is a HasOne or HasMany association.'
);
});
});
it('should save has many association', () => {
return factory
.create('saleB', { Tickets: [{ $: 'ticket1' }] })
.then((sale) => {
sale.should.have.property('Tickets');
sale.Tickets.should.have.length(1);
sale.$.should.have.property('ticket1');
});
});
});
});