allex_mssqlexecutorlib
Version:
AllexJS lib for executing queries against the MS SQL Server
875 lines (854 loc) • 19.6 kB
JavaScript
var delaytime = 1000;
var beginTxn = 'BEGIN TRANSACTION';
var commitTxn = 'COMMIT TRANSACTION';
var testFunc1 = function (res) {
console.log('last result', require('util').inspect(res, {depth: 11, colors: true}));
}
var verbatimTests = [
[
'Verbatim with no fields, no proc',
{
type: 'verbatim',
value: 7
},
7
],
[
'Verbatim with no fields, scalar value, has proc',
{
type: 'verbatim',
value: 7,
proc: function (val) {
return val-4;
}
},
3
],
[
'Verbatim with no fields, array value, has proc',
{
type: 'verbatim',
value: [7],
proc: function (val) {
return val-4;
}
},
3
],
[
'Verbatim with field, scalar value, has proc',
{
type: 'verbatim',
value: [7],
field: 'TheC',
proc: function (val) {
return val-4;
}
},
{
TheC: 3
}
]
];
var lookupTests = [
[
'Lookup with no fields, no proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5'
},
[0]
],
[
'Lookup with no fields, has proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
proc: function (c) {return c-17;}
},
-17
],
[
'Lookup with no fields, has procs',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
proc: [function (val) {return val-17;}]
},
[-17]
],
[
'Lookup with field, has procs',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
field: 'TheC',
proc: [function (val) {return val-17;}]
},
{
TheC: [-17]
}
],
[
'Lookup with field and result, has procs',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
result: {
Mm: 'no',
TheC: null
},
field: 'TheC',
proc: [function (val) {return val-17;}]
},
{
Mm: 'no',
TheC: [-17]
}
],
[
'Lookup with 2 fields from table, no self fields, has procs',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'b, c',
where: 'a=5',
proc: [
null,
function (val) {return val-17;}
],
},
['bla5', -17]
],
[
'Lookup with 2 fields from table, no self fields, has modulating proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'b, c',
where: 'a=5',
proc: function (b, c) {return b+'*'+c;},
},
'bla5*0'
],
[
'Lookup with fields, no proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
fields: ['TheC']
},
{TheC:0}
],
[
'Lookup with fields and proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
fields: ['TheC'],
proc: [function (val) {return val+5;}]
},
{TheC:5}
],
[
'Lookup with fields and promised proc',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'c',
where: 'a=5',
fields: ['TheC'],
proc: [function (val) {
return q.delay(delaytime, val+5);
}]
},
{TheC:5}
],
[
'Lookup with 2 fields and mixed procs (using this)',
{
type: 'lookup',
table: 'AllexTestTable',
what: 'b, c',
where: 'a=5',
fields: ['TheB', 'TheC'],
specval: 8,
proc: [
function (val) {
return q.delay(delaytime, val+this.specval);
},
function (val) {
return val-50;
}
]
},
{TheB: 'bla58', TheC:-50}
]
];
var recordsetTests = [
[
'Recordset, sentence, no fields, no proc',
{
type: 'recordset',
sentence: 'SELECT a FROM AllexTestTable WHERE a<8'
},
[
{a: 5},{a: 6},{a: 7}
]
],
[
'Recordset, sentence, no fields, has proc',
{
type: 'recordset',
sentence: 'SELECT a FROM AllexTestTable WHERE a<8',
proc: function (rec0, rec1, rec2) {
return rec0.a+rec1.a+rec2.a;
}
},
18
],
[
'Recordset, sentence, no fields, has rsproc',
{
type: 'recordset',
sentence: 'SELECT a FROM AllexTestTable where a<8',
rsproc: function (rec) {
return rec.map(function (r) {return r.a-5})
}
},
[
0, 1, 2
]
],
[
'Recordset, template, has field, has recproc',
{
type: 'recordset',
sentence: {
template: 'SELECT a FROM TABLE WHERE a<8',
replacements: {
TABLE: 'AllexTestTable'
}
},
recproc: function (rec) {
return rec.a-5;
},
result: {
Mm: 'nono',
myLookups: null
},
field: 'myLookups'
},
{
Mm: 'nono',
myLookups: [
0, 1, 2
]
}
]
];
var recordset2arryofscalarsTests = [
[
'Recordset2ArryOfScalars, sentence, no fields, no scalarproc',
{
type: 'recordset2arryofscalars',
sentence: 'SELECT a FROM AllexTestTable WHERE a<8',
scalarfield: 'a'
},
[5, 6, 7]
],
[
'Recordset2ArryOfScalars, sentence, no fields, has scalarproc',
{
type: 'recordset2arryofscalars',
sentence: 'SELECT a FROM AllexTestTable WHERE a<8',
scalarfield: 'a',
scalarproc: function (val) {
return val+2;
}
},
[7, 8, 9]
]
]
var firstrecordTests = [
[
'First record, no fields, no proc',
{
type: 'firstrecord',
sentence: {
template: 'SELECT a FROM TABLE',
replacements: {
TABLE: 'AllexTestTable'
}
}
},
[5]
],
[
'First record, has field, has proc',
{
type: 'firstrecord',
sentence: {
template: 'SELECT * FROM TABLE',
replacements: {
TABLE: 'AllexTestTable'
}
},
proc: function (a, b, c) {
return a - 5;
},
field: 'MyA'
},
{MyA: 0}
],
[
'First record, no fields, has procs',
{
type: 'firstrecord',
sentence: {
template: 'SELECT a FROM TABLE',
replacements: {
TABLE: 'AllexTestTable'
}
},
proc: [function (a) {
return a - 5;
}]
},
[0]
],
[
'First record, has fields, has procs',
{
type: 'firstrecord',
sentence: {
template: 'SELECT a FROM TABLE',
replacements: {
TABLE: 'AllexTestTable'
}
},
proc: [function (a) {
return a - 5;
}],
fields: ['MyA']
},
{MyA: 0}
]
];
var insertTestTemplate = [
null,
{
type: 'rowsaffected',
sentence: {
template: "INSERT INTO TABLE (a,b,c) VALUES (AVALUE, BVALUE, CVALUE)",
replacements: {
TABLE: 'AllexTestTable',
AVALUE: null,
BVALUE: null,
CVALUE: null
}
}
},
1
];
var _insertCount = 0;
function insertTest () {
_insertCount++;
var val = 1000+_insertCount;
return [
'INSERT '+val,
lib.extend({}, insertTestTemplate[1], {
sentence: {
replacements: {
AVALUE: val,
BVALUE: "'bla"+val+"'",
CVALUE: -val
}
}
}),
insertTestTemplate[2]
];
}
function singularIt (test) {
return it (test[0], function () {
this.timeout(1e7);
var ret = Executor.queue(test[1]).should.eventually.deep.equal(test[2]);
test = null;
return ret;
})
}
function singularTxnedIt (test) {
return it (test[0]+' under Transaction', function () {
this.timeout(1e7);
var ret = Executor.queue([
beginTxn,
test[1],
commitTxn
]).should.eventually.deep.equal([test[2]]);
test = null;
return ret;
})
}function singularFuncedIt (func, test) {
return it (test[0]+' with Function', function () {
this.timeout(1e7);
var ret = Executor.queue([
func,
test[1],
func
]).should.eventually.deep.equal([test[2]]);
test = null;
return ret;
})
}
function cmptitler (test) {
return test[0];
}
function cmpjober (test) {
return test[1];
}
function cmpexpecter (test) {
return test[2];
}
function arryIt (tests) {
return it ('Array:\n\t'+tests.map(cmptitler).join('\n\t'), function () {
this.timeout(1e7);
var ts = tests.map(cmpjober);
var tslen = ts.length;
if (tslen>2) {
ts.splice(1, 0, beginTxn);
ts.splice(tslen-2, 0, commitTxn);
} else {
ts.unshift(beginTxn);
ts.push(commitTxn);
}
var queue = Executor.queue(ts);
var exp = tests.map(cmpexpecter);
var ret = queue.should.eventually.deep.equal(exp);
/**
var ret = queue.then(function (res) {
return expect(res).to.deep.equal(exp);
})
/**/
tests = null;
return ret;
});
}
function compositeIt (tests) {
return it ('Composite:\n\t'+tests.map(cmptitler).join('\n\t'), function () {
this.timeout(1e7);
var queue = Executor.queue({
type: 'composite',
items: tests.map(cmpjober)
});
var exp = tests.map(cmpexpecter);
var ret = queue.should.eventually.deep.equal(exp);
/**
var ret = queue.then(function (res) {
return expect(res).to.deep.equal(exp);
})
/**/
tests = null;
return ret;
});
}
function randomInt (num) {
return Math.floor(Math.random()*num);
}
function randomIndex (arry) {
return randomInt(arry.length);
}
function randomItem (arry) {
var ret = arry[randomIndex(arry)];
return lib.isFunction(ret) ? ret() : ret;
}
function randomArryIt (testsarry) {
var i;
var tests = [];
var len = 5+randomInt(5);
for (i=0; i<len; i++) {
tests.push(randomItem(randomItem(testsarry)));
}
arryIt(tests);
}
function randomCompositeIt (testsarry) {
var i;
var tests = [];
var len = 5+randomInt(5);
for (i=0; i<len; i++) {
tests.push(randomItem(randomItem(testsarry)));
}
compositeIt(tests);
}
describe('Test SQL Queueing', function () {
it('Load lib', function () {
return setGlobal('Lib', require('..')(execlib));
});
it ('Create Executor', function () {
return setGlobal('Executor', new Lib.Executor(require('./config/connect')));
});
it ('Connect Executor', function () {
return Executor.connect();
});
/*
it ('Create Test Table', function () {
return qlib.promise2console(Executor.queue({
type: 'justdoit',
sentence: Lib.sqlsentencing.createTable({
name: 'AllexTestTable',
fields: [
{
name: 'a',
type: 'int',
nullable: false,
constraint: 'PRIMARY KEY'
},
{
name: 'b',
type: 'varchar (10)',
nullable: true
},
{
name: 'c',
type: 'int',
nullable: false
}
]
})
}), 'create');
});
it ('Truncate first', function () {
return qlib.promise2console(Executor.queue({
type: 'justdoit',
sentence: "TRUNCATE TABLE AllexTestTable"
}), 'truncate');
})
*/
it ('Create Test Table, Truncate it, Insert some values', function () {
var insstring = 'INSERT INTO AllexTestTable (a,b,c) '+Lib.sqlsentencing.toValuesOfHashArray([
{
a: 5,
b: 'bla5',
c: 0
},
{
a: 6,
b: 'bla6',
c: 0
},
{
a: 7,
b: 'bla7',
c: 0
},
], ['a', 'b', 'c']);
return qlib.promise2console(Executor.queue([{
type: 'justdoit',
sentence: Lib.sqlsentencing.createTable({
name: 'AllexTestTable',
fields: [
{
name: 'a',
type: 'int',
nullable: false,
constraint: 'PRIMARY KEY'
},
{
name: 'b',
type: 'varchar (10)',
nullable: true
},
{
name: 'c',
type: 'int',
nullable: false
}
]
})
},{
type: 'justdoit',
sentence: "TRUNCATE TABLE AllexTestTable"
},{
type: 'rowsaffected',
sentence: insstring,
}]), 'create_truncate_insert#1');
});
/**/
compositeIt([
insertTest(),
lookupTests[10],
insertTest(),
recordset2arryofscalarsTests[1],
insertTest()
]);
/**/
/**/
verbatimTests.forEach(singularIt);
lookupTests.forEach(singularIt);
recordsetTests.forEach(singularIt);
recordset2arryofscalarsTests.forEach(singularIt);
firstrecordTests.forEach(singularIt);
/**/
/**/
verbatimTests.forEach(singularTxnedIt);
lookupTests.forEach(singularTxnedIt);
recordsetTests.forEach(singularTxnedIt);
recordset2arryofscalarsTests.forEach(singularTxnedIt);
firstrecordTests.forEach(singularTxnedIt);
verbatimTests.forEach(singularFuncedIt.bind(null, testFunc1));
lookupTests.forEach(singularFuncedIt.bind(null, testFunc1));
recordsetTests.forEach(singularFuncedIt.bind(null, testFunc1));
recordset2arryofscalarsTests.forEach(singularFuncedIt.bind(null, testFunc1));
firstrecordTests.forEach(singularFuncedIt.bind(null, testFunc1));
/**/
/**/
for (var i=0; i<10; i++) {
randomArryIt([
verbatimTests,
lookupTests,
recordsetTests,
recordset2arryofscalarsTests,
firstrecordTests,
[insertTest]
]);
}
/**/
/**/
for (var i=0; i<10; i++) {
randomCompositeIt([
verbatimTests,
lookupTests,
recordsetTests,
recordset2arryofscalarsTests,
firstrecordTests,
[insertTest]
]);
}
/**/
/*composite with promising procs*/
compositeIt([
lookupTests[9],
lookupTests[10]
]);
/**/
/**/
it ('Insert some values again', function () {
var insstring = 'INSERT INTO AllexTestTable (a,b,c) '+Lib.sqlsentencing.toValuesOfHashArray([
{
a1: 15,
b1: 'bla15',
c1: 0
},
{
a1: 16,
b1: 'bla16',
c1: 0
},
{
a1: 17,
b1: 'bla17',
c1: 0
},
], ['a1', 'b1', 'c1']);
return qlib.promise2console(Executor.queue({
type: 'rowsaffected',
sentence: insstring
}), 'insert 2');
});
singularIt(insertTest());
it ('Find an inserted value', function () {
return Executor.queue({
type: 'recordset',
sentence: 'SELECT * FROM AllexTestTable WHERE a=15'
}).then(function (results) {
if (results[0].b !== 'bla15') {
throw new lib.Error('MISMATCH', 'Expected bla15 but got '+results[0].b);
}
console.log(results[0].b);
});
});
it ('Join with valuesOfScalarArray', function () {
return Executor.queue({
type: 'recordset',
sentence: [
'SELECT t.b FROM',
Lib.sqlsentencing.toValuesOfScalarArray([6], 'mycolumn'),
'q',
'LEFT JOIN AllexTestTable t',
'ON q.mycolumn=t.a'
].join(' ')
}).then(function (results) {
if (results[0].b !== 'bla6') {
throw new lib.Error('MISMATCH', 'Expected bla6 but got '+results[0].b);
}
console.log(results[0].b);
});
});
it ('Upsert a record that exists', function () {
this.timeout(1e7);
return Executor.queue([
'BEGIN TRANSACTION',
{
type: 'upsert',
tablename: 'AllexTestTable',
record: {
a: 15,
b: 'bla15_upd',
c: 15
},
selectfields: ['a'],
setfields: ['b']
},
'COMMIT TRANSACTION'
]).then(function (result) {
if (!result[0].updated) {
throw new lib.Error('NOT_UPDATED', 'Expected the record with a:15 to be updated');
}
console.log(result[0]);
});
});
it ('Find the upserted value', function () {
return Executor.queue({
type: 'recordset',
sentence: 'SELECT * FROM AllexTestTable WHERE a=15'
}).then(function (results) {
if (results[0].c !== 0) {
throw new lib.Error('MISMATCH', 'For c expected 0 but got '+results[0].b);
}
if (results[0].b !== 'bla15_upd') {
throw new lib.Error('MISMATCH', 'For b expected bla15_upd but got '+results[0].b);
}
console.log(results[0]);
});
});
it ('Upsert a record that does not exist', function () {
this.timeout(1e7);
return Executor.queue({
type: 'upsert',
tablename: 'AllexTestTable',
record: {
a: 55,
b: 'bla55',
c: 0
},
selectfields: ['a'],
setfields: ['b', 'c']
}).then(function (result) {
if (!result.inserted) {
throw new lib.Error('NOT_INSERTED', 'Expected the record with a:55 to be inserted');
}
console.log(result);
});
});
it ('Find the last upserted value', function () {
return Executor.queue({
type: 'recordset',
sentence: 'SELECT * FROM AllexTestTable WHERE a=55'
}).then(function (results) {
if (results[0].c !== 0) {
throw new lib.Error('MISMATCH', 'For c expected 0 but got '+results[0].b);
}
if (results[0].b !== 'bla55') {
throw new lib.Error('MISMATCH', 'For b expected bla15_upd but got '+results[0].b);
}
console.log(results[0]);
});
});
it ('Upsert many, all updates', function () {
this.timeout(1e7);
return Executor.queue({
type: 'upsertmany',
tablename: 'AllexTestTable',
records: [{
a: 15,
b: 'bla15_2',
c: 14
},{
a: 16,
b: 'bla16_2',
c: 15
}],
selectfields: ['a'],
setfields: ['b', 'c']
}).then(function (result) {
if (result.updated != 2) {
throw new lib.Error('NOT_UPDATED', 'Expected UpsertMany to update 2 records, but only '+result.updated+' got updated');
}
console.log(result);
});
});
it ('Upsert many, one insert, one update', function () {
this.timeout(1e7);
return Executor.queue({
type: 'upsertmany',
tablename: 'AllexTestTable',
records: [{
a: 15,
b: 'bla15_2',
c: 14
},{
a: 116,
b: 'bla116',
c: 115
}],
selectfields: ['a'],
setfields: ['b', 'c']
}).then(function (result) {
if (!(result.updated == 1 && result.inserted == 1)) {
throw new lib.Error('NOT_UPDATED', 'Expected UpsertMany to update 1 record and insert 1 record, but '+result.updated+' got updated and '+result.inserted+' got inserted');
}
console.log(result);
});
});
it ('Upsert many, all inserts', function () {
this.timeout(1e7);
return Executor.queue({
type: 'upsertmany',
tablename: 'AllexTestTable',
records: [{
a: 215,
b: 'bla215',
c: 214
},{
a: 216,
b: 'bla216',
c: 215
}],
selectfields: ['a'],
setfields: ['b', 'c']
}).then(function (result) {
if (result.inserted != 2) {
throw new lib.Error('NOT_INSERTED', 'Expected UpsertMany to insert 2 records, but only '+result.inserted+' got inserted');
}
console.log(result);
});
});
/**/
it ('Drop Test Table', function () {
this.timeout(1e7);
return qlib.promise2console(Executor.queue({
type: 'rowsaffected',
sentence: [
"IF EXISTS (SELECT * FROM SYSOBJECTS WHERE name='AllexTestTable' AND xtype='U')",
"DROP TABLE AllexTestTable"
].join(' '),
rowsaffectedcount: 2
}), 'drop');
});
it ('Destroy Executor', function () {
Executor.destroy();
});
});