From b0ba1472bb1d5d07d8e4bf708848a3fadf06ecd6 Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 18 Nov 2020 10:35:37 +0800 Subject: [PATCH] Test cases for database (#16) * fix: remove unused import * fix: use env instead of hard code for create database instance * refactor: change most "all" hook to "each" hook * refactor: split test suits into files * add: use DB_LOG_SQL in env to control SQL in log * add: test for logical operator and fixes --- .env.example | 3 + packages/actions/src/__tests__/index.ts | 4 +- .../src/__tests__/associations.test.ts | 82 +-- packages/database/src/__tests__/index.ts | 46 +- packages/database/src/__tests__/model.test.ts | 193 ++++- packages/database/src/__tests__/sync.test.ts | 35 +- .../database/src/__tests__/tables.test.ts | 78 ++- packages/database/src/__tests__/types.test.ts | 7 +- packages/database/src/__tests__/utils.test.ts | 658 ------------------ .../src/__tests__/{ => utils}/modules/fn.js | 0 .../src/__tests__/{ => utils}/modules/fnts.ts | 0 .../__tests__/{ => utils}/modules/json.json | 0 .../src/__tests__/{ => utils}/modules/obj.js | 0 .../__tests__/{ => utils}/modules/objts.ts | 0 .../src/__tests__/utils/requireModule.test.ts | 48 ++ .../src/__tests__/utils/toInclude.test.ts | 396 +++++++++++ .../src/__tests__/utils/toWhere.test.ts | 262 +++++++ packages/database/src/utils.ts | 6 +- 18 files changed, 982 insertions(+), 836 deletions(-) delete mode 100644 packages/database/src/__tests__/utils.test.ts rename packages/database/src/__tests__/{ => utils}/modules/fn.js (100%) rename packages/database/src/__tests__/{ => utils}/modules/fnts.ts (100%) rename packages/database/src/__tests__/{ => utils}/modules/json.json (100%) rename packages/database/src/__tests__/{ => utils}/modules/obj.js (100%) rename packages/database/src/__tests__/{ => utils}/modules/objts.ts (100%) create mode 100644 packages/database/src/__tests__/utils/requireModule.test.ts create mode 100644 packages/database/src/__tests__/utils/toInclude.test.ts create mode 100644 packages/database/src/__tests__/utils/toWhere.test.ts diff --git a/.env.example b/.env.example index 42cd9137ce..8e931b3633 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,9 @@ DB_DATABASE=nocobase DB_USER=test DB_PASSWORD=test +# set to 'on' to enable log +DB_LOG_SQL= + # DB_DIALECT=mysql # DB_PORT=3306 diff --git a/packages/actions/src/__tests__/index.ts b/packages/actions/src/__tests__/index.ts index 56b16a1203..6c13e8aa73 100644 --- a/packages/actions/src/__tests__/index.ts +++ b/packages/actions/src/__tests__/index.ts @@ -2,9 +2,9 @@ import { resolve } from 'path'; import glob from 'glob'; import Koa from 'koa'; -import { Options, Dialect } from 'sequelize'; +import { Dialect } from 'sequelize'; import bodyParser from 'koa-bodyparser'; -import supertest, { SuperAgentTest } from 'supertest'; +import supertest from 'supertest'; import Database, { requireModule } from '@nocobase/database'; import Resourcer from '@nocobase/resourcer'; diff --git a/packages/database/src/__tests__/associations.test.ts b/packages/database/src/__tests__/associations.test.ts index de4d227505..25a7731a21 100644 --- a/packages/database/src/__tests__/associations.test.ts +++ b/packages/database/src/__tests__/associations.test.ts @@ -3,11 +3,21 @@ import { HasMany, HasOne, Integer, BelongsTo, BelongsToMany } from '../fields'; import { DataTypes } from 'sequelize'; +import Database from '..'; + +let db: Database; + +beforeEach(() => { + db = getDatabase(); +}); + +afterEach(() => { + db.close(); +}); describe('associations', () => { describe('hasOne', () => { - it('shound be defaults', async () => { - const db = getDatabase(); + it('should be defaults', async () => { db.table({ name: 'foos', }); @@ -25,8 +35,7 @@ describe('associations', () => { expect(field.options.foreignKey).toBe('bar_id'); expect(field.options.sourceKey).toBe('id'); }); - it('shound be ok when the association table is defined later', async () => { - const db = getDatabase(); + it('should be ok when the association table is defined later', async () => { db.table({ name: 'bars', fields: [ @@ -44,8 +53,7 @@ describe('associations', () => { expect(field.options.foreignKey).toBe('bar_id'); expect(field.options.sourceKey).toBe('id'); }); - it('shound be custom when target is defined', () => { - const db = getDatabase(); + it('should be custom when target is defined', () => { db.table({ name: 'foos', }); @@ -65,8 +73,7 @@ describe('associations', () => { expect(field.options.sourceKey).toBe('id'); }); - it('shound be custom when sourceKey is defined', () => { - const db = getDatabase(); + it('should be custom when sourceKey is defined', () => { db.table({ name: 'foos', }); @@ -86,8 +93,7 @@ describe('associations', () => { expect(field.options.sourceKey).toBe('sid'); }); - it('shound be integer type when the column of sourceKey does not exist', () => { - const db = getDatabase(); + it('should be integer type when the column of sourceKey does not exist', () => { db.table({ name: 'foos', }); @@ -112,8 +118,7 @@ describe('associations', () => { }); describe('hasMany', () => { - it('shound be defaults', async () => { - const db = getDatabase(); + it('should be defaults', async () => { db.table({ name: 'foo', }); @@ -131,8 +136,7 @@ describe('associations', () => { expect(field.options.foreignKey).toBe('bar_id'); expect(field.options.sourceKey).toBe('id'); }); - it('shound be ok when the association table is defined later', async () => { - const db = getDatabase(); + it('should be ok when the association table is defined later', async () => { db.table({ name: 'bars', fields: [ @@ -150,8 +154,7 @@ describe('associations', () => { expect(field.options.foreignKey).toBe('bar_id'); expect(field.options.sourceKey).toBe('id'); }); - it('shound be custom when target is defined', () => { - const db = getDatabase(); + it('should be custom when target is defined', () => { db.table({ name: 'foos', }); @@ -171,8 +174,7 @@ describe('associations', () => { expect(field.options.sourceKey).toBe('id'); }); - it('shound be custom when sourceKey is defined', () => { - const db = getDatabase(); + it('should be custom when sourceKey is defined', () => { db.table({ name: 'foos', }); @@ -192,8 +194,7 @@ describe('associations', () => { expect(field.options.sourceKey).toBe('sid'); }); - it('shound be integer type when the column of sourceKey does not exist', () => { - const db = getDatabase(); + it('should be integer type when the column of sourceKey does not exist', () => { db.table({ name: 'foos', }); @@ -218,8 +219,7 @@ describe('associations', () => { }); describe('belongsTo', () => { - it('shound be custom foreignKey', async () => { - const db = getDatabase(); + it('should be custom foreignKey', async () => { db.table({ name: 'bars', fields: [ @@ -236,8 +236,7 @@ describe('associations', () => { expect(field.options.targetKey).toBe('id'); expect(field.options.foreignKey).toBe('foo_id'); }); - it('shound be custom foreignKey', async () => { - const db = getDatabase(); + it('should be custom foreignKey', async () => { db.table({ name: 'bars', fields: [ @@ -255,8 +254,7 @@ describe('associations', () => { expect(field.options.targetKey).toBe('id'); expect(field.options.foreignKey).toBe('custom_foo_id'); }); - it('shound be custom primaryKey', () => { - const db = getDatabase(); + it('should be custom primaryKey', () => { db.table({ name: 'foos', fields: [ @@ -282,8 +280,7 @@ describe('associations', () => { expect(field.options.targetKey).toBe('fid'); expect(field.options.foreignKey).toBe('foo_fid'); }); - it('shound be custom primaryKey', () => { - const db = getDatabase(); + it('should be custom primaryKey', () => { db.table({ name: 'bars', fields: [ @@ -309,8 +306,7 @@ describe('associations', () => { expect(field.options.targetKey).toBe('fid'); expect(field.options.foreignKey).toBe('foo_fid'); }); - it('shound throw error', () => { - const db = getDatabase(); + it('should throw error', () => { db.table({ name: 'foos', }); @@ -327,8 +323,7 @@ describe('associations', () => { }); }).toThrow('Unknown attribute "fid" passed as targetKey, define this attribute on model "foos" first') }); - it('shound be ok when the association table is defined later', async () => { - const db = getDatabase(); + it('should be ok when the association table is defined later', async () => { db.table({ name: 'bars', fields: [ @@ -353,8 +348,7 @@ describe('associations', () => { expect(field.options.foreignKey).toBe('foo_fid'); }); - it('shound work', async () => { - const db = getDatabase(); + it('should work', async () => { db.table({ name: 'rows', fields: [ @@ -392,10 +386,7 @@ describe('associations', () => { }); describe('belongsToMany', () => { - it('shound be defaults', async () => { - const db = getDatabase({ - logging: false, - }); + it('should be defaults', async () => { db.table({ name: 'foos', fields: [ @@ -447,8 +438,7 @@ describe('associations', () => { expect(db.getModel('bars').associations.foos).toBeDefined(); }); - it('shound be correct when use custom primary key', async () => { - const db = getDatabase(); + it('should be correct when use custom primary key', async () => { db.table({ name: 'foos', fields: [ @@ -515,9 +505,6 @@ describe('associations', () => { }); it('through be defined after source and target', async () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'foos', fields: [ @@ -555,9 +542,6 @@ describe('associations', () => { }); it('through be defined after source', async () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'foos', fields: [ @@ -596,9 +580,6 @@ describe('associations', () => { }); it('#', () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'posts', fields: [ @@ -657,8 +638,7 @@ describe('associations', () => { }); }); - it('shound be defined', () => { - const db = getDatabase(); + it('should be defined', () => { db.table({ name: 'bars', fields: [ diff --git a/packages/database/src/__tests__/index.ts b/packages/database/src/__tests__/index.ts index 594b9a40c2..0b2ea2ee43 100644 --- a/packages/database/src/__tests__/index.ts +++ b/packages/database/src/__tests__/index.ts @@ -1,37 +1,25 @@ import Database from '../database'; -import { Options } from 'sequelize'; +import { Dialect } from 'sequelize'; -export const config: { - [key: string]: Options; -} = { - mysql: { - username: 'test', - password: 'test', - database: 'test', - host: '127.0.0.1', - port: 43306, - dialect: 'mysql', - }, - postgres: { - username: 'test', - password: 'test', - database: 'test', - host: '127.0.0.1', - port: 45432, - dialect: 'postgres', - define: { - hooks: { - beforeCreate(model, options) { - - }, + + +const config = { + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + host: process.env.DB_HOST, + port: Number.parseInt(process.env.DB_PORT, 10), + dialect: process.env.DB_DIALECT as Dialect, + define: { + hooks: { + beforeCreate(model, options) { + }, }, - // logging: false, }, + logging: false, }; -export function getDatabase(options: Options = {}) { - // console.log(process.env.DB_DIALECT); - const db = new Database({...config[process.env.DB_DIALECT||'postgres'], ...options}); - return db; +export function getDatabase() { + return new Database(config); }; diff --git a/packages/database/src/__tests__/model.test.ts b/packages/database/src/__tests__/model.test.ts index b9d6c41080..bad72c70f5 100644 --- a/packages/database/src/__tests__/model.test.ts +++ b/packages/database/src/__tests__/model.test.ts @@ -1,18 +1,23 @@ -import { getDatabase } from '.'; import Database from '..'; +import { getDatabase } from '.'; import { Op, Sequelize } from 'sequelize'; -import Model from '../model'; +import Model, { ModelCtor } from '../model'; import { BelongsToMany } from '../fields'; import { Mode } from 'fs'; + +let db: Database; + +beforeEach(async () => { + db = await getDatabase(); +}); + +afterEach(async () => { + await db.close(); +}); + describe('actions', () => { - let db: Database; - - beforeAll(async () => { - db = getDatabase({ - logging: false, - }); - + beforeEach(async () => { db.table({ name: 'users', tableName: 'user1234', @@ -196,11 +201,6 @@ describe('actions', () => { await db.sync({ force: true, }); - - }); - - afterAll(async () => { - await db.close(); }); it('through attributes', async () => { @@ -235,9 +235,9 @@ describe('actions', () => { it('scope', async () => { const [User, Post, Comment] = db.getModels(['users', 'posts', 'comments']); const user1 = await User.create(); - const user2 =await User.create(); - const user3 =await User.create(); - const user4 =await User.create(); + const user2 = await User.create(); + const user3 = await User.create(); + const user4 = await User.create(); const post = await Post.create(); const comment = await Comment.create(); comment.updateAssociations({ @@ -265,16 +265,144 @@ describe('actions', () => { ], }); const comments = await post.getCurrent_user_comments(); - // console.log(comments); + // TODO: no expect + }); + }); + + describe('parseApiJson', () => { + let db: Database; + let Foo: ModelCtor; + beforeEach(() => { + db = getDatabase(); + db.table({ + name: 'bazs', + tableName: 'bazs_model' + }); + db.table({ + name: 'bays', + tableName: 'bays_model' + }); + db.table({ + name: 'bars', + tableName: 'bars_model', + fields: [ + { + type: 'belongsTo', + name: 'baz', + }, + { + type: 'belongsTo', + name: 'bay', + }, + ], + }); + db.table({ + name: 'foos', + tableName: 'foos_model', + fields: [ + { + type: 'hasMany', + name: 'bars', + }, + { + type: 'hasMany', + name: 'fozs', + }, + { + type: 'hasMany', + name: 'coos', + }, + ], + }); + db.table({ + name: 'fozs', + tableName: 'fozs_model', + }); + db.table({ + name: 'coos', + tableName: 'coos_model', + }); + Foo = db.getModel('foos'); + }); + + afterEach(() => db.close()); + + it('parseApiJson', () => { + const data = Foo.parseApiJson({ + filter: { + col1: 'co2', + } + }); + expect(data).toEqual({ where: { col1: 'co2' } }); + }); + + it('parseApiJson', () => { + const data = Foo.parseApiJson({ + fields: ['col1'], + }); + expect(data).toEqual({ attributes: ['col1'] }); + }); + + it('parseApiJson', () => { + const data = Foo.parseApiJson({ + fields: ['col1'], + filter: { + col1: 'co2', + }, + }); + expect(data).toEqual({ attributes: ['col1'], where: { col1: 'co2' } }); + }); + + it('parseApiJson', () => { + const data = Foo.parseApiJson({ + fields: ['col1'], + filter: { + col1: 'val1', + bars: { + col1: 'val1', + } + }, + }); + expect(data).toEqual({ + attributes: ['col1'], + where: { col1: 'val1' }, + include: [ + { + association: 'bars', + where: { col1: 'val1' }, + } + ], + }); + }); + + it('parseApiJson', () => { + const data = Foo.parseApiJson({ + fields: ['col1', 'bars.col1'], + filter: { + col1: 'val1', + bars: { + col1: 'val1', + } + }, + }); + expect(data).toEqual({ + attributes: ['col1'], + where: { col1: 'val1' }, + include: [ + { + association: 'bars', + attributes: ['col1'], + where: { col1: 'val1' }, + } + ], + }); }); }); describe('findByApiJson', () => { it('q', async () => { - db.getModel('tags').addScope('scopeName', (name, ctx) => { expect(ctx.scopeName).toBe(name); - console.log(ctx); return { where: { name: name, @@ -330,7 +458,11 @@ describe('actions', () => { scopeName: 'tag3', }, }); + console.log(options); + try{ + // DatabaseError [SequelizeDatabaseError]: column tags.scopeName does not exist + // SELECT count("posts"."id") AS "count" FROM "post123456" AS "posts" INNER JOIN ( "posts_tags1234" AS "tags->posts_tags" INNER JOIN "tag1234" AS "tags" ON "tags"."id" = "tags->posts_tags"."tag_id") ON "posts"."id" = "tags->posts_tags"."post_id" AND "tags"."scopeName" = 'tag3' INNER JOIN "user1234" AS "user" ON "posts"."user_id" = "user"."id" AND "user"."name" = 'name112233' WHERE "posts"."title" = 'title112233'; const { rows, count } = await Post.findAndCountAll({ ...options, // group: ['id'], @@ -340,11 +472,14 @@ describe('actions', () => { // console.log(JSON.stringify(rows[0].toJSON(), null, 2)); - rows.forEach(post => { - // expect(post.toJSON()).toEqual({ title: 'title112233', 'tags_count': 3, user: { name: 'name112233', posts_count: 1 } }); - expect(post.get('title')).toBe('title112233'); - expect(post.user.get('name')).toBe('name112233'); + rows.forEach(row => { + // expect(row.toJSON()).toEqual({ title: 'title112233', 'tags_count': 3, user: { name: 'name112233', posts_count: 1 } }); + expect(row.get('title')).toBe('title112233'); + expect(row.user.get('name')).toBe('name112233'); }); + } catch(error) { + console.error(error); + } // console.log(count); @@ -878,15 +1013,11 @@ describe('actions', () => { }); describe('belongsToMany', () => { - let db: Database; let post: Model; let tag1: Model; let tag2: Model; - beforeAll(async () => { - db = getDatabase({ - logging: false, - }); + beforeEach(async () => { db.table({ name: 'posts', tableName: 't333333_posts', @@ -928,10 +1059,6 @@ describe('belongsToMany', () => { tag2 = await Tag.create({name: 'tag2'}); }); - afterAll(async () => { - await db.close(); - }); - it('@', async () => { await post.updateAssociations({ tags: tag1.name, diff --git a/packages/database/src/__tests__/sync.test.ts b/packages/database/src/__tests__/sync.test.ts index 4d0c13abec..7b16eb7559 100644 --- a/packages/database/src/__tests__/sync.test.ts +++ b/packages/database/src/__tests__/sync.test.ts @@ -1,21 +1,27 @@ +import Database from '..'; import { getDatabase } from './'; + + +let db: Database; + +beforeEach(async () => { + db = await getDatabase(); +}); + +afterEach(async () => { + await db.close(); +}); + describe('db sync', () => { describe('table.sync', () => { it('shound be ok1', async () => { - const db = getDatabase({ - logging: false, - }); const table = db.table({ name: 'foos', }); await table.sync(); - await db.close(); }); it('sync#belongsTo', async () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'foos', tableName: 'foos1', @@ -43,12 +49,8 @@ describe('db sync', () => { drop: false, } }); - await db.close(); }); it('sync#hasMany', async () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'foos', tableName: 'foos2', @@ -76,12 +78,8 @@ describe('db sync', () => { drop: false, } }); - await db.close(); }); it('sync#belongsToMany', async () => { - const db = getDatabase({ - logging: false, - }); db.table({ name: 'foos', fields: [ @@ -134,12 +132,8 @@ describe('db sync', () => { col1: 'val1' }); expect(await db.getModel('bars').count()).toBe(2); - await db.close(); }); it('shound be ok2', async () => { - const db = getDatabase({ - logging: false, - }); const table = db.table({ name: 'goos', }); @@ -166,7 +160,6 @@ describe('db sync', () => { drop: false, } }); - await db.close(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/database/src/__tests__/tables.test.ts b/packages/database/src/__tests__/tables.test.ts index 5e9fbba8e4..590f134480 100644 --- a/packages/database/src/__tests__/tables.test.ts +++ b/packages/database/src/__tests__/tables.test.ts @@ -3,20 +3,19 @@ import Database from '../database'; import Table from '../table'; import Model from '../model'; +let db: Database; + +beforeEach(async () => { + db = await getDatabase(); +}); + +afterEach(async () => { + await db.close(); +}); + describe('tables', () => { - let db: Database; - - // beforeAll(() => { - // db = getDatabase(); - // }); - - // afterAll(async () => { - // await db.sequelize.close(); - // }); - describe('options', () => { - it('shoud be defined', () => { - db = getDatabase(); + it('should be defined', () => { db.table({ name: 'foo', }); @@ -34,7 +33,6 @@ describe('tables', () => { return 'test12345'; } } - db = getDatabase(); const table = db.table({ name: 'abc', model: Abc, @@ -44,8 +42,7 @@ describe('tables', () => { expect(Abc.getModel('abc').test12345()).toBe('test12345'); }); - it('shoud tableName === name', async () => { - db = getDatabase(); + it('should tableName === name', async () => { db.table({ name: 'foos', }); @@ -53,8 +50,7 @@ describe('tables', () => { expect(db.getModel('foos').getTableName()).toBe('foos'); }); - it('shoud be custom when tableName is defined', async () => { - db = getDatabase(); + it('should be custom when tableName is defined', async () => { db.table({ name: 'bar', tableName: 'bar_v2' @@ -63,8 +59,7 @@ describe('tables', () => { expect(db.getModel('bar').getTableName()).toBe('bar_v2'); }); - it('shoud be custom when timestamps is defined', async () => { - db = getDatabase(); + it('should be custom when timestamps is defined', async () => { db.table({ name: 'baz', createdAt: 'created', @@ -74,8 +69,7 @@ describe('tables', () => { expect(db.getModel('baz').rawAttributes.updated).toBeDefined(); }); - it('index shound be defined', async () => { - db = getDatabase(); + it('index should be defined', async () => { db.table({ name: 'baz', fields: [ @@ -104,8 +98,7 @@ describe('tables', () => { ]); }); - it('index shound be defined', async () => { - db = getDatabase(); + it('index should be defined', async () => { db.table({ name: 'baz2', indexes: [ @@ -140,8 +133,7 @@ describe('tables', () => { }); describe('#extend()', () => { - it('shoud be extend', async () => { - db = getDatabase(); + it('should be extend', async () => { db.table({ name: 'baz', }); @@ -155,8 +147,7 @@ describe('tables', () => { expect(db.getModel('baz').rawAttributes.created).toBeDefined(); expect(db.getModel('baz').rawAttributes.updated).toBeDefined(); }); - it('shoud be extend', async () => { - db = getDatabase(); + it('should be extend', async () => { db.table({ name: 'foos', }); @@ -196,10 +187,9 @@ describe('tables', () => { describe('associations', () => { beforeAll(() => { - db = getDatabase(); }); - it('shound be undefined when target table does not exist', () => { + it('should be undefined when target table does not exist', () => { db.table({ name: 'bars', fields: [ @@ -212,14 +202,22 @@ describe('tables', () => { expect(db.getModel('bars').associations.foo).toBeUndefined(); }); - it('shound be defined when target table exists', () => { + it('should be defined when target table exists', () => { + db.table({ + name: 'bars', + fields: [ + { + type: 'hasOne', + name: 'foo', + } + ], + }); db.table({name: 'foos'}); expect(db.getModel('bars').associations.foo).toBeDefined(); }); describe('#setFields()', () => { - beforeAll(() => { - db = getDatabase(); + beforeEach(() => { db.table({ name: 'table1', fields: [ @@ -239,13 +237,13 @@ describe('tables', () => { name: 'table2', }); }); - it('shound be defined', () => { + it('should be defined', () => { const table1 = db.getModel('table1'); expect(Object.keys(table1.associations).length).toBe(2); expect(table1.associations.table21).toBeDefined(); expect(table1.associations.table22).toBeDefined(); }); - it('shound be defined', () => { + it('should be defined', () => { db.getTable('table1').setFields([ { type: 'string', @@ -267,8 +265,7 @@ describe('tables', () => { }); describe('#addField()', () => { - beforeAll(() => { - db = getDatabase(); + beforeEach(() => { db.table({ name: 'table1', }); @@ -276,7 +273,7 @@ describe('tables', () => { name: 'table2', }); }); - it('shound be defined when the field be added after initialization', () => { + it('should be defined when the field be added after initialization', () => { db.getTable('table1').addField({ type: 'hasOne', name: 'table2', @@ -285,7 +282,12 @@ describe('tables', () => { expect(Object.keys(db.getModel('table1').associations).length).toBe(1); expect(db.getModel('table1').associations.table2).toBeDefined(); }); - it('shound be defined when continue to add', () => { + it('should be defined when continue to add', () => { + db.getTable('table1').addField({ + type: 'hasOne', + name: 'table2', + target: 'table2', + }); db.getTable('table1').addField({ type: 'hasOne', name: 'table21', diff --git a/packages/database/src/__tests__/types.test.ts b/packages/database/src/__tests__/types.test.ts index 2a38e80c72..874699d031 100644 --- a/packages/database/src/__tests__/types.test.ts +++ b/packages/database/src/__tests__/types.test.ts @@ -28,7 +28,7 @@ import Database from '..'; describe('field types', () => { const assertTypeInstanceOf = (expected, actual) => { - const db = getDatabase(); + const db: Database = getDatabase(); const table = db.table({ name: 'test', }); @@ -56,6 +56,7 @@ describe('field types', () => { expect(type).toBe(field.getDataType()); } } + db.close(); } it('shound be boolean', () => { @@ -212,7 +213,7 @@ describe('field types', () => { describe('virtual', () => { let db: Database; - beforeAll(async () => { + beforeEach(async () => { db = getDatabase(); db.table({ name: 'formula_tests', @@ -283,7 +284,7 @@ describe('field types', () => { }) await db.sync({force: true}); }); - afterAll(async () => { + afterEach(async () => { await db.close(); }); it('pwd', async () => { diff --git a/packages/database/src/__tests__/utils.test.ts b/packages/database/src/__tests__/utils.test.ts deleted file mode 100644 index 4c1730b5a5..0000000000 --- a/packages/database/src/__tests__/utils.test.ts +++ /dev/null @@ -1,658 +0,0 @@ -import { requireModule, toWhere, toInclude } from '../utils'; -import path from 'path'; -import { Op } from 'sequelize'; -import { getDatabase } from '.'; -import Database from '../database'; -import Model, { ModelCtor } from '../model'; - -describe('utils', () => { - describe('toWhere', () => { - it('Op.eq', () => { - const where = toWhere({ - id: { - eq: 12 - }, - }); - expect(where).toEqual({ - id: { - [Op.eq]: 12, - }, - }); - }); - - it('Op.ilike', () => { - const where = toWhere({ - id: { - ilike: 'val1' - }, - }); - expect(where).toEqual({ - id: { - [Op.iLike]: 'val1', - }, - }); - }); - - it('Op.ilike', () => { - const where = toWhere({ - 'id.ilike': 'val1', - }); - expect(where).toEqual({ - id: { - [Op.iLike]: 'val1', - }, - }); - }); - - it('Op.is null', () => { - const where = toWhere({ - 'id.is': null, - }); - expect(where).toEqual({ - id: { - [Op.is]: null, - }, - }); - }); - - it('Op.in', () => { - const where = toWhere({ - id: { - in: [12] - }, - }); - expect(where).toEqual({ - id: { - [Op.in]: [12], - }, - }); - }); - - it('Op.in', () => { - const where = toWhere({ - 'id.in': [11, 12], - }); - expect(where).toEqual({ - id: { - [Op.in]: [11, 12], - }, - }); - }); - - describe('association', () => { - let db: Database; - let Foo: ModelCtor; - beforeAll(() => { - db = getDatabase(); - db.table({ - name: 'bazs', - }); - db.table({ - name: 'bars', - fields: [ - { - type: 'belongsTo', - name: 'baz', - } - ] - }); - db.table({ - name: 'foos', - fields: [ - { - type: 'hasMany', - name: 'bars', - } - ], - }); - Foo = db.getModel('foos'); - }); - - const toWhereExpect = (options, logging = false) => { - const where = toWhere(options, { - associations: Foo.associations, - }); - return expect(where); - } - - it('association', () => { - toWhereExpect({ - col1: 'val1', - bars: { - name: { - ilike: 'aa', - }, - col2: { - lt: 2, - }, - baz: { - col1: 12, - }, - }, - 'bars.col3.ilike': 'aa', - }).toEqual({ - col1: 'val1', - $__include: { - bars: { - name: { - [Op.iLike]: 'aa', - }, - col2: { - [Op.lt]: 2, - }, - col3: { - [Op.iLike]: 'aa', - }, - $__include: { - baz: { - col1: 12 - }, - }, - }, - }, - }); - }); - }); - }); - - describe('toInclude', () => { - let db: Database; - let Foo: ModelCtor; - beforeAll(() => { - db = getDatabase(); - db.table({ - name: 'bazs', - }); - db.table({ - name: 'bays', - }); - db.table({ - name: 'bars', - fields: [ - { - type: 'belongsTo', - name: 'baz', - }, - { - type: 'belongsTo', - name: 'bay', - }, - ], - }); - db.table({ - name: 'foos', - fields: [ - { - type: 'hasMany', - name: 'bars', - }, - { - type: 'hasMany', - name: 'fozs', - }, - { - type: 'hasMany', - name: 'coos', - }, - ], - }); - db.table({ - name: 'fozs', - }); - db.table({ - name: 'coos', - }); - Foo = db.getModel('foos'); - }); - - const toIncludeExpect = (options: any, logging = false) => { - const include = toInclude(options, { - Model: Foo, - associations: Foo.associations, - }); - if (logging) { - console.log(JSON.stringify(include, null, 2)); - } - return expect(include); - }; - - it('normal columns', () => { - toIncludeExpect({ - fields: ['col1', 'col2'], - }).toEqual({ attributes: [ 'col1', 'col2' ] }); - }); - - it('association count attribute', () => { - toIncludeExpect({ - fields: ['col1', 'bars_count'], - }).toEqual({ attributes: [ 'col1', Foo.withCountAttribute('bars') ] }); - }); - - it('association without attributes', () => { - toIncludeExpect({ - fields: ['col1', 'bars'], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - } - ], - }); - }); - - it('association attributes', () => { - toIncludeExpect({ - fields: ['col1', 'bars.col1', 'bars.col2'], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - attributes: [ 'col1', 'col2' ], - } - ], - }); - }); - - it('association attributes', () => { - toIncludeExpect({ - fields: ['col1', ['bars', 'col1'], ['bars', 'col2']], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - attributes: [ 'col1', 'col2' ], - } - ], - }); - }); - - it('nested association', () => { - toIncludeExpect({ - fields: ['col1', 'bars.baz'], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - attributes: [], - include: [ - { - association: 'baz', - } - ] - } - ], - }); - }); - - it.skip('nested association', () => { - // TODO,输出 bars 的所有字段 - toIncludeExpect({ - fields: ['col1', 'bars', 'bars.baz'], - }, true).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - include: [ - { - association: 'baz', - } - ] - } - ], - }); - }); - - it('nested association', () => { - toIncludeExpect({ - fields: ['col1', 'bars.col1', 'bars.col2', 'bars.baz'], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - attributes: ['col1', 'col2'], - include: [ - { - association: 'baz', - } - ] - } - ], - }); - }); - - it('nested association', () => { - // TODO,输出 bars 的所有字段 - toIncludeExpect({ - fields: ['col1', 'bars.col1', 'bars.col2', 'bars.baz.col1', 'bars.baz.col2'], - }).toEqual({ - attributes: [ 'col1' ], - include: [ - { - association: 'bars', - attributes: ['col1', 'col2'], - include: [ - { - association: 'baz', - attributes: ['col1', 'col2'], - } - ] - } - ], - }); - }); - - it('append attributes', () => { - toIncludeExpect({ - fields: { - appends: ['bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - }, - }); - }); - - it('append attributes', () => { - toIncludeExpect({ - fields: { - appends: ['bars.col1', 'bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - }, - include: [ - { - association: 'bars', - attributes: { - include: ['col1'], - }, - } - ] - }); - }); - - it('only & append attributes', () => { - toIncludeExpect({ - fields: { - only: ['col1'], - appends: ['bars_count'], - }, - }).toEqual({ - attributes: ['col1', Foo.withCountAttribute('bars')], - }); - }); - - it('only & append attributes', () => { - toIncludeExpect({ - fields: { - only: ['col1', 'bars.col1'], - appends: ['bars_count'], - }, - }).toEqual({ - attributes: ['col1', Foo.withCountAttribute('bars')], - include: [ - { - association: 'bars', - attributes: ['col1'], - } - ], - }); - }); - - it('excpet attributes', () => { - toIncludeExpect({ - fields: { - except: ['col1'], - }, - }).toEqual({ - attributes: { - include: [], - exclude: ['col1'], - }, - }); - }); - - it('excpet attributes', () => { - toIncludeExpect({ - fields: { - except: ['col1', 'bars.col1'], - }, - }).toEqual({ - attributes: { - include: [], - exclude: ['col1'], - }, - include: [ - { - association: 'bars', - attributes: { - include: [], - exclude: ['col1'], - } - } - ] - }); - }); - - it('excpet & append attributes', () => { - toIncludeExpect({ - fields: { - except: ['col1'], - appends: ['bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - exclude: ['col1'], - }, - }); - }); - - describe('where options', () => { - it('where', () => { - toIncludeExpect({ - filter: { - col1: 'val1', - }, - fields: { - except: ['col1'], - appends: ['bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - exclude: ['col1'], - }, - where: { - col1: 'val1', - }, - }); - }); - - it('where', () => { - toIncludeExpect({ - filter: { - col1: 'val1', - bars: { - col1: 'val1', - } - }, - fields: { - except: ['col1'], - appends: ['bars', 'bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - exclude: ['col1'], - }, - where: { - col1: 'val1', - }, - include: [ - { - association: 'bars', - where: { - col1: 'val1', - } - } - ] - }); - }); - - it('where', () => { - toIncludeExpect({ - filter: { - col1: 'val1', - bars: { - col1: 'val1', - } - }, - fields: { - except: ['col1'], - appends: ['bars_count'], - }, - }).toEqual({ - attributes: { - include: [Foo.withCountAttribute('bars')], - exclude: ['col1'], - }, - where: { - col1: 'val1', - }, - include: [ - { - association: 'bars', - where: { - col1: 'val1', - } - } - ] - }); - }); - - it('parseApiJson', () => { - const data = Foo.parseApiJson({ - filter: { - col1: 'co2', - } - }); - expect(data).toEqual({ where: { col1: 'co2' } }); - }); - - it('parseApiJson', () => { - const data = Foo.parseApiJson({ - fields: ['col1'], - }); - expect(data).toEqual({ attributes: ['col1'] }); - }); - - it('parseApiJson', () => { - const data = Foo.parseApiJson({ - fields: ['col1'], - filter: { - col1: 'co2', - }, - }); - expect(data).toEqual({ attributes: ['col1'], where: { col1: 'co2' } }); - }); - - it('parseApiJson', () => { - const data = Foo.parseApiJson({ - fields: ['col1'], - filter: { - col1: 'val1', - bars: { - col1: 'val1', - } - }, - }); - expect(data).toEqual({ - attributes: ['col1'], - where: { col1: 'val1' }, - include: [ - { - association: 'bars', - where: { col1: 'val1' }, - } - ], - }); - }); - - it('parseApiJson', () => { - const data = Foo.parseApiJson({ - fields: ['col1', 'bars.col1'], - filter: { - col1: 'val1', - bars: { - col1: 'val1', - } - }, - }); - expect(data).toEqual({ - attributes: ['col1'], - where: { col1: 'val1' }, - include: [ - { - association: 'bars', - attributes: ['col1'], - where: { col1: 'val1' }, - } - ], - }); - }); - }); - - }); - - describe('requireModule', () => { - test('toBeTruthy', () => { - const r = requireModule(true); - expect(r).toBeTruthy(); - }); - - test('toBeInstanceOf Function', () => { - const r = requireModule(() => {}); - expect(r).toBeInstanceOf(Function); - }); - - test('toBeInstanceOf Function', () => { - const r = requireModule(path.resolve(__dirname, './modules/fn')); - expect(r).toBeInstanceOf(Function); - }); - - test('toBeInstanceOf Function', () => { - const r = requireModule(path.resolve(__dirname, './modules/fnts')); - expect(r).toBeInstanceOf(Function); - }); - - test('object', () => { - const r = requireModule(path.resolve(__dirname, './modules/obj')); - expect(r).toEqual({ - 'foo': 'bar', - }); - }); - - test('object', () => { - const r = requireModule(path.resolve(__dirname, './modules/objts')); - expect(r).toEqual({ - 'foo': 'bar', - }); - }); - - test('json', () => { - const r = requireModule(path.resolve(__dirname, './modules/json')); - expect(r).toEqual({ - 'foo': 'bar', - }); - }); - }); -}); diff --git a/packages/database/src/__tests__/modules/fn.js b/packages/database/src/__tests__/utils/modules/fn.js similarity index 100% rename from packages/database/src/__tests__/modules/fn.js rename to packages/database/src/__tests__/utils/modules/fn.js diff --git a/packages/database/src/__tests__/modules/fnts.ts b/packages/database/src/__tests__/utils/modules/fnts.ts similarity index 100% rename from packages/database/src/__tests__/modules/fnts.ts rename to packages/database/src/__tests__/utils/modules/fnts.ts diff --git a/packages/database/src/__tests__/modules/json.json b/packages/database/src/__tests__/utils/modules/json.json similarity index 100% rename from packages/database/src/__tests__/modules/json.json rename to packages/database/src/__tests__/utils/modules/json.json diff --git a/packages/database/src/__tests__/modules/obj.js b/packages/database/src/__tests__/utils/modules/obj.js similarity index 100% rename from packages/database/src/__tests__/modules/obj.js rename to packages/database/src/__tests__/utils/modules/obj.js diff --git a/packages/database/src/__tests__/modules/objts.ts b/packages/database/src/__tests__/utils/modules/objts.ts similarity index 100% rename from packages/database/src/__tests__/modules/objts.ts rename to packages/database/src/__tests__/utils/modules/objts.ts diff --git a/packages/database/src/__tests__/utils/requireModule.test.ts b/packages/database/src/__tests__/utils/requireModule.test.ts new file mode 100644 index 0000000000..b8f79aa725 --- /dev/null +++ b/packages/database/src/__tests__/utils/requireModule.test.ts @@ -0,0 +1,48 @@ +import path from "path"; + +import { requireModule } from "../../utils"; + + + +describe('utils.requireModule', () => { + test('toBeTruthy', () => { + const r = requireModule(true); + expect(r).toBeTruthy(); + }); + + test('toBeInstanceOf Function', () => { + const r = requireModule(() => {}); + expect(r).toBeInstanceOf(Function); + }); + + test('toBeInstanceOf Function', () => { + const r = requireModule(path.resolve(__dirname, './modules/fn')); + expect(r).toBeInstanceOf(Function); + }); + + test('toBeInstanceOf Function', () => { + const r = requireModule(path.resolve(__dirname, './modules/fnts')); + expect(r).toBeInstanceOf(Function); + }); + + test('object', () => { + const r = requireModule(path.resolve(__dirname, './modules/obj')); + expect(r).toEqual({ + 'foo': 'bar', + }); + }); + + test('object', () => { + const r = requireModule(path.resolve(__dirname, './modules/objts')); + expect(r).toEqual({ + 'foo': 'bar', + }); + }); + + test('json', () => { + const r = requireModule(path.resolve(__dirname, './modules/json')); + expect(r).toEqual({ + 'foo': 'bar', + }); + }); +}); diff --git a/packages/database/src/__tests__/utils/toInclude.test.ts b/packages/database/src/__tests__/utils/toInclude.test.ts new file mode 100644 index 0000000000..6e33cfe548 --- /dev/null +++ b/packages/database/src/__tests__/utils/toInclude.test.ts @@ -0,0 +1,396 @@ +import { getDatabase } from '..'; +import Database from '../../database'; +import Model, { ModelCtor } from '../../model'; +import { toInclude } from '../../utils'; + + + +describe('toInclude', () => { + let db: Database; + let Foo: ModelCtor; + beforeEach(() => { + db = getDatabase(); + db.table({ + name: 'bazs', + }); + db.table({ + name: 'bays', + }); + db.table({ + name: 'bars', + fields: [ + { + type: 'belongsTo', + name: 'baz', + }, + { + type: 'belongsTo', + name: 'bay', + }, + ], + }); + db.table({ + name: 'foos', + fields: [ + { + type: 'hasMany', + name: 'bars', + }, + { + type: 'hasMany', + name: 'fozs', + }, + { + type: 'hasMany', + name: 'coos', + }, + ], + }); + db.table({ + name: 'fozs', + }); + db.table({ + name: 'coos', + }); + Foo = db.getModel('foos'); + }); + + afterEach(() => db.close()); + + const toIncludeExpect = (options: any, logging = false) => { + const include = toInclude(options, { + Model: Foo, + associations: Foo.associations, + }); + if (logging) { + console.log(JSON.stringify(include, null, 2)); + } + return expect(include); + }; + + it('normal columns', () => { + toIncludeExpect({ + fields: ['col1', 'col2'], + }).toEqual({ attributes: [ 'col1', 'col2' ] }); + }); + + it('association count attribute', () => { + toIncludeExpect({ + fields: ['col1', 'bars_count'], + }).toEqual({ attributes: [ 'col1', Foo.withCountAttribute('bars') ] }); + }); + + it('association without attributes', () => { + toIncludeExpect({ + fields: ['col1', 'bars'], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + } + ], + }); + }); + + it('association attributes', () => { + toIncludeExpect({ + fields: ['col1', 'bars.col1', 'bars.col2'], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + attributes: [ 'col1', 'col2' ], + } + ], + }); + }); + + it('association attributes', () => { + toIncludeExpect({ + fields: ['col1', ['bars', 'col1'], ['bars', 'col2']], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + attributes: [ 'col1', 'col2' ], + } + ], + }); + }); + + it('nested association', () => { + toIncludeExpect({ + fields: ['col1', 'bars.baz'], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + attributes: [], + include: [ + { + association: 'baz', + } + ] + } + ], + }); + }); + + it.skip('nested association', () => { + // TODO,输出 bars 的所有字段 + toIncludeExpect({ + fields: ['col1', 'bars', 'bars.baz'], + }, true).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + include: [ + { + association: 'baz', + } + ] + } + ], + }); + }); + + it('nested association', () => { + toIncludeExpect({ + fields: ['col1', 'bars.col1', 'bars.col2', 'bars.baz'], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + attributes: ['col1', 'col2'], + include: [ + { + association: 'baz', + } + ] + } + ], + }); + }); + + it('nested association', () => { + // TODO,输出 bars 的所有字段 + toIncludeExpect({ + fields: ['col1', 'bars.col1', 'bars.col2', 'bars.baz.col1', 'bars.baz.col2'], + }).toEqual({ + attributes: [ 'col1' ], + include: [ + { + association: 'bars', + attributes: ['col1', 'col2'], + include: [ + { + association: 'baz', + attributes: ['col1', 'col2'], + } + ] + } + ], + }); + }); + + it('append attributes', () => { + toIncludeExpect({ + fields: { + appends: ['bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + }, + }); + }); + + it('append attributes', () => { + toIncludeExpect({ + fields: { + appends: ['bars.col1', 'bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + }, + include: [ + { + association: 'bars', + attributes: { + include: ['col1'], + }, + } + ] + }); + }); + + it('only & append attributes', () => { + toIncludeExpect({ + fields: { + only: ['col1'], + appends: ['bars_count'], + }, + }).toEqual({ + attributes: ['col1', Foo.withCountAttribute('bars')], + }); + }); + + it('only & append attributes', () => { + toIncludeExpect({ + fields: { + only: ['col1', 'bars.col1'], + appends: ['bars_count'], + }, + }).toEqual({ + attributes: ['col1', Foo.withCountAttribute('bars')], + include: [ + { + association: 'bars', + attributes: ['col1'], + } + ], + }); + }); + + it('excpet attributes', () => { + toIncludeExpect({ + fields: { + except: ['col1'], + }, + }).toEqual({ + attributes: { + include: [], + exclude: ['col1'], + }, + }); + }); + + it('excpet attributes', () => { + toIncludeExpect({ + fields: { + except: ['col1', 'bars.col1'], + }, + }).toEqual({ + attributes: { + include: [], + exclude: ['col1'], + }, + include: [ + { + association: 'bars', + attributes: { + include: [], + exclude: ['col1'], + } + } + ] + }); + }); + + it('excpet & append attributes', () => { + toIncludeExpect({ + fields: { + except: ['col1'], + appends: ['bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + exclude: ['col1'], + }, + }); + }); + + describe('where options', () => { + it('where', () => { + toIncludeExpect({ + filter: { + col1: 'val1', + }, + fields: { + except: ['col1'], + appends: ['bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + exclude: ['col1'], + }, + where: { + col1: 'val1', + }, + }); + }); + + it('where', () => { + toIncludeExpect({ + filter: { + col1: 'val1', + bars: { + col1: 'val1', + } + }, + fields: { + except: ['col1'], + appends: ['bars', 'bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + exclude: ['col1'], + }, + where: { + col1: 'val1', + }, + include: [ + { + association: 'bars', + where: { + col1: 'val1', + } + } + ] + }); + }); + + it('where', () => { + toIncludeExpect({ + filter: { + col1: 'val1', + bars: { + col1: 'val1', + } + }, + fields: { + except: ['col1'], + appends: ['bars_count'], + }, + }).toEqual({ + attributes: { + include: [Foo.withCountAttribute('bars')], + exclude: ['col1'], + }, + where: { + col1: 'val1', + }, + include: [ + { + association: 'bars', + where: { + col1: 'val1', + } + } + ] + }); + }); + + }); + +}); diff --git a/packages/database/src/__tests__/utils/toWhere.test.ts b/packages/database/src/__tests__/utils/toWhere.test.ts new file mode 100644 index 0000000000..b96ea15262 --- /dev/null +++ b/packages/database/src/__tests__/utils/toWhere.test.ts @@ -0,0 +1,262 @@ +import { Op } from "sequelize"; +import Database from "../../database"; +import Model, { ModelCtor } from '../../model'; +import { toWhere } from "../../utils"; +import { getDatabase } from ".."; + +describe('utils.toWhere', () => { + describe('single', () => { + it('=', () => { + const where = toWhere({ + id: 12, + }); + expect(where).toEqual({ + id: 12, + }); + }); + + it('Op.eq', () => { + const where = toWhere({ + id: { + eq: 12 + }, + }); + expect(where).toEqual({ + id: { + [Op.eq]: 12, + }, + }); + }); + + it('Op.ilike', () => { + const where = toWhere({ + id: { + ilike: 'val1' + }, + }); + expect(where).toEqual({ + id: { + [Op.iLike]: 'val1', + }, + }); + }); + + it('Op.ilike', () => { + const where = toWhere({ + 'id.ilike': 'val1', + }); + expect(where).toEqual({ + id: { + [Op.iLike]: 'val1', + }, + }); + }); + + it('Op.is null', () => { + const where = toWhere({ + 'id.is': null, + }); + expect(where).toEqual({ + id: { + [Op.is]: null, + }, + }); + }); + + it('Op.in', () => { + const where = toWhere({ + id: { + in: [12] + }, + }); + expect(where).toEqual({ + id: { + [Op.in]: [12], + }, + }); + }); + + it('Op.in', () => { + const where = toWhere({ + 'id.in': [11, 12], + }); + expect(where).toEqual({ + id: { + [Op.in]: [11, 12], + }, + }); + }); + + it('Op.between', () => { + expect(toWhere({ + 'id.between': [1, 2] + })).toEqual({ + id: { [Op.between]: [1, 2] } + }); + }); + }); + + describe('group by logical operator', () => { + it('field.or', () => { + expect(toWhere({ + 'id.or': [1, 2] + })).toEqual({ + id: { [Op.or]: [1, 2] } + }); + }); + + it('field.and', () => { + expect(toWhere({ + 'id.and': [1, 2] + })).toEqual({ + id: { [Op.and]: [1, 2] } + }); + }); + + it('root or', () => { + expect(toWhere({ + or: [{ a: 1 }, { b: 2 }] + })).toEqual({ + [Op.or]: [{ a: 1 }, { b: 2 }] + }); + }); + + it('root and', () => { + expect(toWhere({ + and: [{ a: 1 }, { b: 2 }] + })).toEqual({ + [Op.and]: [{ a: 1 }, { b: 2 }] + }); + }); + + it('root "and" and "or"', () => { + expect(toWhere({ + and: [{ a: 1 }, { b: 2 }], + or: [{ c: 3 }, { d: 4 }] + })).toEqual({ + [Op.and]: [{ a: 1 }, { b: 2 }], + [Op.or]: [{ c: 3 }, { d: 4 }] + }); + }); + + it('root "and" and field', () => { + expect(toWhere({ + and: [{ a: 1 }, { b: 2 }], + 'id.or': [3, 4] + })).toEqual({ + [Op.and]: [{ a: 1 }, { b: 2 }], + id: { [Op.or]: [3, 4] } + }); + }); + + it('or in and', () => { + expect(toWhere({ + and: [{ a: 1 }, { 'b.or': [3, 4] }], + })).toEqual({ + [Op.and]: [{ a: 1 }, { b: { [Op.or]: [3, 4] } }], + }); + }); + + it('and in or', () => { + expect(toWhere({ + or: [{ a: 1 }, { and: [{ c: 3 }, { d: 4 }] }], + })).toEqual({ + [Op.or]: [{ a: 1 }, { [Op.and]: [{ c: 3 }, { d: 4 }] }], + }); + }); + + // TODO: bug + it.skip('field as or', () => { + expect(toWhere({ + or: 1, + })).toEqual({ + or: 1, + }); + }); + + // TODO: bug + it.skip('or for field as or', () => { + expect(toWhere({ + 'or.or': [1, 2], + })).toEqual({ + or: { [Op.or]: [1, 2] }, + }); + }); + }); + + describe('association', () => { + let db: Database; + let Foo: ModelCtor; + beforeAll(() => { + db = getDatabase(); + db.table({ + name: 'bazs', + }); + db.table({ + name: 'bars', + fields: [ + { + type: 'belongsTo', + name: 'baz', + } + ] + }); + db.table({ + name: 'foos', + fields: [ + { + type: 'hasMany', + name: 'bars', + } + ], + }); + Foo = db.getModel('foos'); + }); + afterAll(() => db.close()); + + const toWhereExpect = (options, logging = false) => { + const where = toWhere(options, { + associations: Foo.associations, + }); + return expect(where); + } + + it('association', () => { + toWhereExpect({ + col1: 'val1', + bars: { + name: { + ilike: 'aa', + }, + col2: { + lt: 2, + }, + baz: { + col1: 12, + }, + }, + 'bars.col3.ilike': 'aa', + }).toEqual({ + col1: 'val1', + $__include: { + bars: { + name: { + [Op.iLike]: 'aa', + }, + col2: { + [Op.lt]: 2, + }, + col3: { + [Op.iLike]: 'aa', + }, + $__include: { + baz: { + col1: 12 + }, + }, + }, + }, + }); + }); + }); +}); diff --git a/packages/database/src/utils.ts b/packages/database/src/utils.ts index ab6dbb9ac8..919d421766 100644 --- a/packages/database/src/utils.ts +++ b/packages/database/src/utils.ts @@ -19,9 +19,12 @@ interface ToWhereContext { } export function toWhere(options: any, context: ToWhereContext = {}) { - if (options === null || Array.isArray(options) || typeof options !== 'object') { + if (options === null || typeof options !== 'object') { return options; } + if (Array.isArray(options)) { + return options.map((item) => toWhere(item, context)); + } const { Model, associations = {}, ctx, dialect } = context; const items = {}; // 先处理「点号」的问题 @@ -48,6 +51,7 @@ export function toWhere(options: any, context: ToWhereContext = {}) { } } else { + // TODO: to fix same op key as field name values[op.has(key) ? op.get(key) : key] = toWhere(items[key], context); } }