UNPKG

think-model

Version:

An adapter-based ORM for ThinkJS 3.x

937 lines (826 loc) 21.7 kB
const {test} = require('ava'); const Model = require('../lib/model'); const handle = require('think-model-abstract'); const db = new handle(new Model('test', {handle})); test('model instance normal', t => { t.plan(3); const config = { handle }; const modelName = 'post'; const model = new Model(modelName, config); t.is(model.modelName, modelName); t.is(model.config, config); t.deepEqual(model.options, {}); }); test('model instance abnormal', t => { try { new Model({handle: 222}); t.fail(); } catch (e) { t.pass(); } }); test('model get db', t => { const config = { handle }; const model = new Model(config); t.true(model.db() instanceof config.handle); }); test('model get models', t => { t.plan(2); const models = {post: {}}; const model = new Model({handle}); t.deepEqual(model.models, {}); model.models = models; t.is(model.models, models); }); test('model get table prefix', t => { t.plan(2); t.is((new Model({handle})).tablePrefix, ''); const model = new Model({handle, prefix: 'fk_'}); t.is(model.tablePrefix, 'fk_'); }); test('model get table name', t => { t.plan(2); let model = new Model('post', {handle}); t.is(model.tableName, 'post'); model = new Model('post', {handle, prefix: 'fk_'}); t.is(model.tableName, 'fk_post'); }); test('model get pk', t => { t.plan(2); const model = new Model({handle}); t.is(model.pk, 'id'); model._pk = 'user_id'; t.is(model.pk, model._pk); }); test('model get model inline', t => { t.plan(3); const model = new Model('post', {handle, prefix: 'fk_'}); model.models = { 'admin/user': function(tableName) { this.tableName = tableName; } }; t.is(model.model('user').tablePrefix, 'fk_'); t.is(model.model('user').models, model.models); t.is(model.model('admin/user').tableName, 'user'); }); test('model set cache option', t => { t.plan(4); const model = new Model('post', { handle, cache: { type: 'file', handle: () => {} } }); model.cache(500); t.is(model.options.cache._keyTimeout, 500); model.cache('page', {timeout: 300}); t.is(model.options.cache.key, 'page'); t.is(model.options.cache._keyTimeout, 300); model.cache('page', {key: 'post'}); t.is(model.options.cache.key, 'post'); }); test('model set limit', t => { t.plan(6); const model = new Model('post', {handle}); model.limit(); t.is(model.options.limit, undefined); model.limit([1]); t.deepEqual(model.options.limit, [1, undefined]); model.limit([1, 2]); t.deepEqual(model.options.limit, [1, 2]); model.limit(-1, -1); t.deepEqual(model.options.limit, [0, 0]); model.limit([-1, -10]); t.deepEqual(model.options.limit, [0, 0]); model.limit([]); t.deepEqual(model.options.limit, [0, undefined]); }); test('model set page', t => { const model = new Model('post', {handle}); model.page(); t.deepEqual(model.options.limit, [0, 10]); model.page(0); t.deepEqual(model.options.limit, [0, 10]); model.page(2); t.deepEqual(model.options.limit, [10, 10]); model.page(0, 30); t.deepEqual(model.options.limit, [0, 30]); model.page([3, 20]); t.deepEqual(model.options.limit, [40, 20]); }); test('model set where', t => { t.plan(4); const model = new Model('post', {handle}); model.where(); t.is(model.options.where, undefined); model.where('hello'); t.is(model.options.where._string, 'hello'); model.options.where = 'hello'; model.where('123'); t.is(model.options.where._string, '123'); delete model.options.where; model.where({id: ['>', 30]}); t.deepEqual(model.options.where, {id: ['>', 30]}); }); test('model set field reverse', t => { t.plan(8); const model = new Model('post', {handle}); model.field(); t.is(model.options.field, undefined); t.is(model.options.fieldReverse, undefined); model.field('hello'); t.is(model.options.field, 'hello'); t.is(model.options.fieldReverse, false); model.field('hello', true); t.is(model.options.field, 'hello'); t.is(model.options.fieldReverse, true); model.fieldReverse('hello2'); t.is(model.options.field, 'hello2'); t.is(model.options.fieldReverse, true); }); test('model set table name', t => { t.plan(4); const model = new Model('post', {handle, prefix: 'fk_'}); model.table(); t.is(model.options.table, undefined); model.table(' user '); t.is(model.options.table, 'fk_user'); model.table('SELECT * FROM user'); t.is(model.options.table, 'SELECT * FROM user'); model.table('user', true); t.is(model.options.table, 'user'); }); test('model set union', t => { t.plan(3); const model = new Model('post', {handle}); model.union(); t.is(model.options.union, undefined); model.union('test'); t.deepEqual(model.options.union, [{union: 'test', all: false}]); model.union('test2', true); t.deepEqual(model.options.union, [{union: 'test', all: false}, {union: 'test2', all: true}]); }); test('model set join', t => { t.plan(3); const model = new Model('post', {handle}); model.join(); t.is(model.options.join, undefined); model.join(222); t.deepEqual(model.options.join, [222]); model.join([1, 2, 3, 4]); t.deepEqual(model.options.join, [222, 1, 2, 3, 4]); }); test('model set order alias having group lock auto explan distinct', t => { t.plan(9); const model = new Model('post', {handle}); t.is(model.order('id').options.order, 'id'); t.is(model.alias('user').options.alias, 'user'); t.is(model.having('user').options.having, 'user'); t.is(model.group('user').options.group, 'user'); t.is(model.lock('user').options.lock, 'user'); t.is(model.auto('user').options.auto, 'user'); t.is(model.explain('user').options.explain, 'user'); t.is(model.distinct('user').options.field, 'user'); t.deepEqual(model.distinct({field: 'user'}).options.distinct, {field: 'user'}); }); test('model parse options', async t => { t.plan(4); const adapter = function() {}; adapter.prototype.getReverseFields = function() { return ['id', 'name', 'title']; }; const model = new Model('post', {handle: adapter}); t.deepEqual( await model.parseOptions(3), {table: 'post', pk: 'id', tablePrefix: '', where: {id: '3'}} ); t.deepEqual( await model.parseOptions('3,4'), {table: 'post', pk: 'id', tablePrefix: '', where: {id: {IN: '3,4'}}} ); t.deepEqual( await model.parseOptions({table: 'user', where: {id: 3}}), {table: 'user', pk: 'id', tablePrefix: '', where: {id: 3}} ); model.config.prefix = 'fk_'; model.options = { field: 'title', fieldReverse: true }; t.deepEqual( await model.parseOptions(), {table: 'fk_post', pk: 'id', tablePrefix: 'fk_', field: ['id', 'name', 'title']} ); }); test('model add data', async t => { t.plan(2); const adapter = class { add() { return 3; } parseData(data) { return data; } }; const model = new Model('post', {handle: adapter}); try { await model.add(); t.fail(); } catch (e) { t.pass(); }; const result = await model.add({title: 'hello', content: 'hello world'}); t.is(result, 3); }); test('model then add data', async t => { t.plan(3); const model = new Model('post', {handle: class { add(data) { t.deepEqual(data, {title: 'hello2', content: 'hello world again!'}); return 2; } parseData(data) { return data; } }}); model.find = function() { if (this.options.where.id === 1) { return {id: 1, title: 'hello', content: 'hello world'}; } return {}; }; let result = await model.thenAdd({title: 'hello2', content: 'hello world again!'}, {id: 1}); t.deepEqual(result, {type: 'exist', id: 1}); result = await model.thenAdd({title: 'hello2', content: 'hello world again!'}, {id: 2}); t.deepEqual(result, {type: 'add', id: 2}); }); test('model then update data exist', async t => { t.plan(2); const model = new Model('post', {handle}); model.find = function() { return {id: 1, title: 'test', content: 'world'}; }; model.update = function(data) { t.is(this.options.where.id, 1); return {id: 1, title: 'hello', content: 'world'}; }; const result = await model.thenUpdate({title: 'hello'}, {id: 1}); t.deepEqual(result, 1); }); test('model then update data not exist', async t => { t.plan(2); const model = new Model('post', {handle}); model.find = function() { t.is(this.options.where.id, 3); return {}; }; model.add = function(data) { return 4; }; const result = await model.thenUpdate({title: 'hello', content: 'world'}, {id: 3}); t.is(result, 4); }); test('model add many data error', async t => { t.plan(2); const model = new Model('post', {handle: class { parseData(data) { return data; } addMany() { return [1, 2, 3, 4]; } }}); try { await model.addMany({title: 'hello'}); } catch (e) { t.pass(); } try { await model.addMany([1, 2]); } catch (e) { t.pass(); } }); test('model add many data', async t => { t.plan(6); const model = new Model('post', {handle: class { parseData(data) { return data; } addMany(data) { t.true(data.every(d => d.user === 'lizheming')); return [1, 2, 3, 4]; } }}); const addData = [ {title: 'hello', content: 'world'}, {title: 'hello1', content: 'world1'}, {title: 'hello2', content: 'world2'}, {title: 'hello3', content: 'world3'} ]; model.beforeAdd = function(data) { data.user = 'lizheming'; return data; }; model.afterAdd = function(data) { t.true(data.hasOwnProperty('id')); }; const result = await model.addMany(addData); t.deepEqual(result, [1, 2, 3, 4]); }); test('model delete data', async t => { t.plan(3); const model = new Model('post', {handle: class { delete(options) { return 1; } }}); const options = {id: 3}; model.beforeDelete = model.afterDelete = function(opt) { t.is(opt.id, options.id); return opt; }; const result = await model.delete(options); t.is(result, 1); }); test('model update data', async t => { t.plan(6); const model = new Model('post', {handle: class { parseData(data, isUpdate) { t.true(isUpdate); return data; } update(data, options) { t.deepEqual(options.where, {id: 3}); t.deepEqual(data, {title: 'hello'}); return 'update data'; } }}); try { await model.update({title: 'hello'}); } catch (e) { t.pass(); } const result = await model.update({title: 'hello', id: 3}); t.is(result, 'update data'); }); test('model updateMany data error', async t => { const model = new Model('post', {handle: class { parseData(data) { return data; } update() { return true; } }}); try { await model.updateMany({id: 3, title: 'hello'}); t.fail(); } catch (e) { t.deepEqual(model.options, {}); } }); test('updateMany has no pk', async t => { const model = new Model('post', {handle: class { parseData(data) { return data; } update() { return true; } }}); try { await model.updateMany([{title: 'hello'}]); t.fail(); } catch (e) { t.deepEqual(model.options, {}); } }); test('updateMany normal', async t => { t.plan(5); const model = new Model('post', {handle: class { parseData(data) { return data; } }}); model.update = function(data, options) { if (data.id === 3) { t.deepEqual(data, {id: 3, title: 'hello1', content: 'world1'}); } else { t.deepEqual(data, {id: 10, title: 'hello2', content: 'world2'}); } t.deepEqual(options, {}); return data.id; }; const result = await model.updateMany([ {id: 3, title: 'hello1', content: 'world1'}, {id: 10, title: 'hello2', content: 'world2'} ], {}); t.deepEqual(result, [3, 10]); }); test('model find data', async t => { t.plan(5); const options = { pk: 'id', limit: 1, table: 'post', tablePrefix: '', where: { id: 3 } }; const data = {title: 'hello', content: 'world'}; const model = new Model('post', {handle: class { select(opt) { t.deepEqual(opt, options); return [data]; } }}); model.beforeFind = function(opt) { t.deepEqual(opt, options); return opt; }; model.afterFind = function(findData, opt) { t.deepEqual(findData, data); t.deepEqual(opt, options); return findData; }; const result = await model.where({id: 3}).limit([0, 10]).find(); t.deepEqual(result, data); }); test('model select data', async t => { t.plan(5); const options = { pk: 'id', table: 'post', tablePrefix: '', where: { id: ['>', 10] } }; const data = [{title: 'hello', content: 'world'}]; const model = new Model('post', {handle: class { select(opt) { t.deepEqual(opt, options); return data; } }}); model.beforeSelect = function(opt) { t.deepEqual(opt, options); return opt; }; model.afterSelect = function(selectData, opt) { t.deepEqual(selectData, data); t.deepEqual(opt, options); return selectData; }; const result = await model.where({id: ['>', 10]}).select(); t.deepEqual(result, data); }); test.todo('selectAdd'); test.todo('countSelect'); test('model get field normal', async t => { t.plan(2); const model = new Model('post', {handle: class { select(options) { t.deepEqual(options, { pk: 'id', table: 'post', tablePrefix: '', field: 'content, title' }); return [ {title: 'hello1', content: 'world1'}, {title: 'hello2', content: 'world2'} ]; } }}); const result = await model.getField('content, title'); t.deepEqual(result, { title: ['hello1', 'hello2'], content: ['world1', 'world2'] }); }); test('model get field one data', async t => { t.plan(2); const model = new Model('post', {handle: class { select(options) { t.deepEqual(options, { pk: 'id', limit: 1, table: 'post', tablePrefix: '', field: 'content, title' }); return [ {title: 'hello1', content: 'world1'} ]; } }}); const result = await model.getField('content, title', true); t.deepEqual(result, { title: 'hello1', content: 'world1' }); }); test('model get field no data 1', async t => { t.plan(1); const model = new Model('post', {handle: class { select(options) { return []; } }}); const result = await model.getField('content, title', true); t.deepEqual(result, { title: undefined, content: undefined }); }); test('model get field no data 2', async t => { t.plan(1); const model = new Model('post', {handle: class { select(options) { return []; } }}); const result = await model.getField('content', true); t.deepEqual(result, undefined); }); test('model get field no data 3', async t => { t.plan(1); const model = new Model('post', {handle: class { select(options) { return []; } }}); const result = await model.getField('content'); t.deepEqual(result, []); }); test('model get field no data 3', async t => { t.plan(1); const model = new Model('post', {handle: class { select(options) { return []; } }}); const result = await model.getField('content,title'); t.deepEqual(result, { title: [], content: [] }); }); test('model get field one key', async t => { t.plan(2); const model = new Model('post', {handle: class { select(options) { t.deepEqual(options, { pk: 'id', table: 'post', tablePrefix: '', field: 'title' }); return [ {title: 'hello1'}, {title: 'hello2'} ]; } }}); const result = await model.getField('title'); t.deepEqual(result, ['hello1', 'hello2']); }); test('model get field one data one key', async t => { t.plan(2); const model = new Model('post', {handle: class { select(options) { t.deepEqual(options, { pk: 'id', limit: 1, table: 'post', tablePrefix: '', field: 'title,content' }); return [ {title: 'hello2', content: 'world2'} ]; } }}); const result = await model.getField('title,content', true); t.deepEqual(result, {title: 'hello2', content: 'world2'}); }); test('model increment', async t => { t.plan(12); const model = new Model('post', {handle}); model.db(db); const arr = ['count', 'view']; const obj = {count: 3, view: 4}; const str = 'count'; const arrData = (step = 1) => ({ count: ['exp', 'count+' + step], view: ['exp', 'view+' + step] }); const objData = () => ({ count: ['exp', 'count+3'], view: ['exp', 'view+4'] }); const strData = (step = 1) => ({ count: ['exp', 'count+' + step] }); for (const step of [1, 10]) { let time = 0; model.update = function(data) { if (time === 0) { t.deepEqual(data, arrData(step)); return arrData(step); } else if (time === 1) { t.deepEqual(data, objData()); return objData(); } else { t.deepEqual(data, strData(step)); return strData(step); } }; t.deepEqual( await model.increment(arr, step), arrData(step) ); time = 1; t.deepEqual( await model.increment(obj, step), objData() ); time = 2; t.deepEqual( await model.increment(str, step), strData(step) ); } }); test('model decrement', async t => { t.plan(12); const model = new Model('post', {handle}); model.db(db); const arr = ['count', 'view']; const obj = {count: 3, view: 4}; const str = 'count'; const arrData = (step = 1) => ({ count: ['exp', 'count-' + step], view: ['exp', 'view-' + step] }); const objData = () => ({ count: ['exp', 'count-3'], view: ['exp', 'view-4'] }); const strData = (step = 1) => ({ count: ['exp', 'count-' + step] }); for (const step of [1, 10]) { let time = 0; model.update = function(data) { if (time === 0) { t.deepEqual(data, arrData(step)); return arrData(step); } else if (time === 1) { t.deepEqual(data, objData()); return objData(); } else { t.deepEqual(data, strData(step)); return strData(step); } }; t.deepEqual( await model.decrement(arr, step), arrData(step) ); time = 1; t.deepEqual( await model.decrement(obj, step), objData() ); time = 2; t.deepEqual( await model.decrement(str, step), strData(step) ); } }); for (const logic of ['count', 'sum', 'min', 'max', 'avg']) { test(`model ${logic} empty`, t => { const model = new Model('post', {handle}); model.db(db); model.getField = function(sql) { t.is(sql, `${logic.toUpperCase()}(*) AS think_${logic}`); }; model[logic](); }); test(`model ${logic} custom pk`, t => { const model = new Model('post', {handle}); model.db(db); model._pk = 'post_id'; model.getField = function(sql) { t.is(sql, `${logic.toUpperCase()}(*) AS think_${logic}`); }; model[logic](); }); test(`model ${logic} empty pk`, t => { const model = new Model('post', {handle}); model.db(db); Object.defineProperty(model, 'pk', {value: false}); model.getField = function(sql) { t.is(sql, `${logic.toUpperCase()}(*) AS think_${logic}`); }; model[logic](); }); test(`model ${logic} with field`, t => { const model = new Model('post', {handle}); model.db(db); Object.defineProperty(model, 'pk', {value: false}); model.getField = function(sql) { t.is(sql, `${logic.toUpperCase()}(title) AS think_${logic}`); }; model[logic]('title'); }); } test('model query method', async t => { t.plan(3); const options = {table: 'post', where: {id: ['>', 30]}}; const model = new Model('post', {handle: class { select(opt, c) { t.is(opt, options); return 'select return'; } }}); model.parseSql = function(opt) { t.is(opt, options); return opt; }; const result = await model.query(options); t.is(result, 'select return'); }); test('model excute method', async t => { t.plan(3); const options = {table: 'post', where: {id: ['>', 30]}}; const model = new Model('post', {handle: class { execute(opt, c) { t.is(opt, options); return 'select return'; } }}); model.parseSql = function(opt) { t.is(opt, options); return opt; }; const result = await model.execute(options); t.is(result, 'select return'); }); test('model parser sql', t => { const model = new Model('post', { handle, prefix: 'fk_' }); model.db(db); const result = model.parseSql('__TABLE__ __TABLE__ __TITLE__ __title__'); t.is(result.sql, ' fk_post fk_post fk_title __title__'); }); test('set db 2', t => { const model = new Model('post', { handle, prefix: 'fk_' }); const db1 = model.db(); const db2 = model.db(); t.is(db1, db2); }); test('set db 3', t => { const model1 = new Model('post', { handle, prefix: 'fk_' }); const model2 = new Model('post2', { handle, prefix: 'fk_' }); const db1 = model1.db(); model2.db(db1); const db2 = model2.db(); t.is(db1 === db2, false); }); test('set db 4', t => { const model1 = new Model('post', { handle, prefix: 'fk_' }); const model2 = new Model('post2', { handle, prefix: 'fk_' }); const db1 = model1.db(); model2.db(db1); const db2 = model2.db(); t.is(db1.query, db2.query); });