Ghost/core/test/utils/mocks/knex.js
2018-04-06 13:35:38 +02:00

296 lines
12 KiB
JavaScript

'use strict';
const mockKnex = require('mock-knex'),
_ = require('lodash'),
debug = require('ghost-ignition').debug('tests:knex-mock'),
DataGenerator = require('../fixtures/data-generator'),
knex = require('../../../server/data/db').knex;
/**
* Knex mock. The database is our Datagenerator.
* You can either self register queries or you simply rely on the data generator data.
*
* Please extend if you use-case does not work.
*
* @TODO: sqlite3 :memory: mode wasn't working for me
*/
class KnexMock {
initialiseDb() {
this.db = {};
_.each(_.pick(_.cloneDeep(DataGenerator.forKnex), [
'posts',
'users',
'tags',
'permissions',
'roles',
'posts_authors',
'posts_tags'
]), (objects, tableName) => {
this.db[tableName] = [];
_.each(objects, (object) => {
this.db[tableName].push(object);
});
});
}
resetDb() {
return this.initialiseDb();
}
mock(options) {
options = options || {autoMock: true};
mockKnex.mock(knex);
this.initialiseDb();
this.tracker = mockKnex.getTracker();
this.tracker.install();
if (options.autoMock) {
this.tracker.on('query', (query) => {
query.sql = query.sql.replace(/`/g, '"');
debug('#### Query start.');
debug(query.sql);
// CASE: transactions
if (query.sql.match(/BEGIN|COMMIT|ROLLBACK/)) {
query.response();
debug('#### Query end.\n');
return;
}
if (query.method === 'select') {
if (query.bindings.length && query.sql.match(/where/)) {
// CASE: joins should return e.g. `posts_tags=[tag,tag]`
if (query.sql.match(/inner\sjoin/)) {
let targetTable = query.sql.match(/inner\sjoin\s(\"\w+\")/)[1],
targetAttribute = query.sql.match(/on\s\"\w+\"\.(\"\w+\")/)[1],
joinAttribute = query.sql.match(/on\s\"\w+\"\.\"\w+\"\s\=\s\"\w+\"\.(\"\w+\")/)[1],
joinTable = query.sql.match(/on\s\"\w+\"\.\"\w+\"\s\=\s(\"\w+\")/)[1],
targetIdentifier = query.sql.match(/(\"\w+\")\sin\s\(\?\)/),
value = query.bindings[0],
targetEntries,
toReturn = [];
if (targetIdentifier) {
targetIdentifier = targetIdentifier[1];
} else {
targetIdentifier = query.sql.match(/where\s\"\w+\"\.\"(\w+)\"\s\=/)[1];
}
targetTable = targetTable.replace(/"/g, '');
targetIdentifier = targetIdentifier.replace(/"/g, '');
targetAttribute = targetAttribute.replace(/"/g, '');
joinTable = joinTable.replace(/"/g, '');
joinAttribute = joinAttribute.replace(/"/g, '');
debug(targetTable, targetIdentifier, targetAttribute, joinTable, joinAttribute);
targetEntries = _.filter(this.db[targetTable], ((existing) => {
if (existing[targetIdentifier] === value) {
return true;
}
}));
if (targetEntries && targetEntries.length) {
_.each(targetEntries, ((target) => {
const found = _.find(this.db[joinTable], ((joinEntry) => {
if (joinEntry[joinAttribute] === target[targetAttribute]) {
return true;
}
}));
_.each(target, function (value, key) {
let match = query.sql.match(new RegExp('\\"' + targetTable + '\\"\\.\\"' + key + '"\\sas\\s(\\"\\w+\\")'));
// CASE: e.g. id
if (match) {
match = match[1];
match = match.replace(/"/g, '');
found[match] = value;
}
});
if (found) {
toReturn.push(found);
}
}));
// @TODO: This is not really generic ;)
toReturn = _.orderBy(toReturn, ['_pivot_sort_order'], ['asc']);
query.response(toReturn);
debug('#### Query end.\n');
} else {
query.response([]);
debug('#### Query end.\n');
}
} else {
let tableName = query.sql.match(/from\s\"(\w+)\"/)[1],
where = query.sql.match(/\"(\w+)\"\s\=\s\?/),
value = query.bindings[0],
dbEntry;
// where "users"."id" in ('1')
if (!where) {
where = query.sql.match(/\"\w+\"\.\"(\w+)\"\sin\s\(\?\)/)[1];
} else {
where = where[1];
}
debug(tableName, where, value);
dbEntry = _.filter(this.db[tableName], ((existing) => {
if (existing[where] === value) {
return true;
}
}));
if (dbEntry) {
// select fields
dbEntry = _.map(dbEntry, (obj) => {
let keys = query.sql.match(/select\s(\".*\"\,?)+\sfrom/);
if (keys) {
keys = keys[1];
keys = keys.replace(/"/g, '');
keys = keys.replace(/\s/g, '');
keys = keys.split(',');
return _.pick(obj, keys);
}
return obj;
});
query.response(dbEntry);
debug('#### Query end.\n');
} else {
query.response([]);
debug('#### Query end. Not found\n');
}
}
} else {
const tableName = query.sql.match(/from\s\"(\w+)\"/)[1];
query.response(this.db[tableName]);
}
} else if (query.method === 'insert') {
const tableName = query.sql.match(/into\s\"(\w+)\"/)[1];
let keys = query.sql.match(/\(([^)]+)\)/)[1],
entry = {};
keys = keys.replace(/"/g, '');
keys = keys.replace(/\s/g, '');
keys = keys.split(',');
_.each(keys, (key, index) => {
entry[key] = query.bindings[index];
});
if (!this.db[tableName]) {
this.db[tableName] = [];
}
this.db[tableName].push(entry);
query.response(entry);
debug('#### Query end.\n');
} else if (query.method === 'update') {
let tableName = query.sql.match(/update\s\"(\w+)\"/)[1],
where = query.sql.match(/where\s\"(\w+)\"\s\=\s\?/)[1],
andWhere = query.sql.match(/where\s\"\w+\"\s\=\s\?\sand\s\"(\w+)\"/),
valueWhere,
valueAndWhere,
dbEntry;
if (andWhere) {
andWhere = andWhere[1];
valueWhere = query.bindings.slice(1, -1)[0];
valueAndWhere = query.bindings.slice(-1)[0];
} else {
valueWhere = query.bindings.slice(-1)[0];
}
debug(tableName, where, valueWhere, andWhere, valueAndWhere, query.bindings);
dbEntry = _.find(this.db[tableName], ((existing) => {
if (existing[where] === valueWhere) {
if (andWhere) {
if (existing[andWhere] === valueAndWhere) {
return true;
}
} else {
return true;
}
}
}));
if (!dbEntry) {
query.response([]);
debug('#### Query end. Can\'t update - not found.\n');
} else {
let keys = query.sql.match(/set(.*)where/)[1],
entry = {};
keys = keys.match(/\"\w+\"/g).join(',');
keys = keys.replace(/"/g, '');
keys = keys.replace(/\s/g, '');
keys = keys.split(',');
debug('set', keys);
_.each(keys, (key, index) => {
entry[key] = query.bindings[index];
dbEntry[key] = entry[key];
});
query.response(entry);
debug('#### Query end.\n');
}
} else {
let tableName = query.sql.match(/from\s\"(\w+)\"/)[1],
where = query.sql.match(/where\s\"(\w+)\"\s\=\s\?/)[1],
andWhere = query.sql.match(/where\s\"\w+\"\s\=\s\?\sand\s\"(\w+)\"/),
valueWhere,
valueAndWhere;
valueWhere = query.bindings[0];
if (andWhere) {
andWhere = andWhere[1];
valueAndWhere = query.bindings[1];
}
debug(tableName, where, valueWhere, andWhere, valueAndWhere, query.bindings);
this.db[tableName] = this.db[tableName].filter((existing) => {
if (existing[where] === valueWhere) {
if (andWhere) {
if (existing[andWhere] === valueAndWhere) {
return false;
} else {
return true;
}
} else {
return false;
}
} else {
return true;
}
});
query.response([]);
}
});
}
return this.tracker;
}
unmock() {
this.tracker.uninstall();
mockKnex.unmock(knex);
}
}
module.exports = KnexMock;