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
This commit is contained in:
Junyi 2020-11-18 10:35:37 +08:00 committed by GitHub
parent 53729e188a
commit b0ba1472bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 982 additions and 836 deletions

View File

@ -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

View File

@ -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';

View File

@ -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: [

View File

@ -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);
};

View File

@ -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<Model>;
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,

View File

@ -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();
});
});
});
});

View File

@ -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',

View File

@ -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 () => {

View File

@ -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<Model>;
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<Model>;
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',
});
});
});
});

View File

@ -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',
});
});
});

View File

@ -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<Model>;
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',
}
}
]
});
});
});
});

View File

@ -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<Model>;
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
},
},
},
},
});
});
});
});

View File

@ -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);
}
}