persistanz
Version:
Object relational mapping (ORM) library with unique features.
756 lines (655 loc) • 31.8 kB
JavaScript
;
var prepare = require("./attic/prepare.js");
var conf = prepare.loadConfig();
var Persistanz = require("../lib/Persistanz.js");
var assert = require("chai").assert;
require('co-mocha');
describe("Same CRUD tests with transactions", function(done) {
for (var adapterName of conf.applyTestsTo) {
describe("running for " + adapterName, function() {
var pers;
(function(adapterName){
before("Set up databases and initialize persistanz : " + adapterName , function * () {
var dbConf = conf.dbConfigs[adapterName];
dbConf.adapter = adapterName;
yield prepare.createTestDatabase(dbConf);
pers = new Persistanz(dbConf);
var result = yield pers.create();
assert(result === true, "Create should succeed and must return true.");
});
})(adapterName);
after("Destroy persistanz instance", function * () {
yield pers.destroy();
});
/***********************************************************************/
function checkDeleteResult(deleteResult, object, type, id, method) {
["command", "status", "object"].forEach(field => {
assert(field in deleteResult, field + " must be a property in deleteResult.");
});
assert(deleteResult.command === "delete", "command must read delete.");
assert(deleteResult.status === "deleted", "status must read deleted.");
assert(typeof deleteResult.object.id === "number", "deleted object must have a numeric id.");
assert(deleteResult.object.id > 0, "deleted object must have an id.");
//checks with the initial object value. Valid only for pre typecast methods:
if (["pers.deleteObject", "object.delete"].indexOf(method) > -1) {
assert(object.id === deleteResult.object.id, "object id must be the same as that of deleteResult.");
assert(object === deleteResult.object, "delete object is the same object as the original.");
}
assert(deleteResult.object instanceof type, "result object must be of correct type.");
assert(deleteResult.object.id === id, "deleteResult object property must be what we set.");
}
function checkInsertResult(saveResult, object, type, name, method) {
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "insert", "command must read insert.");
assert(saveResult.status === "saved", "status must read saved.");
assert(typeof saveResult.object.id === "number", "saved object must have a numeric id.");
assert(saveResult.object.id > 0, "saved object must have an id.");
//checks with the initial object value. Valid only for pre typecast methods:
if (["pers.save", "pers.insert", "object.save", "object.insert"].indexOf(method) > -1) {
assert(object.id === saveResult.object.id, "object id must be the same as that of saveResult.");
assert(saveResult.lastInsertId === object.id, "lastInsertId must be the same as object id.");
assert(object.name === name, "Object.name should be what we set.");
}
assert(saveResult.object instanceof type, "result object must be of correct type.");
assert(saveResult.object.name === name, "saveResult object property must be what we set.");
}
/***********************************************************************/
describe("insert: promises with pers", function(){
it("pers.save() for insert using promises", function * (){
let c = new pers.m.Customer();
c.name = "Ege Madra";
let tx = yield pers.getTransaction();
let saveResult = yield pers.saveX(tx, c);
checkInsertResult(saveResult, c, pers.m.Customer, "Ege Madra", "pers.save");
yield tx.commit();
});
it("pers.insert() for insert using promises", function * (){
let c = new pers.m.Customer();
c.name = "John Doe";
let tx = yield pers.getTransaction();
let saveResult = yield pers.insertX(tx, c);
checkInsertResult(saveResult, c, pers.m.Customer, "John Doe", "pers.insert");
yield tx.commit();
});
it("pers.saveAs() for insert using promises", function * (){
var o = {name: "Ege Madra2"};
let tx = yield pers.getTransaction();
let saveResult = yield pers.saveAsX(tx, o, "Customer");
checkInsertResult(saveResult, o, pers.m.Customer, "Ege Madra2", "pers.saveAs");
yield tx.commit();
});
it("pers.insertAs() for insert using promises", function * (){
var o = {name: "John Doe2"};
let tx = yield pers.getTransaction();
let saveResult = yield pers.insertAsX(tx, o, "Customer");
checkInsertResult(saveResult, o, pers.m.Customer, "John Doe2", "pers.insertAs");
yield tx.commit();
});
});
describe("insert: promises with model", function(){
it("object.save() for insert using promises", function * (){
let c = new pers.m.Customer();
c.name = "Ege Madra";
let tx = yield pers.getTransaction();
let saveResult = yield c.saveX(tx);
checkInsertResult(saveResult, c, pers.m.Customer, "Ege Madra", "object.save");
yield tx.commit();
});
it("object.insert() for insert using promises", function * (){
let c = new pers.m.Customer();
c.name = "John Doe";
let tx = yield pers.getTransaction();
let saveResult = yield c.insertX(tx);
checkInsertResult(saveResult, c, pers.m.Customer, "John Doe", "object.insert");
yield tx.commit();
});
it("Model.save() for insert using promises", function * (){
var o = {name: "Ege Madra2"}
let tx = yield pers.getTransaction();
let saveResult = yield pers.models.Customer.saveX(tx, o);
checkInsertResult(saveResult, o, pers.m.Customer, "Ege Madra2", "Model.save");
yield tx.commit();
});
it("Model.insert() for insert using promises", function * (){
var o = {name: "John Doe2"}
let tx = yield pers.getTransaction();
let saveResult = yield pers.models.Customer.insertX(tx, o);
checkInsertResult(saveResult, o, pers.m.Customer, "John Doe2", "Model.insert");
yield tx.commit();
});
});
describe("insert: callbacks with pers", function() {
it("pers.save() for insert using callbacks", function (done){
let c = new pers.m.Customer();
c.name = "Ege Madra";
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.saveX(tx, c, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, c, pers.m.Customer, "Ege Madra", "pers.save");
tx.commit(function(err, result){
return done(err);
});
});
});
});
it("pers.insert() for insert using callbacks", function (done){
let c = new pers.m.Customer();
c.name = "John Doe";
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.insertX(tx, c, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, c, pers.m.Customer, "John Doe", "pers.insert");
tx.commit(function(err, result){
done(err);
});
});
});
});
it("pers.saveAs() for insert using callbacks", function (done){
var o = {name: "Ege Madra2"};
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.saveAsX(tx, o, "Customer", function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, o, pers.m.Customer, "Ege Madra2", "pers.saveAs");
tx.commit(function(err, result){
done(err);
});
});
});
});
it("pers.insertAs() for insert using callbacks", function (done){
var o = {name: "John Doe2"};
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.insertAsX(tx, o, "Customer", function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, o, pers.m.Customer, "John Doe2", "pers.insertAs");
tx.commit(function(err, result){
done(err);
});
});
});
});
});
describe("insert: callbacks with model", function(){
it("object.save() for insert using callback", function (done){
let c = new pers.m.Customer();
c.name = "Ege Madra";
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
c.saveX(tx, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, c, pers.m.Customer, "Ege Madra", "object.save");
tx.commit(function(err, result){
done(err);
});
});
});
});
it("object.insert() for insert using callbacks", function (done){
let c = new pers.m.Customer();
c.name = "John Doe";
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
c.insertX(tx, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, c, pers.m.Customer, "John Doe", "object.insert");
tx.commit(function(err, result){
done(err);
});
});
});
});
it("Model.save() for insert using callbacks", function (done){
var o = {name: "Ege Madra2"};
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.models.Customer.saveX(tx, o, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, o, pers.m.Customer, "Ege Madra2", "Model.save");
tx.commit(function(err, result){
done(err);
});
});
});
});
it("Model.insert() for insert using callbacks", function (done){
var o = {name: "John Doe2"};
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.models.Customer.insertX(tx, o, function(err, saveResult){
if (err) return done(err);
checkInsertResult(saveResult, o, pers.m.Customer, "John Doe2", "Model.insert");
tx.commit(function(err, result){
done(err);
});
});
});
});
});
describe("insertion errors", function (){
it("Ensure inserts with existing ids throwing (promise)", function * (){
try {
let tx = yield pers.getTransaction();
yield pers.insertAsX(tx, {"id": 1, name: "Ege"}, "Customer");
assert(false, "Inserting with an existing id must throw.");
}
catch(err){ //automatic rollback must occur.
assert(true);
}
});
it("Ensure inserts with existing ids returns error (callback)", function (done){
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.insertAsX(tx, {"id": 1, name: "Ege"}, "Customer", function(err, result){
assert(err != null, "Inserting with an existing id must return error.");
assert(result == null, "There must not be a result.");
done();
});
})
});
it("Inserts without pks to non-auto-increment tables must throw in promise mode", function * (){
let tx = yield pers.getTransaction();
try {
yield pers.insertAsX(tx, {name: "Latvia"}, "Country");
assert(false, "Inserting without a pk to a non auto-increment table must throw.");
}
catch(err){
assert(true);
}
});
it("Inserts without pks to non-auto-increment tables must error in callback mode", function (done){
pers.getTransaction(null, function(err, tx){
if (err) return done(err);
pers.insertAsX(tx, {name: "Latvia"}, "Country", function(err, result){
assert(err != null, "Inserting without a pk to a non auto-increment table must error.");
assert(result == null, "There must not be a result.");
done();
});
});
});
});
/***********************************************************************/
describe("Make sure we have 16 customers.", function() {
it("Get all customers, using repository with promises", function * (){
let tx = yield pers.getTransaction();
let customers = yield pers.query(tx).from("Customer").exec();
tx.commit();
assert(Array.isArray(customers), ".query() returns an array.");
assert(customers.length === 16, "we must have inserted 16 customers.");
});
it("Get all customers, using model with promises", function * (){
let tx = yield pers.getTransaction();
var customers = yield pers.models.Customer.query(tx).exec();
tx.commit();
assert(Array.isArray(customers), ".query() returns an array.");
assert(customers.length === 16, "we must have inserted 16 customers.");
});
});
describe("Make sure basic queries don't throw in callback mode", function () {
it("bad queries with callbacks can't throw", function (done){
var errCount = 0, resultCount = 0;
function cb (err, results) {
if (err != null) errCount ++;
if (results != null) resultCount ++;
};
var Customer = pers.models.Customer;
pers.getTransaction(null, (err, tx) => pers.models.Country.query(tx).select("nonexistentColumn").exec(cb) ); //1
pers.getTransaction(null, (err, tx) => Customer.insertX(tx, {}, cb) ); //2
pers.getTransaction(null, (err, tx) => Customer.saveX(tx, {}, cb) ); //3
var c = new Customer();
pers.getTransaction(null, (err, tx) => c.saveX(tx, cb) ); //4
pers.getTransaction(null, (err, tx) => c.insertX(tx, cb) ); //5
pers.getTransaction(null, (err, tx) => pers.saveX(tx, new Customer(), cb) ); //6
pers.getTransaction(null, (err, tx) => pers.saveAsX(tx, {}, "Customer", cb) ); //7
pers.getTransaction(null, (err, tx) => pers.insertX(tx, new Customer(), cb) ); //8
pers.getTransaction(null, (err, tx) => pers.insertAsX(tx, {}, "Customer", cb) ); //9
pers.getTransaction(null, (err, tx) => pers.models.Country.query(tx).select("nonexistentColumn").one(cb) ); //10
setTimeout(function(){
assert(errCount === 10, "We must have collected 10 errors by now.");
assert(resultCount === 0, "We must have collected no results by now.");
done();
},50);
});
});
describe("Updates", function () {
var c1 = null;
var tx = null;
function * getCustomer1 () {
tx = yield pers.getTransaction();
return c1 = yield pers.q().f("Customer").one();
}
it("pers.save()", function * () {
yield getCustomer1();
c1.name = "Darth Vader";
var saveResult = yield pers.saveX(tx, c1);
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "update", "command must read update.");
assert(saveResult.status === "saved", "status must read saved.");
assert(saveResult.object.name === "Darth Vader", "Saved object should reflect the new value.")
assert(saveResult.object === c1, "There is only one object!");
});
it("object.save()", function * () {
assert(c1.name === "Darth Vader", "It must be modified now.");
c1.name = "Princess Lea";
var saveResult = yield c1.saveX(tx);
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "update", "command must read update.");
assert(saveResult.status === "saved", "status must read saved.");
assert(saveResult.object.name === "Princess Lea", "Saved object should reflect the new value.")
assert(saveResult.object === c1, "There is only one object!");
});
it("pers.saveAs()", function * () {
assert(c1.name === "Princess Lea", "It must be modified now.");
var newObject = {id: c1.id, name: "Luke Skywalker"};
var saveResult = yield pers.saveAsX(tx, newObject, "Customer");
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "update", "command must read update.");
assert(saveResult.status === "saved", "status must read saved.");
assert(saveResult.object.name === "Luke Skywalker", "Saved object should reflect the new value.")
assert(saveResult.object.id === c1.id, "Although 2 different objects, ids must be the same.");
});
it("Model.save()", function * () {
assert(c1.name === "Princess Lea", "Previous save as should not have touched the original object");
var newObject = {id: c1.id, name: "Han Solo"};
var saveResult = yield pers.models.Customer.saveX(tx, newObject);
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "update", "command must read update.");
assert(saveResult.status === "saved", "status must read saved.");
assert(saveResult.object.name === "Han Solo", "Saved object should reflect the new value.")
assert(saveResult.object.id === c1.id, "Although 2 different objects, ids must be the same.");
//now validate:
var finalCustomer = yield pers.loadByIdX(tx, "Customer", c1.id);
assert(finalCustomer.id === c1.id && finalCustomer.name === "Han Solo", "Just validate.");
});
it("Not update anything", function * () {
const fakeId = 98564;
var saveResult = yield pers.saveAsX(tx, {id: fakeId, name: "Avarel Dalton"}, "Customer");
["lastInsertId", "command", "status", "object"].forEach(field => {
assert(field in saveResult, field + " must be a property in saveResult.");
});
assert(saveResult.command === "update", "command must read update.");
assert(saveResult.status === "not-saved", "status must read not-saved.");
//validate not saved:
var nullCustomer = yield pers.m.Customer.loadByIdX(tx, fakeId);
assert(nullCustomer === null, "There should be no customer with that id.");
});
it("Commit and check in callback", function (done) {
tx.commit(function(err, res){
if (err) return done(err);
pers.loadById("Customer", c1.id, "*", function(err, finalCustomer){
if (err) return done(err);
assert(finalCustomer.name === "Han Solo", "After-commit name must be the same.");
done();
});
});
});
});
//These tests are to satisfy some coverage. Assertations are unimportant
//but postgres should not stall at the end.
describe("Cast error and transactions.", function() {
it("Cast error in callback", function (done) {
pers.getTransaction(null, function(err, tx){
pers.saveAsX(tx, {}, "NONEXISTANT_MODEL", function(err){
assert(err != null, "err can't be null");
done();
})
});
});
it("Cast error in promise", function * () {
var tx = yield pers.getTransaction();
try{
yield pers.saveAsX(tx, {}, "NONEXISTANT_MODEL");
assert(false, "should not end up here.");
} catch (err) {
assert(err != null, "err can't be null");
}
});
});
describe("deletes: pers.deleteById & pers.deleteObject", function() {
it("pers.deleteById with promises", function * (){
let tx = yield pers.getTransaction();
var deleteResult = yield pers.deleteByIdX(tx, "Customer", 1);
checkDeleteResult(deleteResult, null, pers.models.Customer, 1, "pers.deleteById");
yield tx.commit();
});
it("pers.deleteById with callbacks", function (done){
pers.getTransaction(null, function(err, tx){
pers.deleteByIdX(tx, "Customer", 2, function(err, deleteResult){
if (err) return done(err);
checkDeleteResult(deleteResult, null, pers.models.Customer, 2, "pers.deleteById");
tx.commit(function(err, result){
if (err) return done(err);
done();
});
});
});
});
it("pers.deleteObject with promises", function * () {
let tx = yield pers.getTransaction();
var c = pers.cast({id: 3}, "Customer");
var deleteResult = yield pers.deleteObjectX(tx, c);
checkDeleteResult(deleteResult, c, pers.models.Customer, 3, "pers.deleteObject");
yield tx.commit();
});
it("pers.deleteObject with callbacks", function (done){
var c = pers.cast({id: 4}, "Customer");
pers.getTransaction(null, function(err, tx){
pers.deleteObjectX(tx, c, function(err, deleteResult){
if (err) return done(err);
checkDeleteResult(deleteResult, c, pers.models.Customer, 4, "pers.deleteObject");
tx.commit(function(err, result){
if (err) return done(err);
done();
});
});
});
});
});
describe("deletes: Model.deleteById & object.delete", function() {
it("Model.deleteById with promises", function * (){
let tx = yield pers.getTransaction();
var deleteResult = yield pers.models.Customer.deleteByIdX(tx, 5);
checkDeleteResult(deleteResult, null, pers.models.Customer, 5, "Model.deleteById");
yield tx.commit();
});
it("Model.deleteById with callbacks", function (done){
pers.getTransaction(null, function(err, tx){
pers.models.Customer.deleteByIdX(tx, 6, function(err, deleteResult){
if (err) return done(err);
checkDeleteResult(deleteResult, null, pers.models.Customer, 6, "Model.deleteById");
tx.commit(function(err, result){
if (err) return done(err);
done();
});
});
});
});
it("object.delete with promises", function * () {
var c = pers.cast({id: 7}, "Customer");
let tx = yield pers.getTransaction();
var deleteResult = yield c.deleteX(tx);
checkDeleteResult(deleteResult, c, pers.models.Customer, 7, "object.delete");
yield tx.commit();
});
it("object.delete with callbacks", function (done){
var c = pers.cast({id: 8}, "Customer");
pers.getTransaction(null, function(err, tx){
c.deleteX(tx, function(err, deleteResult){
if (err) return done(err);
checkDeleteResult(deleteResult, c, pers.models.Customer, 8, "object.delete");
tx.commit(function(err, result){
done();
});
});
})
});
});
describe("not-deleted and validation", function () {
it("Not delete anything", function * () {
const id = 1; //we have already deleted Customer:1 above.
let tx = yield pers.getTransaction();
var deleteResult = yield pers.deleteByIdX(tx, "Customer", 1);
["command", "status", "object"].forEach(field => {
assert(field in deleteResult, field + " must be a property in deleteResult.");
});
assert(deleteResult.command === "delete", "command must read delete.");
assert(deleteResult.status === "not-deleted", "status must read not-deleted.");
yield tx.commit();
});
it("Validate deletes by confirming we have only 8 customers left", function * (){
var customers = yield pers.models.Customer.q().exec();
assert(customers.length === 8, "We should have 8 customers now.");
});
});
describe("tx rollback and closed", function () {
it("rollback with promises", function * (){
const id = 9988; const name = "no name";
var tx = yield pers.getTransaction();
yield pers.insertAsX(tx, {name, id}, "Customer");
var c = yield pers.loadByIdX(tx, "Customer", id);
assert(c.id === id && c.name === name);
yield tx.rollback();
c = yield pers.loadById("Customer", id);
assert(c === null, "There can't be a such customer.");
try {
yield tx.rollback();
assert("Should not have come here.");
} catch (err) {
assert(err.toString().includes("Transaction is closed"), "We should have tx closed error.");
}
});
it("rollback with callbacks", function (done){
const id = 9988; const name = "no name";
pers.getTransaction(null, function(err, tx){
pers.insertAsX(tx, {name, id}, "Customer", function(err, saveResult){
tx.rollback(function(err){
pers.loadById("Customer", id, "*", function(err, customer){
assert(customer === null, "No such customer.");
tx.rollback(function(err){
assert(err.toString().includes("Transaction is closed"), "We should have tx closed error.");
return done();
});
});
});
});
});
});
});
describe("hydrateX()", function() {
let saveResult = null, customer = null, tx = null;
function * prepare () {
tx = yield pers.getTransaction();
saveResult = yield pers.insertAsX(tx, {name: "James Bond"}, "Customer");
customer = yield pers.loadByIdX(tx, "Customer", saveResult.lastInsertId, "id");
assert(customer.id === saveResult.lastInsertId, "customer id must be the same as inserted.");
assert(customer.name === undefined, "We didn't select name field.");
}
it("pers.hydrateX() with promises", function * () {
yield prepare();
let newCustomer = yield pers.hydrateX(tx, customer);
assert(customer === newCustomer, "They are the same objects.");
assert(customer.name === "James Bond", "original customer is modified must have the name 'James Bond'.");
});
it("object.hydrateX() with promises", function * () {
delete customer.name;
let newCustomer = yield customer.hydrateX(tx, "name");
assert(customer === newCustomer, "They are the same objects.");
assert(customer.name === "James Bond", "original customer is modified must have the name 'James Bond'.");
});
it("pers.hydrateX() with callbacks", function (done) {
delete customer.name;
pers.hydrateX(tx, customer, "name", function(err, newCustomer){
if (err) return done(err);
assert(customer === newCustomer, "They are the same objects.");
assert(customer.name === "James Bond", "original customer is modified must have the name 'James Bond'.");
done();
});
});
it("object.hydrateX() with callbacks", function (done) {
delete customer.name;
customer.hydrateX(tx, "name", function(err, newCustomer){
if (err) return done(err);
assert(customer === newCustomer, "They are the same objects.");
assert(customer.name === "James Bond", "original customer is modified must have the name 'James Bond'.");
tx.commit(function(err, result){
done();
});
});
});
it("hydrate without pk throws (promises)", function * () {
var c = new pers.models.Customer();
try {
let tx = yield pers.getTransaction();
yield c.hydrateX(tx, "name");
assert(false, "cannot end up here.");
} catch (err) {
assert(err.toString().includes("without the primary key"), "Should tell about the missing pk.");
}
});
it("hydrate without a corresponding row in db throws (promises)", function * () {
var c = pers.cast({id: 2657}, "Customer");
try {
let tx = yield pers.getTransaction();
yield c.hydrateX(tx, "name");
} catch (err) {
assert(err.toString().includes("not found in the database"), "Should tell about the missing row.");
}
});
it("hydrate without pk errors (callbacks)", function (done) {
var c = new pers.models.Customer();
pers.getTransaction(null, function(err, tx){
c.hydrateX(tx, "name", function(err, result){
assert(err.toString().includes("without the primary key"), "Should tell about the missing pk.");
done();
});
});
});
it("hydrate without a corresponding row in db errors (callbacks)", function (done) {
var c = pers.cast({id: 2657}, "Customer");
pers.getTransaction(null, function(err, tx){
c.hydrateX(tx, "name", function(err, result){
assert(err.toString().includes("not found in the database"), "Should tell about the missing row.");
done();
});
});
});
});
describe("missing tx errors", function () {
it("all missing tx errors", function * (){
var throwers = [
pers.loadByIdX("hey"),
pers.hydrateX("pey"),
pers.insertX("huy"),
pers.insertAsX("huy"),
pers.saveX("huy"),
pers.saveAsX("huy"),
pers.deleteByIdX("huy"),
pers.deleteObjectX("huy"),
];
for (var t of throwers) {
try {
yield t;
assert(false, "Cannot end up here.");
} catch (err) {
assert(err.message.indexOf('No transaction object supplied') > -1, "Err should have descriptive info.");
}
}
});
});
});
}
});