persistanz
Version:
Object relational mapping (ORM) library with unique features.
353 lines (309 loc) • 19.2 kB
JavaScript
"use strict";
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("Select queries without a configuration", 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 * insertAFewRecords () {
var dw = yield pers.saveAs({name: "Darth Vader"}, "Customer");
var ls = yield pers.saveAs({name: "Luke Skywalker"}, "Customer");
var dateTime = new Date();
var dwo1 = yield pers.saveAs({customerId: dw.lastInsertId, dateTime}, "Order");
var dwo2 = yield pers.saveAs({customerId: dw.lastInsertId, dateTime}, "Order");
var lso1 = yield pers.saveAs({customerId: ls.lastInsertId, dateTime}, "Order");
//few products:
var ws = yield pers.saveAs({title_en: "White Shirt", title_tr: "Beyaz Gömlek", __type: "Shirt"}, "Product");
var bus = yield pers.saveAs({title_en: "Blue Shirt", title_tr: "Mavi Gömlek", __type: "Shirt"}, "Product");
var bls = yield pers.saveAs({title_en: "Black Shirt", title_tr: "Siyah Gömlek", __type: "Shirt"}, "Product");
//few order items:
yield pers.saveAs({orderId: dwo1.lastInsertId, productId: bls.lastInsertId}, "OrderItem"); //black shirt for Vader.
yield pers.saveAs({orderId: dwo1.lastInsertId, productId: bus.lastInsertId}, "OrderItem"); //blue shirt for Vader.
yield pers.saveAs({orderId: lso1.lastInsertId, productId: ws.lastInsertId}, "OrderItem"); //white shirt for Luke.
yield pers.saveAs({orderId: lso1.lastInsertId, productId: bus.lastInsertId}, "OrderItem"); //blue shirt for Luke.
yield pers.saveAs({orderId: dwo2.lastInsertId, productId: ws.lastInsertId}, "OrderItem"); //white shirt for Vader.
//few addresses:
yield pers.insertAs({customerId: dw.lastInsertId, address:"Death Star 1", id: 77}, "Address");
yield pers.insertAs({customerId: ls.lastInsertId, address:"Tatooine", id: 108}, "Address");
}
it("Bridge fields: select and order", function * (){
yield insertAFewRecords();
var orders = yield pers.q().f("Order").s("*, customer.*").o("{customer.name} desc").exec();
assert(Array.isArray(orders), "Orders collection is an array.");
assert(orders.length === 3, "We have 3 orders.");
assert(orders[0].customer.name === 'Luke Skywalker', "Luke must come first due to order by clause.");
assert("dateTime" in orders[0], "dateTime must be in the orders.");
});
it("! operator", function * (){
var orders = yield pers.q().f("Order").s("*, !dateTime, customer.*, !customer.name").exec();
assert(! ("dateTime" in orders[0]), "We excluded the dateTime field in order.");
assert(! ("name" in orders[0].customer), "We excluded the customer name.");
assert("id" in orders[0].customer, "But we included the id.");
});
it("select with", function * (){
var orders = yield pers.q().f("Order").s("*, !dateTime").sw("customer", "*, !name").exec();
assert(! ("dateTime" in orders[0]), "We excluded the dateTime field in order.");
assert(! ("name" in orders[0].customer), "We excluded the customer name.");
assert("id" in orders[0].customer, "But we included the id.");
});
it("where clause", function * (){
var orders = yield pers.q().f("Order").s("*, customer.*")
.w("{customer.name} like ?", '%walk%').exec();
assert(orders.length === 1, "Luke has 1 order");
assert(orders[0].customer.name === 'Luke Skywalker', "Luke.");
});
it("where clause with tagged template", function * (){
var orders = yield pers.q().f("Order").s("*, customer.*")
.w`{customer.name} like ${'%walk%'}`.exec();
assert(orders.length === 1, "Luke has 1 order");
assert(orders[0].customer.name === 'Luke Skywalker', "Luke.");
});
it("where clause with tagged template, using array", function * (){
var names = ["Luke Skywalker"];
var orders = yield pers.q().f("Order").s("*, customer.*")
.w`{customer.name} in ( ${names} )`.exec();
assert(orders.length === 1, "Luke has 1 order");
assert(orders[0].customer.name === 'Luke Skywalker', "Luke.");
});
it("where clause with tagged template, using subquery", function * (){
var subQuery = pers.q().f("Customer").s("id").o("{name} asc")
.w("{name} = ?", "Darth Vader");
var orders = yield pers.q().f("Order").s("*, customer.*")
.w`{customerId} in ( ${subQuery} )`.exec();
assert(orders.length === 2, "Darth has 2 orders");
assert(orders[0].customer.name === 'Darth Vader', "Vader.");
assert(orders[1].customer.name === 'Darth Vader', "Vader.");
});
it("limit clause", function * (){
var limit = 1, offset = 2;
var orders = yield pers.q().f("Order").s("*, customer.*").o("{customer.name}")
.limit("? OFFSET ?", [limit, offset]).exec();
assert(orders.length === 1, "We limited by 1");
assert(orders[0].customer.name === 'Luke Skywalker', "3rd order is by Luke.");
});
it("limit clause with tagged template", function * (){
var limit = 1, offset = 2;
var orders = yield pers.q().f("Order").s("*, customer.*").o("{customer.name}")
.limit`${limit} OFFSET ${offset}` .exec();
assert(orders.length === 1, "We limited by 1");
assert(orders[0].customer.name === 'Luke Skywalker', "3rd order is by Luke.");
});
it("distinct", function * (){
//don't add "id" to select clause.
var orders = yield pers.q().f("Order").s("dateTime, customer.*").distinct().exec();
assert(orders.length === 2, "2 distinct orders, one by Luke, one by Vader.");
});
it("query options", function * (){
//don't add "id" to select clause.
var orders = yield pers.q().f("Order").s("dateTime, customer.*").options("DISTINCT").exec();
assert(orders.length === 2, "2 distinct orders, one by Luke, one by Vader.");
});
it("calc", function * () {
var orders = yield pers.q().f("Order")
.w("{customer.name} = ?", "Darth Vader").calc().limit("?", 1).exec();
assert(typeof orders === 'object', "with calc, exec return is not an array but an object");
assert("objects" in orders, "should have an objects key.");
assert(Array.isArray(orders.objects), "objects key is an array.");
assert("count" in orders, "should have a count key.");
assert(orders.objects.length === 1, "We limited the result set to 1 item");
assert(orders.count === 2, "But vader has 2 orders in total.");
});
it("index with fields", function * () {
//we used non-unique index, meaning we can't get at most 1 order per customer:
var orders = yield pers.q().f("Order").s("*, customer.*")
.index("customer.name").order("{customer.name} desc").exec();
assert(orders instanceof Map, "exec with index return value is a Map object.");
assert(orders.size === 2, "No where or limit, but we have 1 order from each customer.");
assert(orders.get("Luke Skywalker") instanceof pers.m.Order, "each item is an order");
assert(Array.from(orders.keys())[0] === 'Luke Skywalker', ".index() must respect the order by clause.");
assert(Array.from(orders.keys())[1] === 'Darth Vader', ".index() must respect the order by clause.");
});
it("index with callbacks", function * () {
//we used a unique index this time:
var orders = yield pers.q().f("Order").s("*, customer.*")
.index( order => order.id + ":" + order.customer.name)
.order("{customer.name} desc, {id} asc").exec();
assert(orders instanceof Map, "exec with index return value is a Map object.");
assert(orders.size === 3, "All items should have been returned.");
assert(orders.get("3:Luke Skywalker") instanceof pers.m.Order, "each item is an order");
assert(Array.from(orders.keys())[0] === '3:Luke Skywalker', ".index() must respect the order by clause.");
assert(Array.from(orders.keys())[1] === '1:Darth Vader', ".index() must respect the order by clause.");
assert(Array.from(orders.keys())[2] === '2:Darth Vader', ".index() must respect the order by clause.");
});
it("calc with index", function * () {
var orders = yield pers.q().f("Order").calc().limit(1)
.w("{customer.name} = ?", "Darth Vader").index("customer.name").exec();
assert(typeof orders === 'object', "with calc, exec return is not an array but an object");
assert("objects" in orders, "should have an objects key.");
assert(orders.objects instanceof Map, "objects is a Map.");
assert("count" in orders, "should have a count key.");
assert(orders.objects.size === 1, "We limited the result set to 1 item");
assert(orders.count === 2, "But vader has 2 orders in total.");
assert(orders.objects.get("Darth Vader") instanceof pers.m.Order, "Each item in the map is an Order.");
});
it("selectAlias", function * () {
var orders = yield pers.q().f("Order").s("*").o("{customer.name} desc")
.sa('{customer.name} AS "customerName", 1+1 as two').exec();
assert("two" in orders[0], "there must be a key named 'two' in each order.");
assert("customerName" in orders[0], "there must be a key named 'customerName' in each order.");
assert(orders[0].customerName === 'Luke Skywalker', "selected alias customerName must reflect the name.");
assert(+orders[0].two === 2, "two should come as 2 in all databases.");
});
//this is failing for postgres, because the builder adds customer.id into the select clause
//and postgres doesn't like non-aggregated columns from non-group in the query.
it("group by", function * () {
var orders = yield pers.q().f("Order").sa("{customer.name}")
.g("{customer.name}").exec();
assert(orders.length === 2, "One order from each customer means 2.");
});
it("having, normal method call", function * () {
var orders = yield pers.q().f("Order").sa("{customer.name} as name")
.g("{customer.name}").h("{customer.name} = ?", "Darth Vader").exec();
assert(orders.length === 1, "Only 1 customer should be in the results.");
assert(orders[0].name === 'Darth Vader', "and that is Darth Vader.");
});
it("having, tagged template call", function * () {
var orders = yield pers.q().f("Order").sa("{customer.name} as name")
.g("{customer.name}").h`{customer.name} = ${"Darth Vader"}`.exec();
assert(orders.length === 1, "Only 1 customer should be in the results.");
assert(orders[0].name === 'Darth Vader', "and that is Darth Vader.");
});
it("streaming queries", function * () {
var called = 0;
var endCalled = false;
var totalCount = 0;
var endCalled = false;
var orders = yield pers.q().f("Order").s("*, customer.*").index("id").calc()
.on("object", (order, index) => {
called ++;
assert(order instanceof pers.m.Order, "an order object is of type Order");
assert("customer" in order, "there is a customer field...");
assert("name" in order.customer, "and it thas a name.");
assert(typeof index === "number" && index > 0, "we indexed by id, so it must be nr greater than zero.");
})
.on("calc", count => {
totalCount = count;
})
.on("end", () => {
endCalled = true;
})
.exec();
assert(called === 3, "object callback should have been called 3 times.");
assert("count" in orders, "even streaming queries return a count field with calc.");
assert(orders.count === 3, "which is 3.");
assert(totalCount === 3, "calc callback must be called with count=3 too");
assert(orders.objects === true, "we don't have real objects here, only true.");
assert(endCalled === true, "end callback should have been called.");
});
it("field abstraction over affix", function * () {
pers.setAbstractAffix("_en", "suffix");
var products = yield pers.q().f("Product").s("id, title")
.index("title")
.w("{title} like ?", "%Bl%")
.o("{title}")
.exec();
assert(products.has("Black Shirt"), "Black shirt must exit in our map");
assert(products.has("Blue Shirt"), "Blue shirt must exit in our map");
var bs = products.get("Blue Shirt");
assert("title" in bs, "Product must have a 'title' field.");
assert(bs.title === 'Blue Shirt', "and it must read 'Blue Shirt'");
assert( ! ("title_en" in bs), "title_en, shoud not appear in the object, we didn't ask for it.");
assert(Array.from(products.keys())[0] === 'Black Shirt', "order by should work under abstraction and be respected.");
});
it("field abstraction over affix cancellation", function * () {
//cancel only _en
pers.setAbstractAffix("_en", null);
try {
var products = yield pers.q().f("Product").s("id, title").exec();
assert(false, "Should not come here.");
} catch (err) {
assert(err.message.includes("cannot be resolved"), "must report unresolved field.");
}
//reset it to _tr
pers.setAbstractAffix("_tr");
var products = yield pers.q().f("Product").s("title").w("{title} like ?", ["%Beyaz%"]).exec();
assert(products.length === 1, "Only 1 product must be in the record set.");
assert(products[0].title === 'Beyaz Gömlek', "and it is the white shirt.");
//cancel all.
pers.setAbstractAffix(null);
try {
var products = yield pers.q().f("Product").s("title").w("{title} like ?", ["%Beyaz%"]).exec();
assert(false, "Should not come here.");
} catch (err) {
assert(err.message.includes("cannot be resolved"), "must report unresolved field.");
}
});
it("tomany queries with inline select", function * () {
//pull the entire data in our database starting with the Customer:
var customers = yield pers.q().f("Customer")
.s("*, orders.*, orders.orderItems.*, orders.orderItems.product.*")
.o("{name} asc") //Vader comes first
.exec();
assert("orders" in customers[0], "each customer has an orders key.");
assert(Array.isArray(customers[0].orders), "which is an array");
assert(customers[0].name === "Darth Vader", "first is vader");
assert(customers[0].orders.length === 2, "Vader has 2 orders");
assert(Array.isArray(customers[0].orders[0].orderItems), "orderItems is an array.");
assert(customers[0].orders[0].orderItems.length === 2, "Vader's first order has 2 items");
assert("title_en" in customers[0].orders[0].orderItems[0].product, "each orderItem has product property, which has a title_en");
});
it("tomany queries with toMany() method and clauses", function * () {
var customers = yield pers.q().f("Customer")
.s("*").o("{name} asc") //Vader comes first
.toMany("orders").s("*").w("{customer.name} = ?", "Luke Skywalker")
.toMany("orders.orderItems").s("*, product.*").o("{product.title_en}")
.exec();
assert("orders" in customers[0], "each customer has an orders key.");
assert(Array.isArray(customers[0].orders), "which is an array");
assert(customers[0].name === "Darth Vader", "first is vader");
//despite we have all customers, orders subquery is filtered to fetch only Luke's orders:
assert(customers[0].orders.length === 0, "We didn't select Vader's orders")
assert(customers[1].orders.length === 1, "Luke has 1 orders");
assert(Array.isArray(customers[1].orders[0].orderItems), "orderItems is an array.");
assert(customers[1].orders[0].orderItems.length === 2, "Luke's only order has 2 items");
assert("title_en" in customers[1].orders[0].orderItems[0].product, "each orderItem has product property, which has a title_en");
assert(customers[1].orders[0].orderItems[0].product.title_en === 'Blue Shirt', "Luke's first item reads Blue Shirt as we ordered the items by their title_en");
});
it("tomany queries with toMany() method and parallel toMany queries", function * () {
var customers = yield pers.q().f("Customer")
.s("*").o("{name} asc") //Vader comes first
.toMany("orders").s("*").w("{customer.name} = ?", "Luke Skywalker")
.toMany("orders.orderItems").s("*, product.*").o("{product.title_en}")
.toMany("addresses").s("*").index("id") //not part of previous toManys.
.exec();
assert("orders" in customers[0], "each customer has an orders key.");
assert(Array.isArray(customers[0].orders), "which is an array");
assert(customers[0].name === "Darth Vader", "first is vader");
//despite we have all customers, orders subquery is filtered to fetch only Luke's orders:
assert(customers[0].orders.length === 0, "We didn't select Vader's orders")
assert(customers[1].orders.length === 1, "Luke has 1 orders");
assert(Array.isArray(customers[1].orders[0].orderItems), "orderItems is an array.");
assert(customers[1].orders[0].orderItems.length === 2, "Luke's only order has 2 items");
assert("title_en" in customers[1].orders[0].orderItems[0].product, "each orderItem has product property, which has a title_en");
assert(customers[1].orders[0].orderItems[0].product.title_en === 'Blue Shirt', "Luke's first item reads Blue Shirt as we ordered the items by their title_en");
//addresses
assert("addresses" in customers[0], "customers have their adresses,");
assert(customers[0].addresses instanceof Map, "and they are Maps.");
//we may not have Vader's orders but we have his addresses:
assert(customers[0].addresses.get(77) instanceof pers.m.Address, "Each item in addrsses is an Address.");
assert(customers[0].addresses.get(77).address === 'Death Star 1', "One of Vader's addresses is DS1.");
});
});
}
});