From 990c8fc4b4f63ed748b62f11bb921751b4f52dd1 Mon Sep 17 00:00:00 2001 From: Muhammed Mustafa Date: Fri, 2 Sep 2022 22:17:40 +0530 Subject: [PATCH] refactor/Added unit test for bulk insert, update and delete apis --- .../nocodb/src/__tests__/unit/rest/init/db.ts | 10 ++ .../src/__tests__/unit/rest/init/index.ts | 2 +- .../__tests__/unit/rest/tests/factory/row.ts | 68 +++++++-- .../unit/rest/tests/tableRow.test.ts | 134 +++++++++++++++++- 4 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 packages/nocodb/src/__tests__/unit/rest/init/db.ts diff --git a/packages/nocodb/src/__tests__/unit/rest/init/db.ts b/packages/nocodb/src/__tests__/unit/rest/init/db.ts new file mode 100644 index 0000000000..06b50d4368 --- /dev/null +++ b/packages/nocodb/src/__tests__/unit/rest/init/db.ts @@ -0,0 +1,10 @@ +import { DbConfig } from '../../../../interface/config'; + +const isSqlite = (context) => + (context.dbConfig as DbConfig).client === 'sqlite'; + +const isMysql = (context) => + (context.dbConfig as DbConfig).client === 'mysql' || + (context.dbConfig as DbConfig).client === 'mysql2'; + +export { isSqlite, isMysql }; diff --git a/packages/nocodb/src/__tests__/unit/rest/init/index.ts b/packages/nocodb/src/__tests__/unit/rest/init/index.ts index f8437274f9..f855fd69d6 100644 --- a/packages/nocodb/src/__tests__/unit/rest/init/index.ts +++ b/packages/nocodb/src/__tests__/unit/rest/init/index.ts @@ -64,5 +64,5 @@ export default async function () { const { token } = await createUser({ app: server }, { roles: 'editor' }); - return { app: server, token }; + return { app: server, token, dbConfig }; } diff --git a/packages/nocodb/src/__tests__/unit/rest/tests/factory/row.ts b/packages/nocodb/src/__tests__/unit/rest/tests/factory/row.ts index 8370830c29..c58c1562c1 100644 --- a/packages/nocodb/src/__tests__/unit/rest/tests/factory/row.ts +++ b/packages/nocodb/src/__tests__/unit/rest/tests/factory/row.ts @@ -1,13 +1,14 @@ import { ColumnType, UITypes } from 'nocodb-sdk'; import request from 'supertest'; import Column from '../../../../../lib/models/Column'; +import Filter from '../../../../../lib/models/Filter'; import Model from '../../../../../lib/models/Model'; import Project from '../../../../../lib/models/Project'; +import Sort from '../../../../../lib/models/Sort'; +import NcConnectionMgrv2 from '../../../../../lib/utils/common/NcConnectionMgrv2'; const rowValue = (column: ColumnType, index: number) => { switch (column.uidt) { - case UITypes.ID: - return index; case UITypes.Number: return index; case UITypes.SingleLineText: @@ -31,6 +32,30 @@ const getRow = async (context, project, table, id) => { return response.body; }; +const listRow = async ({ + project, + table, + options, +}: { + project: Project; + table: Model; + options?: { + limit?: any; + offset?: any; + filterArr?: Filter[]; + sortArr?: Sort[]; + }; +}) => { + const baseModel = await Model.getBaseModelSQL({ + id: table.id, + dbDriver: NcConnectionMgrv2.get(project.bases[0]), + }); + + const ignorePagination = !options; + + return await baseModel.list(options, ignorePagination); +}; + const getOneRow = async ( context, { project, table }: { project: Project; table: Model } @@ -42,6 +67,25 @@ const getOneRow = async ( return response.body; }; +const generateDefaultRowAttributes = ({ + columns, + index, +}: { + columns: ColumnType[]; + index: number; +}) => + columns.reduce((acc, column) => { + if ( + column.uidt === UITypes.LinkToAnotherRecord || + column.uidt === UITypes.ForeignKey || + column.uidt === UITypes.ID + ) { + return acc; + } + acc[column.column_name] = rowValue(column, index); + return acc; + }, {}); + const createRow = async ( context, { @@ -55,16 +99,7 @@ const createRow = async ( } ) => { const columns = await table.getColumns(); - const rowData = columns.reduce((acc, column) => { - if ( - column.uidt === UITypes.LinkToAnotherRecord || - column.uidt === UITypes.ForeignKey - ) { - return acc; - } - acc[column.column_name] = rowValue(column, index); - return acc; - }, {}); + const rowData = generateDefaultRowAttributes({ columns, index }); const response = await request(context.app) .post(`/api/v1/db/data/noco/${project.id}/${table.id}`) @@ -112,4 +147,11 @@ const createRelation = async ( .set('xc-auth', context.token); }; -export { createRow, getRow, createRelation, getOneRow }; +export { + createRow, + getRow, + createRelation, + getOneRow, + listRow, + generateDefaultRowAttributes, +}; diff --git a/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts b/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts index b1271bb141..4892c530ca 100644 --- a/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts +++ b/packages/nocodb/src/__tests__/unit/rest/tests/tableRow.test.ts @@ -11,7 +11,15 @@ import { createRollupColumn, } from './factory/column'; import { createTable } from './factory/table'; -import { createRelation, createRow, getOneRow, getRow } from './factory/row'; +import { + createRelation, + createRow, + generateDefaultRowAttributes, + getOneRow, + getRow, + listRow, +} from './factory/row'; +import { isMysql, isSqlite } from '../init/db'; const isColumnsCorrectInResponse = (row, columns: ColumnType[]) => { const responseColumnsListStr = Object.keys(row).sort().join(','); @@ -1263,6 +1271,130 @@ function tableTest() { throw new Error('Should not exist'); } }); + + it('Bulk insert', async function () { + const table = await createTable(context, project); + const columns = await table.getColumns(); + + const rowAttributes = Array(99) + .fill(0) + .map((index) => generateDefaultRowAttributes({ columns, index })); + + const response = await request(context.app) + .post(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .send(rowAttributes) + .expect(200); + + const rows = await listRow({ project, table }); + console.log(rows.length); + // Mysql will not return the batched inserted rows + if (!isMysql(context)) { + if ( + !isSqlite(context) && + response.body.length !== rowAttributes.length && + rows.length !== rowAttributes.length + ) { + throw new Error('Wrong number of rows inserted'); + } + + // Max 10 rows will be inserted in sqlite + if (isSqlite(context) && response.body.length !== 10) { + throw new Error('Wrong number of rows inserted'); + } + } else { + if (rows.length !== rowAttributes.length) { + throw new Error('Wrong number of rows inserted'); + } + } + }); + + it('Bulk insert 400 records', async function () { + const table = await createTable(context, project); + const columns = await table.getColumns(); + + const rowAttributes = Array(400) + .fill(0) + .map((index) => generateDefaultRowAttributes({ columns, index })); + + const response = await request(context.app) + .post(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .send(rowAttributes) + .expect(200); + + const rows = await listRow({ project, table }); + + // Mysql will not return the batched inserted rows + if (!isMysql(context)) { + if ( + !isSqlite(context) && + response.body.length !== rowAttributes.length && + rows.length !== rowAttributes.length + ) { + throw new Error('Wrong number of rows inserted'); + } + + // Max 10 rows will be inserted in sqlite + if (isSqlite(context) && response.body.length !== 10) { + throw new Error('Wrong number of rows inserted'); + } + } else { + if (rows.length !== rowAttributes.length) { + throw new Error('Wrong number of rows inserted'); + } + } + }); + + it('Bulk update', async function () { + const table = await createTable(context, project); + + const arr = Array(120) + .fill(0) + .map((_, index) => index); + for (const index of arr) { + await createRow(context, { project, table, index }); + } + + const rows = await listRow({ project, table }); + + await request(context.app) + .patch(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .send( + rows.map((row) => ({ title: `new-${row['Title']}`, id: row['Id'] })) + ) + .expect(200); + + const updatedRows: Array = await listRow({ project, table }); + if (!updatedRows.every((row) => row['Title'].startsWith('new-'))) { + throw new Error('Wrong number of rows updated'); + } + }); + + it('Bulk delete', async function () { + const table = await createTable(context, project); + + const arr = Array(120) + .fill(0) + .map((_, index) => index); + for (const index of arr) { + await createRow(context, { project, table, index }); + } + + const rows = await listRow({ project, table }); + + await request(context.app) + .delete(`/api/v1/db/data/bulk/noco/${project.id}/${table.id}`) + .set('xc-auth', context.token) + .send(rows.map((row) => ({ id: row['Id'] }))) + .expect(200); + + const updatedRows: Array = await listRow({ project, table }); + if (updatedRows.length !== 0) { + throw new Error('Wrong number of rows delete'); + } + }); } export default function () {