mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
323 lines
9.1 KiB
323 lines
9.1 KiB
2 years ago
|
import fs from 'fs';
|
||
|
import process from 'process';
|
||
|
import { knex } from 'knex';
|
||
|
import SqlMgrv2 from '../../src/db/sql-mgr/v2/SqlMgrv2';
|
||
|
import type { Knex } from 'knex';
|
||
|
import type { DbConfig } from '../../src/interface/config';
|
||
|
import NcConfigFactory from '../../src/utils/NcConfigFactory'
|
||
|
|
||
|
export default class TestDbMngr {
|
||
|
public static readonly dbName = 'test_meta';
|
||
|
public static readonly sakilaDbName = 'test_sakila';
|
||
|
public static metaKnex: Knex;
|
||
|
public static sakilaKnex: Knex;
|
||
|
|
||
|
public static defaultConnection = {
|
||
|
user: 'root',
|
||
|
password: 'password',
|
||
|
host: 'localhost',
|
||
|
port: 3306,
|
||
|
client: 'mysql2',
|
||
|
};
|
||
|
|
||
|
public static pgConnection = {
|
||
|
user: 'postgres',
|
||
|
password: 'password',
|
||
|
host: 'localhost',
|
||
|
port: 5432,
|
||
|
client: 'pg',
|
||
|
};
|
||
|
|
||
|
public static connection: {
|
||
|
user: string;
|
||
|
password: string;
|
||
|
host: string;
|
||
|
port: number;
|
||
|
client: string;
|
||
|
} = TestDbMngr.defaultConnection;
|
||
|
|
||
|
public static dbConfig: DbConfig;
|
||
|
|
||
|
static populateConnectionConfig() {
|
||
|
const { user, password, host, port, client } = TestDbMngr.defaultConnection;
|
||
|
TestDbMngr.connection = {
|
||
|
user: process.env['DB_USER'] || user,
|
||
|
password: process.env['DB_PASSWORD'] || password,
|
||
|
host: process.env['DB_HOST'] || host,
|
||
|
port: Number(process.env['DB_PORT']) || port,
|
||
|
client: process.env['DB_CLIENT'] || client,
|
||
|
};
|
||
|
|
||
|
console.log(TestDbMngr.connection);
|
||
|
}
|
||
|
|
||
|
static async testConnection(config: DbConfig) {
|
||
|
try {
|
||
|
console.log('Testing connection', TestDbMngr.connection);
|
||
|
return await SqlMgrv2.testConnection(config);
|
||
|
} catch (e) {
|
||
|
console.log(e);
|
||
|
return { code: -1, message: 'Connection invalid' };
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static async init() {
|
||
|
TestDbMngr.populateConnectionConfig();
|
||
|
|
||
|
// common for both pg and mysql
|
||
|
if (await TestDbMngr.isDbConfigured()) {
|
||
|
await TestDbMngr.connectDb();
|
||
|
} else {
|
||
|
console.log('Mysql is not configured. Switching to sqlite');
|
||
|
await TestDbMngr.switchToSqlite();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static async isDbConfigured() {
|
||
|
const { user, password, host, port, client } = TestDbMngr.connection;
|
||
|
const config = NcConfigFactory.urlToDbConfig(
|
||
|
`${client}://${user}:${password}@${host}:${port}`,
|
||
|
);
|
||
|
config.connection = {
|
||
|
user,
|
||
|
password,
|
||
|
host,
|
||
|
port,
|
||
|
};
|
||
|
const result = await TestDbMngr.testConnection(config);
|
||
|
return result.code !== -1;
|
||
|
}
|
||
|
static async connectDb() {
|
||
|
const { user, password, host, port, client } = TestDbMngr.connection;
|
||
|
if (!process.env[`DATABASE_URL`]) {
|
||
|
process.env[
|
||
|
`DATABASE_URL`
|
||
|
] = `${client}://${user}:${password}@${host}:${port}/${TestDbMngr.dbName}`;
|
||
|
}
|
||
|
|
||
|
TestDbMngr.dbConfig = NcConfigFactory.urlToDbConfig(
|
||
|
NcConfigFactory.extractXcUrlFromJdbc(process.env[`DATABASE_URL`]),
|
||
|
);
|
||
|
this.dbConfig.meta = {
|
||
|
tn: 'nc_evolutions',
|
||
|
dbAlias: 'db',
|
||
|
api: {
|
||
|
type: 'rest',
|
||
|
prefix: '',
|
||
|
graphqlDepthLimit: 10,
|
||
|
},
|
||
|
inflection: {
|
||
|
tn: 'camelize',
|
||
|
cn: 'camelize',
|
||
|
},
|
||
|
};
|
||
|
|
||
|
await TestDbMngr.setupMeta();
|
||
|
await TestDbMngr.setupSakila();
|
||
|
}
|
||
|
|
||
|
static async setupMeta() {
|
||
|
if (TestDbMngr.metaKnex) {
|
||
|
await TestDbMngr.metaKnex.destroy();
|
||
|
}
|
||
|
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
await TestDbMngr.resetMetaSqlite();
|
||
|
TestDbMngr.metaKnex = knex(TestDbMngr.getMetaDbConfig());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TestDbMngr.metaKnex = knex(TestDbMngr.getDbConfigWithNoDb());
|
||
|
await TestDbMngr.resetDatabase(TestDbMngr.metaKnex, TestDbMngr.dbName);
|
||
|
await TestDbMngr.metaKnex.destroy();
|
||
|
|
||
|
TestDbMngr.metaKnex = knex(TestDbMngr.getMetaDbConfig());
|
||
|
await TestDbMngr.useDatabase(TestDbMngr.metaKnex, TestDbMngr.dbName);
|
||
|
}
|
||
|
|
||
|
static async setupSakila() {
|
||
|
if (TestDbMngr.sakilaKnex) {
|
||
|
await TestDbMngr.sakilaKnex.destroy();
|
||
|
}
|
||
|
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
await TestDbMngr.seedSakila();
|
||
|
TestDbMngr.sakilaKnex = knex(TestDbMngr.getSakilaDbConfig());
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TestDbMngr.sakilaKnex = knex(TestDbMngr.getDbConfigWithNoDb());
|
||
|
await TestDbMngr.resetDatabase(
|
||
|
TestDbMngr.sakilaKnex,
|
||
|
TestDbMngr.sakilaDbName,
|
||
|
);
|
||
|
await TestDbMngr.sakilaKnex.destroy();
|
||
|
|
||
|
TestDbMngr.sakilaKnex = knex(TestDbMngr.getSakilaDbConfig());
|
||
|
await TestDbMngr.useDatabase(
|
||
|
TestDbMngr.sakilaKnex,
|
||
|
TestDbMngr.sakilaDbName,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static async switchToSqlite() {
|
||
|
// process.env[`DATABASE_URL`] = `sqlite3:///?database=${__dirname}/${TestDbMngr.dbName}.sqlite`;
|
||
|
TestDbMngr.dbConfig = {
|
||
|
client: 'sqlite3',
|
||
|
connection: {
|
||
|
filename: `${__dirname}/${TestDbMngr.dbName}.db`,
|
||
|
database: TestDbMngr.dbName,
|
||
|
},
|
||
|
useNullAsDefault: true,
|
||
|
meta: {
|
||
|
tn: 'nc_evolutions',
|
||
|
dbAlias: 'db',
|
||
|
api: {
|
||
|
type: 'rest',
|
||
|
prefix: '',
|
||
|
graphqlDepthLimit: 10,
|
||
|
},
|
||
|
inflection: {
|
||
|
tn: 'camelize',
|
||
|
cn: 'camelize',
|
||
|
},
|
||
|
},
|
||
|
};
|
||
|
|
||
|
process.env[
|
||
|
`NC_DB`
|
||
|
] = `sqlite3:///?database=${__dirname}/${TestDbMngr.dbName}.db`;
|
||
|
await TestDbMngr.setupMeta();
|
||
|
await TestDbMngr.setupSakila();
|
||
|
}
|
||
|
|
||
|
private static async resetDatabase(knexClient, dbName) {
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
// return knexClient.raw(`DELETE FROM sqlite_sequence`);
|
||
|
} else {
|
||
|
try {
|
||
|
await knexClient.raw(`DROP DATABASE ${dbName}`);
|
||
|
} catch (e) {}
|
||
|
await knexClient.raw(`CREATE DATABASE ${dbName}`);
|
||
|
console.log(`Database ${dbName} created`);
|
||
|
|
||
|
if (!TestDbMngr.isPg()) {
|
||
|
await knexClient.raw(`USE ${dbName}`);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static isSqlite() {
|
||
|
return TestDbMngr.dbConfig.client === 'sqlite3';
|
||
|
}
|
||
|
|
||
|
static isPg() {
|
||
|
return TestDbMngr.dbConfig.client === 'pg';
|
||
|
}
|
||
|
|
||
|
private static async useDatabase(knexClient, dbName) {
|
||
|
if (!TestDbMngr.isSqlite() && !TestDbMngr.isPg()) {
|
||
|
await knexClient.raw(`USE ${dbName}`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static getDbConfigWithNoDb() {
|
||
|
const dbConfig = JSON.parse(JSON.stringify(TestDbMngr.dbConfig));
|
||
|
delete dbConfig.connection.database;
|
||
|
return dbConfig;
|
||
|
}
|
||
|
|
||
|
static getMetaDbConfig() {
|
||
|
return TestDbMngr.dbConfig;
|
||
|
}
|
||
|
|
||
|
private static resetMetaSqlite() {
|
||
|
if (fs.existsSync(`${__dirname}/test_meta.db`)) {
|
||
|
fs.unlinkSync(`${__dirname}/test_meta.db`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static getSakilaDbConfig() {
|
||
|
const sakilaDbConfig = JSON.parse(JSON.stringify(TestDbMngr.dbConfig));
|
||
|
sakilaDbConfig.connection.database = TestDbMngr.sakilaDbName;
|
||
|
sakilaDbConfig.connection.multipleStatements = true;
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
sakilaDbConfig.connection.filename = `${__dirname}/test_sakila.db`;
|
||
|
}
|
||
|
return sakilaDbConfig;
|
||
|
}
|
||
|
|
||
|
static async seedSakila() {
|
||
|
const testsDir = __dirname.replace('tests/unit', 'tests');
|
||
|
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
if (fs.existsSync(`${__dirname}/test_sakila.db`)) {
|
||
|
fs.unlinkSync(`${__dirname}/test_sakila.db`);
|
||
|
}
|
||
|
fs.copyFileSync(
|
||
|
`${testsDir}/sqlite-sakila-db/sakila.db`,
|
||
|
`${__dirname}/test_sakila.db`,
|
||
|
);
|
||
|
} else if (TestDbMngr.isPg()) {
|
||
|
const schemaFile = fs
|
||
|
.readFileSync(`${testsDir}/pg-sakila-db/01-postgres-sakila-schema.sql`)
|
||
|
.toString();
|
||
|
const dataFile = fs
|
||
|
.readFileSync(
|
||
|
`${testsDir}/pg-sakila-db/02-postgres-sakila-insert-data.sql`,
|
||
|
)
|
||
|
.toString();
|
||
|
await TestDbMngr.sakilaKnex.raw(schemaFile);
|
||
|
await TestDbMngr.sakilaKnex.raw(dataFile);
|
||
|
} else {
|
||
|
const schemaFile = fs
|
||
|
.readFileSync(`${testsDir}/mysql-sakila-db/03-test-sakila-schema.sql`)
|
||
|
.toString();
|
||
|
const dataFile = fs
|
||
|
.readFileSync(`${testsDir}/mysql-sakila-db/04-test-sakila-data.sql`)
|
||
|
.toString();
|
||
|
await TestDbMngr.sakilaKnex.raw(schemaFile);
|
||
|
await TestDbMngr.sakilaKnex.raw(dataFile);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static async disableForeignKeyChecks(knexClient) {
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
await knexClient.raw('PRAGMA foreign_keys = OFF');
|
||
|
} else if (TestDbMngr.isPg()) {
|
||
|
await knexClient.raw(`SET session_replication_role = 'replica'`);
|
||
|
} else {
|
||
|
await knexClient.raw(`SET FOREIGN_KEY_CHECKS = 0`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static async enableForeignKeyChecks(knexClient) {
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
await knexClient.raw(`PRAGMA foreign_keys = ON;`);
|
||
|
} else if (TestDbMngr.isPg()) {
|
||
|
await knexClient.raw(`SET session_replication_role = 'origin'`);
|
||
|
} else {
|
||
|
await knexClient.raw(`SET FOREIGN_KEY_CHECKS = 1`);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static async showAllTables(knexClient) {
|
||
|
if (TestDbMngr.isSqlite()) {
|
||
|
const tables = await knexClient.raw(
|
||
|
`SELECT name FROM sqlite_master WHERE type='table'`,
|
||
|
);
|
||
|
return tables
|
||
|
.filter((t) => t.name !== 'sqlite_sequence' && t.name !== '_evolutions')
|
||
|
.map((t) => t.name);
|
||
|
} else if (TestDbMngr.isPg()) {
|
||
|
const tables = await knexClient.raw(
|
||
|
`SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname != 'pg_catalog' AND schemaname != 'information_schema';`,
|
||
|
);
|
||
|
return tables.rows.map((t) => t.tablename);
|
||
|
} else {
|
||
|
const response = await knexClient.raw(`SHOW TABLES`);
|
||
|
return response[0].map((table) => Object.values(table)[0]);
|
||
|
}
|
||
|
}
|
||
|
}
|