From c5f639338fd0a851a5ecb28b856c994085443e28 Mon Sep 17 00:00:00 2001 From: chenos Date: Tue, 15 Feb 2022 00:20:25 +0800 Subject: [PATCH] refactor: mockDatabase --- .../database/src/__tests__/collection.test.ts | 12 +-- .../src/__tests__/filter-parser.test.ts | 4 +- packages/database/src/__tests__/index.ts | 95 +------------------ packages/database/src/database.ts | 12 ++- packages/database/src/index.ts | 2 +- packages/database/src/mock-database.ts | 37 ++++++++ packages/server/src/application.ts | 4 +- .../test/src/__tests__/mockDatabase.test.ts | 14 --- .../test/src/__tests__/mockServer.test.ts | 34 ------- packages/test/src/index.ts | 3 +- packages/test/src/mockDatabase.ts | 45 --------- packages/test/src/mockServer.ts | 9 +- 12 files changed, 67 insertions(+), 204 deletions(-) create mode 100644 packages/database/src/mock-database.ts delete mode 100644 packages/test/src/__tests__/mockDatabase.test.ts delete mode 100644 packages/test/src/__tests__/mockServer.test.ts delete mode 100644 packages/test/src/mockDatabase.ts diff --git a/packages/database/src/__tests__/collection.test.ts b/packages/database/src/__tests__/collection.test.ts index 52817ded7..4815c7b9c 100644 --- a/packages/database/src/__tests__/collection.test.ts +++ b/packages/database/src/__tests__/collection.test.ts @@ -1,6 +1,6 @@ import { Collection } from '../collection'; import { Database } from '../database'; -import { generatePrefixByPath, mockDatabase } from './index'; +import { mockDatabase } from './index'; test('collection disable authGenId', async () => { const db = mockDatabase(); @@ -91,7 +91,7 @@ describe('collection sync', () => { ]); await collection.sync(); - const tableFields = await (collection.model).queryInterface.describeTable(`${generatePrefixByPath()}_users`); + const tableFields = await (collection.model).queryInterface.describeTable(`${db.getTablePrefix()}users`); expect(tableFields).toHaveProperty('firstName'); expect(tableFields).toHaveProperty('lastName'); @@ -114,7 +114,7 @@ describe('collection sync', () => { const model = collection.model; - const tableFields = await (model).queryInterface.describeTable(`${generatePrefixByPath()}_posts`); + const tableFields = await (model).queryInterface.describeTable(`${db.getTablePrefix()}posts`); expect(tableFields['user_id']).toBeUndefined(); }); @@ -143,7 +143,7 @@ describe('collection sync', () => { const model = collection.model; await collection.sync(); - const tableFields = await (model).queryInterface.describeTable(`${generatePrefixByPath()}_posts_tags`); + const tableFields = await (model).queryInterface.describeTable(`${db.getTablePrefix()}posts_tags`); expect(tableFields['postId']).toBeDefined(); expect(tableFields['tagId']).toBeDefined(); }); @@ -184,13 +184,13 @@ test.skip('update collection options', async () => { }, ); - expect(collection.model.getTableName()).toEqual(`${generatePrefixByPath()}_posts`); + expect(collection.model.getTableName()).toEqual(`${db.getTablePrefix()}posts`); collection.updateOptions({ name: 'articles', }); - expect(collection.model.getTableName()).toEqual(`${generatePrefixByPath()}_articles`); + expect(collection.model.getTableName()).toEqual(`${db.getTablePrefix()}articles`); }); test('collection with association', async () => { diff --git a/packages/database/src/__tests__/filter-parser.test.ts b/packages/database/src/__tests__/filter-parser.test.ts index 0ef3fde3b..f3e285d66 100644 --- a/packages/database/src/__tests__/filter-parser.test.ts +++ b/packages/database/src/__tests__/filter-parser.test.ts @@ -1,7 +1,7 @@ -import { mockDatabase } from './index'; -import FilterParser from '../filter-parser'; import { Op } from 'sequelize'; import { Database } from '../database'; +import FilterParser from '../filter-parser'; +import { mockDatabase } from './index'; test('filter item by string', async () => { const database = mockDatabase(); diff --git a/packages/database/src/__tests__/index.ts b/packages/database/src/__tests__/index.ts index aa23280f1..c78cfa57d 100644 --- a/packages/database/src/__tests__/index.ts +++ b/packages/database/src/__tests__/index.ts @@ -1,94 +1 @@ -import { uid } from '@nocobase/utils'; -import merge from 'deepmerge'; -import { Sequelize } from 'sequelize'; -import { Database, DatabaseOptions } from '../database'; - -export function generatePrefixByPath() { - const { id } = require.main; - const key = id - .replace(`${process.env.PWD}/packages`, '') - .replace(/src\/__tests__/g, '') - .replace('.test.ts', '') - .replace(/[^\w]/g, '_') - .replace(/_+/g, '_'); - return key; -} - -export function getConfig(config: any = {}, options?: any): DatabaseOptions { - if (process.env.DB_DIALECT === 'sqlite') { - const defaults = { - dialect: process.env.DB_DIALECT as any, - storage: ':memory:', - logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, - // sync: { - // force: true, - // }, - hooks: { - beforeDefine(model, options) { - options.tableName = `${generatePrefixByPath()}_${ - options.tableName || options.modelName || options.name.plural - }`; - }, - }, - }; - return merge(defaults, config, options); - } - const database = `mock_${uid()}`; - let dbExists = false; - const defaults = { - username: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: process.env.DB_DATABASE, - host: process.env.DB_HOST, - port: process.env.DB_PORT as any, - dialect: process.env.DB_DIALECT as any, - logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, - dialectOptions: { - charset: 'utf8mb4', - collate: 'utf8mb4_unicode_ci', - }, - hooks: { - beforeDefine(model, options) { - options.tableName = `${generatePrefixByPath()}_${ - options.tableName || options.modelName || options.name.plural - }`; - }, - async beforeSync({ sequelize }: any) { - if (config.database) { - return; - } - if (dbExists) { - return; - } - return; - const db = new Sequelize({ - username: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: process.env.DB_DATABASE, - host: process.env.DB_HOST, - port: process.env.DB_PORT as any, - dialect: process.env.DB_DIALECT as any, - logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, - dialectOptions: { - charset: 'utf8mb4', - collate: 'utf8mb4_unicode_ci', - }, - }); - await db.query(`CREATE DATABASE "${database}";`); - await db.close(); - sequelize.options.database = database; - sequelize.config.database = database; - const ConnectionManager = sequelize.dialect.connectionManager.constructor; - const connectionManager = new ConnectionManager(sequelize.dialect, sequelize); - sequelize.dialect.connectionManager = connectionManager; - sequelize.connectionManager = connectionManager; - dbExists = true; - }, - }, - }; - return merge(defaults, config, options); -} - -export function mockDatabase(options?: DatabaseOptions): Database { - return new Database(getConfig(options)); -} +export { mockDatabase } from '../mock-database'; diff --git a/packages/database/src/database.ts b/packages/database/src/database.ts index 986d72b8b..b9257b573 100644 --- a/packages/database/src/database.ts +++ b/packages/database/src/database.ts @@ -31,7 +31,11 @@ interface MapOf { [key: string]: T; } -export type DatabaseOptions = Options | Sequelize; +export interface IDatabaseOptions extends Options { + tablePrefix?: string; +} + +export type DatabaseOptions = IDatabaseOptions | Sequelize; interface RegisterOperatorsContext { db?: Database; @@ -48,6 +52,7 @@ type OperatorFunc = (value: any, ctx?: RegisterOperatorsContext) => any; export class Database extends EventEmitter implements AsyncEmitter { sequelize: Sequelize; fieldTypes = new Map(); + options: IDatabaseOptions; models = new Map>(); repositories = new Map(); operators = new Map(); @@ -66,6 +71,7 @@ export class Database extends EventEmitter implements AsyncEmitter { this.sequelize = options; } else { this.sequelize = new Sequelize(options); + this.options = options; } this.collections = new Map(); @@ -115,6 +121,10 @@ export class Database extends EventEmitter implements AsyncEmitter { return collection; } + getTablePrefix() { + return this.options.tablePrefix || ''; + } + /** * get exists collection by its name * @param name diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index d028c771d..5b34b6c4a 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -4,6 +4,7 @@ export * from './database'; export { Database as default } from './database'; export * from './fields'; export * from './magic-attribute-model'; +export * from './mock-database'; export * from './relation-repository/belongs-to-many-repository'; export * from './relation-repository/belongs-to-repository'; export * from './relation-repository/hasmany-repository'; @@ -12,4 +13,3 @@ export * from './relation-repository/single-relation-repository'; export * from './repository'; export * from './update-associations'; - diff --git a/packages/database/src/mock-database.ts b/packages/database/src/mock-database.ts new file mode 100644 index 000000000..b0a75822b --- /dev/null +++ b/packages/database/src/mock-database.ts @@ -0,0 +1,37 @@ +import { merge, uid } from '@nocobase/utils'; +import { resolve } from 'path'; +import { Database, IDatabaseOptions } from './database'; + +export class MockDatabase extends Database { + constructor(options: IDatabaseOptions) { + super({ + storage: ':memory:', + tablePrefix: `mock_${uid(6)}_`, + ...options, + }); + this.sequelize.beforeDefine((model, opts) => { + opts.tableName = `${this.getTablePrefix()}${opts.tableName || opts.modelName || opts.name.plural}`; + }); + } +} + +export function getConfigByEnv() { + return { + username: process.env.DB_USER, + password: process.env.DB_PASSWORD, + database: process.env.DB_DATABASE, + host: process.env.DB_HOST, + port: process.env.DB_PORT, + dialect: process.env.DB_DIALECT, + logging: process.env.DB_LOG_SQL === 'on' ? console.log : false, + storage: process.env.DB_STORAGE ? resolve(process.cwd(), process.env.DB_STORAGE) : ':memory:', + dialectOptions: { + charset: 'utf8mb4', + collate: 'utf8mb4_unicode_ci', + }, + }; +} + +export function mockDatabase(options: IDatabaseOptions = {}): MockDatabase { + return new MockDatabase(merge(getConfigByEnv(), options)); +} diff --git a/packages/server/src/application.ts b/packages/server/src/application.ts index dbb8e8602..c6176e53f 100644 --- a/packages/server/src/application.ts +++ b/packages/server/src/application.ts @@ -1,6 +1,6 @@ import { ACL } from '@nocobase/acl'; import { registerActions } from '@nocobase/actions'; -import Database, { CleanOptions, CollectionOptions, DatabaseOptions, SyncOptions } from '@nocobase/database'; +import Database, { CleanOptions, CollectionOptions, IDatabaseOptions, SyncOptions } from '@nocobase/database'; import Resourcer, { ResourceOptions } from '@nocobase/resourcer'; import { applyMixins, AsyncEmitter } from '@nocobase/utils'; import { Command, CommandOptions } from 'commander'; @@ -18,7 +18,7 @@ export interface ResourcerOptions { } export interface ApplicationOptions { - database?: DatabaseOptions; + database?: IDatabaseOptions | Database; resourcer?: ResourcerOptions; bodyParser?: any; cors?: any; diff --git a/packages/test/src/__tests__/mockDatabase.test.ts b/packages/test/src/__tests__/mockDatabase.test.ts deleted file mode 100644 index ba65f90a8..000000000 --- a/packages/test/src/__tests__/mockDatabase.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { mockDatabase } from '../'; - -describe('mock databasea', () => { - it('mock databasea', async () => { - const db = mockDatabase(); - db.collection({ - name: 'tests', - fields: [{ type: 'string', name: 'name' }], - }); - expect(db.getCollection('tests').model.getTableName()).toBe('_test_mockDatabase_tests'); - await db.sync(); - await db.close(); - }); -}); diff --git a/packages/test/src/__tests__/mockServer.test.ts b/packages/test/src/__tests__/mockServer.test.ts deleted file mode 100644 index 27f24f4bf..000000000 --- a/packages/test/src/__tests__/mockServer.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { mockServer, MockServer } from '../'; - -describe('mock server', () => { - let api: MockServer; - - beforeEach(() => { - api = mockServer({ - dataWrapping: false, - }); - api.resourcer.registerActionHandlers({ - list: async (ctx, next) => { - ctx.body = [1, 2]; - await next(); - }, - }); - api.resourcer.define({ - name: 'test', - }); - }); - - afterEach(async () => { - return api.destroy(); - }); - - it('agent', async () => { - const response = await api.agent().get('/test'); - expect(response.body).toEqual([1, 2]); - }); - - it('resource', async () => { - const response = await api.agent().resource('test').list(); - expect(response.body).toEqual([1, 2]); - }); -}); diff --git a/packages/test/src/index.ts b/packages/test/src/index.ts index 63badf75a..4f222fecb 100644 --- a/packages/test/src/index.ts +++ b/packages/test/src/index.ts @@ -1,2 +1,3 @@ -export * from './mockDatabase'; +export { mockDatabase } from '@nocobase/database'; export * from './mockServer'; + diff --git a/packages/test/src/mockDatabase.ts b/packages/test/src/mockDatabase.ts deleted file mode 100644 index de0461092..000000000 --- a/packages/test/src/mockDatabase.ts +++ /dev/null @@ -1,45 +0,0 @@ -import merge from 'deepmerge'; -import Database, { DatabaseOptions } from '@nocobase/database'; - -export function generatePrefixByPath() { - const { id } = require.main; - const key = id - .replace(`${process.env.PWD}/packages`, '') - .replace(/src\/__tests__/g, '') - .replace('.test.ts', '') - .replace(/[^\w]/g, '_') - .replace(/_+/g, '_'); - return key; -} - -export function getConfig(config = {}, options?: any): DatabaseOptions { - return merge( - { - username: process.env.DB_USER, - password: process.env.DB_PASSWORD, - storage: process.env.DB_STORAGE, - database: process.env.DB_DATABASE, - host: process.env.DB_HOST, - port: process.env.DB_PORT, - dialect: process.env.DB_DIALECT, - logging: process.env.DB_LOG_SQL === 'on', - sync: { - force: true, - alter: { - drop: true, - }, - }, - hooks: { - beforeDefine(model, options) { - options.tableName = `${generatePrefixByPath()}_${options.tableName || options.name.plural}`; - }, - }, - }, - config || {}, - options, - ) as any; -} - -export function mockDatabase(options?: DatabaseOptions): Database { - return new Database(getConfig(options)); -} diff --git a/packages/test/src/mockServer.ts b/packages/test/src/mockServer.ts index ee31cc765..5e4673626 100644 --- a/packages/test/src/mockServer.ts +++ b/packages/test/src/mockServer.ts @@ -1,7 +1,7 @@ +import { mockDatabase } from '@nocobase/database'; +import Application, { ApplicationOptions } from '@nocobase/server'; import qs from 'qs'; import supertest, { SuperAgentTest } from 'supertest'; -import Application, { ApplicationOptions } from '@nocobase/server'; -import { getConfig } from './mockDatabase'; interface ActionParams { filterByTk?: any; @@ -123,10 +123,11 @@ export class MockServer extends Application { } } -export function mockServer(options?: ApplicationOptions) { +export function mockServer(options: ApplicationOptions = {}) { + const database = mockDatabase((options?.database) || {}); return new MockServer({ ...options, - database: getConfig(options?.database), + database, }); }