diff --git a/packages/shared/lib/components/ActiontechTable/hooks/test/useTableRequestParams.test.tsx b/packages/shared/lib/components/ActiontechTable/hooks/test/useTableRequestParams.test.tsx index 398963539..2793b19aa 100644 --- a/packages/shared/lib/components/ActiontechTable/hooks/test/useTableRequestParams.test.tsx +++ b/packages/shared/lib/components/ActiontechTable/hooks/test/useTableRequestParams.test.tsx @@ -1,5 +1,6 @@ import { renderHook, act, cleanup } from '@testing-library/react'; import useTableRequestParams from '../useTableRequestParams'; +import { PaginationProps } from 'antd'; describe('lib/ActiontechTable-hooks-useTableRequestParams', () => { beforeEach(() => { @@ -37,16 +38,66 @@ describe('lib/ActiontechTable-hooks-useTableRequestParams', () => { it('render use createSortParams', async () => { const { result } = renderHook(() => useTableRequestParams()); + const params: { order_by?: string; is_asc?: boolean } = {}; + result.current.createSortParams(params); + expect(params).toEqual({}); + await act(async () => { - const sortInfo = result.current.sortInfo; - expect(sortInfo).toEqual({}); + result.current.tableChange( + {} as PaginationProps, + {} as Record, + { + column: { + dataIndex: 'query_time_max', + title: '最长执行时间', + sorter: true + }, + order: 'ascend', + field: 'query_time_max' + }, + {} as any + ); + }); - result.current.createSortParams({ - order_by: 'aa', - field: 'aa', - is_asc: true - }); - expect(sortInfo).toEqual({}); + expect(result.current.sortInfo).toEqual({ + column: { + dataIndex: 'query_time_max', + title: '最长执行时间', + sorter: true + }, + order: 'ascend', + field: 'query_time_max' + }); + + result.current.createSortParams(params); + expect(params).toEqual({ + is_asc: true, + order_by: 'query_time_max' + }); + + await act(async () => { + result.current.tableChange( + {} as PaginationProps, + {} as Record, + [ + { + column: { + dataIndex: 'last_receive_timestamp', + title: '最后匹配时间', + sorter: true + }, + order: 'descend', + field: 'last_receive_timestamp' + } + ], + {} as any + ); + }); + + result.current.createSortParams(params); + expect(params).toEqual({ + is_asc: false, + order_by: 'last_receive_timestamp' }); }); diff --git a/packages/sqle/src/hooks/useBackendTable/__snapshots__/useBackendTable.test.tsx.snap b/packages/sqle/src/hooks/useBackendTable/__snapshots__/useBackendTable.test.tsx.snap index 50f4ce48a..e4f765fe1 100644 --- a/packages/sqle/src/hooks/useBackendTable/__snapshots__/useBackendTable.test.tsx.snap +++ b/packages/sqle/src/hooks/useBackendTable/__snapshots__/useBackendTable.test.tsx.snap @@ -1,5 +1,421 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`useBackendTable should create table filter meta and custom props based on filter meta list 1`] = ` +{ + "extraTableFilterMeta": Map { + "sql" => { + "checked": false, + "filterCustomType": "input", + "filterKey": "sql", + "filterLabel": "SQL", + }, + "rule_name" => { + "checked": false, + "filterCustomType": "select", + "filterKey": "rule_name", + "filterLabel": "审核规则", + }, + "db_user" => { + "checked": false, + "filterCustomType": "select", + "filterKey": "db_user", + "filterLabel": "用户", + }, + "schema_name" => { + "checked": false, + "filterCustomType": "select", + "filterKey": "schema_name", + "filterLabel": "schema", + }, + "counter" => { + "checked": false, + "filterCustomType": "input", + "filterKey": "counter", + "filterLabel": "出现次数 > ", + }, + "query_time_avg" => { + "checked": false, + "filterCustomType": "input", + "filterKey": "query_time_avg", + "filterLabel": "平均执行时间 > ", + }, + "row_examined_avg" => { + "checked": false, + "filterCustomType": "input", + "filterKey": "row_examined_avg", + "filterLabel": "平均扫描行数 > ", + }, + "last_receive_timestamp" => { + "checked": false, + "filterCustomType": "date-range", + "filterKey": "last_receive_timestamp", + "filterLabel": "最后匹配时间", + }, + }, + "tableFilterCustomProps": Map { + "sql" => undefined, + "rule_name" => { + "options": [ + { + "label": "MySQL", + "options": [ + { + "label": "禁止使用没有WHERE条件或者WHERE条件恒为TRUE的SQL", + "value": "all_check_where_is_invalid", + }, + { + "label": "禁止char, varchar类型字段字符长度总和超过阈值", + "value": "ddl_check_char_length", + }, + { + "label": "TIMESTAMP 类型的列必须添加默认值", + "value": "ddl_check_column_timestamp_without_default", + }, + { + "label": "除了自增列及大字段列之外,每个列都必须添加默认值", + "value": "ddl_check_column_without_default", + }, + { + "label": "新建表建议加入 IF NOT EXISTS,保证重复执行不报错", + "value": "ddl_check_table_without_if_not_exists", + }, + { + "label": "禁止使用全模糊搜索或左模糊搜索", + "value": "dml_check_fuzzy_search", + }, + { + "label": "JOIN字段必须包含索引", + "value": "dml_check_join_field_use_index", + }, + { + "label": "禁止对索引列进行数学运算和使用函数", + "value": "dml_check_math_computation_or_func_on_index", + }, + ], + }, + ], + }, + "db_user" => { + "options": [ + { + "label": "", + "value": "", + }, + ], + }, + "schema_name" => { + "options": [ + { + "label": "test123", + "value": "test123", + }, + { + "label": "", + "value": "", + }, + { + "label": "sqle", + "value": "sqle", + }, + { + "label": "test", + "value": "test", + }, + ], + }, + "counter" => undefined, + "query_time_avg" => undefined, + "row_examined_avg" => undefined, + "last_receive_timestamp" => { + "showTime": true, + }, + }, +} +`; + +exports[`useBackendTable should create table filter meta and custom props based on filter meta list 2`] = ` +{ + "extraTableFilterMeta": Map { + "sql" => { + "checked": true, + "filterCustomType": "input", + "filterKey": "sql", + "filterLabel": "SQL", + }, + "rule_name" => { + "checked": true, + "filterCustomType": "select", + "filterKey": "rule_name", + "filterLabel": "审核规则", + }, + "db_user" => { + "checked": true, + "filterCustomType": "select", + "filterKey": "db_user", + "filterLabel": "用户", + }, + "schema_name" => { + "checked": true, + "filterCustomType": "select", + "filterKey": "schema_name", + "filterLabel": "schema", + }, + "counter" => { + "checked": true, + "filterCustomType": "input", + "filterKey": "counter", + "filterLabel": "出现次数 > ", + }, + "query_time_avg" => { + "checked": true, + "filterCustomType": "input", + "filterKey": "query_time_avg", + "filterLabel": "平均执行时间 > ", + }, + "row_examined_avg" => { + "checked": true, + "filterCustomType": "input", + "filterKey": "row_examined_avg", + "filterLabel": "平均扫描行数 > ", + }, + "last_receive_timestamp" => { + "checked": true, + "filterCustomType": "date-range", + "filterKey": "last_receive_timestamp", + "filterLabel": "最后匹配时间", + }, + }, + "tableFilterCustomProps": Map { + "sql" => undefined, + "rule_name" => { + "options": [ + { + "label": "MySQL", + "options": [ + { + "label": "禁止使用没有WHERE条件或者WHERE条件恒为TRUE的SQL", + "value": "all_check_where_is_invalid", + }, + { + "label": "禁止char, varchar类型字段字符长度总和超过阈值", + "value": "ddl_check_char_length", + }, + { + "label": "TIMESTAMP 类型的列必须添加默认值", + "value": "ddl_check_column_timestamp_without_default", + }, + { + "label": "除了自增列及大字段列之外,每个列都必须添加默认值", + "value": "ddl_check_column_without_default", + }, + { + "label": "新建表建议加入 IF NOT EXISTS,保证重复执行不报错", + "value": "ddl_check_table_without_if_not_exists", + }, + { + "label": "禁止使用全模糊搜索或左模糊搜索", + "value": "dml_check_fuzzy_search", + }, + { + "label": "JOIN字段必须包含索引", + "value": "dml_check_join_field_use_index", + }, + { + "label": "禁止对索引列进行数学运算和使用函数", + "value": "dml_check_math_computation_or_func_on_index", + }, + ], + }, + ], + }, + "db_user" => { + "options": [ + { + "label": "", + "value": "", + }, + ], + }, + "schema_name" => { + "options": [ + { + "label": "test123", + "value": "test123", + }, + { + "label": "", + "value": "", + }, + { + "label": "sqle", + "value": "sqle", + }, + { + "label": "test", + "value": "test", + }, + ], + }, + "counter" => undefined, + "query_time_avg" => undefined, + "row_examined_avg" => undefined, + "last_receive_timestamp" => { + "showTime": true, + }, + }, +} +`; + +exports[`useBackendTable should generate sortable table columns with custom class names and render options 1`] = ` +[ + { + "className": "test-cls", + "dataIndex": "fingerprint", + "render": [Function], + "sorter": false, + "title": "SQL指纹", + }, + { + "className": "test-cls", + "dataIndex": "sql", + "render": [Function], + "sorter": false, + "title": "SQL", + }, + { + "className": undefined, + "dataIndex": "audit_results", + "render": [Function], + "sorter": false, + "title": "审核结果", + }, + { + "className": undefined, + "dataIndex": "counter", + "render": [Function], + "sorter": true, + "title": "出现次数", + }, + { + "className": undefined, + "dataIndex": "last_receive_timestamp", + "render": [Function], + "sorter": true, + "title": "最后匹配时间", + }, + { + "className": undefined, + "dataIndex": "query_time_avg", + "render": [Function], + "sorter": true, + "title": "平均执行时间", + }, + { + "className": undefined, + "dataIndex": "query_time_max", + "render": [Function], + "sorter": true, + "title": "最长执行时间", + }, + { + "className": undefined, + "dataIndex": "row_examined_avg", + "render": [Function], + "sorter": true, + "title": "平均扫描行数", + }, + { + "className": undefined, + "dataIndex": "db_user", + "render": [Function], + "sorter": false, + "title": "用户", + }, + { + "className": undefined, + "dataIndex": "schema_name", + "render": [Function], + "sorter": false, + "title": "Schema", + }, +] +`; + +exports[`useBackendTable should generate sortable table columns with custom class names and render options 2`] = ` +[ + { + "className": undefined, + "dataIndex": "fingerprint", + "render": [Function], + "sorter": false, + "title": "SQL指纹", + }, + { + "className": undefined, + "dataIndex": "sql", + "render": [Function], + "sorter": false, + "title": "SQL", + }, + { + "className": undefined, + "dataIndex": "audit_results", + "render": [Function], + "sorter": false, + "title": "审核结果", + }, + { + "className": undefined, + "dataIndex": "counter", + "render": [Function], + "sorter": true, + "title": "出现次数", + }, + { + "className": undefined, + "dataIndex": "last_receive_timestamp", + "render": [Function], + "sorter": true, + "title": "最后匹配时间", + }, + { + "className": undefined, + "dataIndex": "query_time_avg", + "render": [Function], + "sorter": true, + "title": "平均执行时间", + }, + { + "className": undefined, + "dataIndex": "query_time_max", + "render": [Function], + "sorter": true, + "title": "最长执行时间", + }, + { + "className": undefined, + "dataIndex": "row_examined_avg", + "render": [Function], + "sorter": true, + "title": "平均扫描行数", + }, + { + "className": undefined, + "dataIndex": "db_user", + "render": [Function], + "sorter": false, + "title": "用户", + }, + { + "className": undefined, + "dataIndex": "schema_name", + "render": [Function], + "sorter": false, + "title": "Schema", + }, +] +`; + exports[`useBackendTable should render table by "head" fields 1`] = `
{ const testData = [ @@ -71,6 +76,61 @@ describe('useBackendTable', () => { } ]; + const mockHeadWithSort = [ + { + field_name: 'fingerprint', + desc: 'SQL指纹', + type: 'sql', + sortable: false + }, + { + field_name: 'sql', + desc: 'SQL', + type: 'sql', + sortable: false + }, + { + field_name: 'audit_results', + desc: '审核结果', + sortable: false + }, + { + field_name: 'counter', + desc: '出现次数', + sortable: true + }, + { + field_name: 'last_receive_timestamp', + desc: '最后匹配时间', + sortable: true + }, + { + field_name: 'query_time_avg', + desc: '平均执行时间', + sortable: true + }, + { + field_name: 'query_time_max', + desc: '最长执行时间', + sortable: true + }, + { + field_name: 'row_examined_avg', + desc: '平均扫描行数', + sortable: true + }, + { + field_name: 'db_user', + desc: '用户', + sortable: false + }, + { + field_name: 'schema_name', + desc: 'Schema', + sortable: false + } + ]; + it('should render table by "head" fields', () => { const { result } = renderHook(() => useBackendTable()); @@ -84,4 +144,303 @@ describe('useBackendTable', () => { expect(container).toMatchSnapshot(); }); }); + + it('should generate sortable table columns with custom class names and render options', () => { + const { result } = renderHook(() => useBackendTable()); + + expect( + result.current.sortableTableColumnFactory(mockHeadWithSort, { + columnClassName: (type?: string) => + type === 'sql' ? 'test-cls' : undefined, + customRender: (text, _, fieldName, type) => { + if (fieldName === 'audit_results') { + return 'audit_results'; + } + + if (!text) { + return '-'; + } + + if (fieldName === 'last_receive_timestamp') { + return formatTime(text, '-'); + } + + if (type === 'sql') { + return 'sql-cell'; + } + + return text; + } + }) + ).toMatchSnapshot(); + + expect( + result.current.sortableTableColumnFactory(mockHeadWithSort) + ).toMatchSnapshot(); + }); + + it('should create table filter meta and custom props based on filter meta list', () => { + const { result } = renderHook(() => useBackendTable()); + + expect( + result.current.tableFilterMetaFactory([ + { + filter_name: 'sql', + desc: 'SQL', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'rule_name', + desc: '审核规则', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'all_check_where_is_invalid', + desc: '禁止使用没有WHERE条件或者WHERE条件恒为TRUE的SQL', + group: 'MySQL' + }, + { + value: 'ddl_check_char_length', + desc: '禁止char, varchar类型字段字符长度总和超过阈值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_timestamp_without_default', + desc: 'TIMESTAMP 类型的列必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_without_default', + desc: '除了自增列及大字段列之外,每个列都必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_table_without_if_not_exists', + desc: '新建表建议加入 IF NOT EXISTS,保证重复执行不报错', + group: 'MySQL' + }, + { + value: 'dml_check_fuzzy_search', + desc: '禁止使用全模糊搜索或左模糊搜索', + group: 'MySQL' + }, + { + value: 'dml_check_join_field_use_index', + desc: 'JOIN字段必须包含索引', + group: 'MySQL' + }, + { + value: 'dml_check_math_computation_or_func_on_index', + desc: '禁止对索引列进行数学运算和使用函数', + group: 'MySQL' + } + ] + }, + { + filter_name: 'db_user', + desc: '用户', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: '', + desc: '', + group: '' + } + ] + }, + { + filter_name: 'schema_name', + desc: 'schema', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'test123', + desc: 'test123', + group: '' + }, + { + value: '', + desc: '', + group: '' + }, + { + value: 'sqle', + desc: 'sqle', + group: '' + }, + { + value: 'test', + desc: 'test', + group: '' + } + ] + }, + { + filter_name: 'counter', + desc: '出现次数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'query_time_avg', + desc: '平均执行时间 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'row_examined_avg', + desc: '平均扫描行数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'last_receive_timestamp', + desc: '最后匹配时间', + filter_input_type: FilterMetaFilterInputTypeEnum.date_time, + filter_op_type: FilterMetaFilterOpTypeEnum.between, + filter_tip_list: [] + } + ]) + ).toMatchSnapshot(); + + expect( + result.current.tableFilterMetaFactory( + [ + { + filter_name: 'sql', + desc: 'SQL', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'rule_name', + desc: '审核规则', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'all_check_where_is_invalid', + desc: '禁止使用没有WHERE条件或者WHERE条件恒为TRUE的SQL', + group: 'MySQL' + }, + { + value: 'ddl_check_char_length', + desc: '禁止char, varchar类型字段字符长度总和超过阈值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_timestamp_without_default', + desc: 'TIMESTAMP 类型的列必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_without_default', + desc: '除了自增列及大字段列之外,每个列都必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_table_without_if_not_exists', + desc: '新建表建议加入 IF NOT EXISTS,保证重复执行不报错', + group: 'MySQL' + }, + { + value: 'dml_check_fuzzy_search', + desc: '禁止使用全模糊搜索或左模糊搜索', + group: 'MySQL' + }, + { + value: 'dml_check_join_field_use_index', + desc: 'JOIN字段必须包含索引', + group: 'MySQL' + }, + { + value: 'dml_check_math_computation_or_func_on_index', + desc: '禁止对索引列进行数学运算和使用函数', + group: 'MySQL' + } + ] + }, + { + filter_name: 'db_user', + desc: '用户', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: '', + desc: '', + group: '' + } + ] + }, + { + filter_name: 'schema_name', + desc: 'schema', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'test123', + desc: 'test123', + group: '' + }, + { + value: '', + desc: '', + group: '' + }, + { + value: 'sqle', + desc: 'sqle', + group: '' + }, + { + value: 'test', + desc: 'test', + group: '' + } + ] + }, + { + filter_name: 'counter', + desc: '出现次数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'query_time_avg', + desc: '平均执行时间 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'row_examined_avg', + desc: '平均扫描行数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'last_receive_timestamp', + desc: '最后匹配时间', + filter_input_type: FilterMetaFilterInputTypeEnum.date_time, + filter_op_type: FilterMetaFilterOpTypeEnum.between, + filter_tip_list: [] + } + ], + true + ) + ).toMatchSnapshot(); + }); }); diff --git a/packages/sqle/src/locale/zh-CN/managementConf.ts b/packages/sqle/src/locale/zh-CN/managementConf.ts index 946a5b9ce..220237b59 100644 --- a/packages/sqle/src/locale/zh-CN/managementConf.ts +++ b/packages/sqle/src/locale/zh-CN/managementConf.ts @@ -119,7 +119,7 @@ export default { disabledConfirmTips: '停用后,将不再扫描该类型的SQL,当前数据会被保留,是否确认停用?', delete: '删除', - deleteSuccessTips: '删除成功', + deleteSuccessTips: '删除成功!', deleteConfirmTips: '删除后该类型智能扫描数据将不再被保留,是否确认删除?' } diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/Overview/__tests__/index.test.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/__tests__/index.test.tsx new file mode 100644 index 000000000..1a00a7891 --- /dev/null +++ b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/__tests__/index.test.tsx @@ -0,0 +1,156 @@ +import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; +import ConfDetailOverview from '..'; +import { superRender } from '../../../../../testUtils/customRender'; +import { SQL_MANAGEMENT_CONF_OVERVIEW_TAB_KEY } from '../../index.data'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import instanceAuditPlan from '../../../../../testUtils/mockApi/instanceAuditPlan'; +import { act, fireEvent } from '@testing-library/react'; +import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data'; +import { mockInstanceAuditPlanInfo } from '../../../../../testUtils/mockApi/instanceAuditPlan/data'; +import { + InstanceAuditPlanInfoActiveStatusEnum, + UpdateAuditPlanStatusReqV1ActiveEnum +} from '@actiontech/shared/lib/api/sqle/service/common.enum'; + +describe('test Overview', () => { + const handleChangeTabSpy = jest.fn(); + const instanceAuditPlanId = '1'; + const refreshAuditPlanDetailSpy = jest.fn(); + const customRender = () => { + return superRender( + + ); + }; + let getInstanceAuditPlanOverviewSpy: jest.SpyInstance; + + beforeEach(() => { + jest.useFakeTimers(); + mockUseCurrentProject(); + mockUseCurrentUser(); + getInstanceAuditPlanOverviewSpy = + instanceAuditPlan.getInstanceAuditPlanOverview(); + }); + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + it('should request data for the table when the component is active', async () => { + customRender(); + + await act(async () => jest.advanceTimersByTime(3000)); + + expect(getInstanceAuditPlanOverviewSpy).toHaveBeenCalledTimes(1); + expect(getInstanceAuditPlanOverviewSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId + }); + }); + + it('should enable, disable, and delete actions with correct API calls and refresh the table', async () => { + const updateAuditPlanStatusSpy = instanceAuditPlan.updateAuditPlanStatus(); + const deleteAuditPlanByTypeSpy = instanceAuditPlan.deleteAuditPlanByType(); + const { getAllByText, findByText, getByText } = customRender(); + + await act(async () => jest.advanceTimersByTime(3000)); + + expect(getAllByText('停 用').length).toBe( + mockInstanceAuditPlanInfo.filter( + (v) => v.active_status === InstanceAuditPlanInfoActiveStatusEnum.normal + ).length + ); + expect(getAllByText('启 用').length).toBe( + mockInstanceAuditPlanInfo.filter( + (v) => + v.active_status === InstanceAuditPlanInfoActiveStatusEnum.disabled + ).length + ); + expect(getAllByText('删 除').length).toBe(mockInstanceAuditPlanInfo.length); + + // disabled + fireEvent.click(getAllByText('停 用')[0]); + await findByText( + '停用后,将不再扫描该类型的SQL,当前数据会被保留,是否确认停用?' + ); + fireEvent.click(getByText('确 认')); + + expect(updateAuditPlanStatusSpy).toHaveBeenCalledTimes(1); + expect(updateAuditPlanStatusSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: mockInstanceAuditPlanInfo + .find( + (v) => + v.active_status === InstanceAuditPlanInfoActiveStatusEnum.normal + ) + ?.audit_plan_type?.audit_plan_id?.toString(), + active: UpdateAuditPlanStatusReqV1ActiveEnum.disabled + }); + expect(getAllByText('停 用')[0].closest('button')).toBeDisabled(); + + await act(async () => jest.advanceTimersByTime(3000)); + expect(getAllByText('停 用')[0].closest('button')).not.toBeDisabled(); + expect(getByText('停用成功!')).toBeInTheDocument(); + expect(getInstanceAuditPlanOverviewSpy).toHaveBeenCalledTimes(2); + + // enabled + fireEvent.click(getAllByText('启 用')[0]); + expect(updateAuditPlanStatusSpy).toHaveBeenCalledTimes(2); + expect(updateAuditPlanStatusSpy).toHaveBeenNthCalledWith(2, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: mockInstanceAuditPlanInfo + .find( + (v) => + v.active_status === InstanceAuditPlanInfoActiveStatusEnum.disabled + ) + ?.audit_plan_type?.audit_plan_id?.toString(), + active: UpdateAuditPlanStatusReqV1ActiveEnum.normal + }); + + expect(getAllByText('启 用')[0].closest('button')).toBeDisabled(); + await act(async () => jest.advanceTimersByTime(3000)); + expect(getAllByText('启 用')[0].closest('button')).not.toBeDisabled(); + expect(getByText('启用成功!')).toBeInTheDocument(); + expect(getInstanceAuditPlanOverviewSpy).toHaveBeenCalledTimes(3); + + //delete + fireEvent.click(getAllByText('删 除')[0]); + await findByText('删除后该类型智能扫描数据将不再被保留,是否确认删除?'); + fireEvent.click(getAllByText('确 认')[1]); + expect(deleteAuditPlanByTypeSpy).toHaveBeenCalledTimes(1); + expect(deleteAuditPlanByTypeSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: + mockInstanceAuditPlanInfo[0].audit_plan_type?.audit_plan_id?.toString() + }); + + expect(getAllByText('删 除')[0].closest('button')).toBeDisabled(); + await act(async () => jest.advanceTimersByTime(3000)); + expect(getAllByText('删 除')[0].closest('button')).not.toBeDisabled(); + expect(getByText('删除成功!')).toBeInTheDocument(); + expect(getInstanceAuditPlanOverviewSpy).toHaveBeenCalledTimes(4); + expect(refreshAuditPlanDetailSpy).toHaveBeenCalledTimes(1); + }); + + it('should handle row click events to change the active tab', async () => { + const { getByText } = customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click( + getByText(mockInstanceAuditPlanInfo[0].audit_plan_type?.desc!) + ); + + expect(handleChangeTabSpy).toHaveBeenCalledTimes(1); + expect(handleChangeTabSpy).toHaveBeenNthCalledWith( + 1, + mockInstanceAuditPlanInfo[0].audit_plan_type?.audit_plan_id?.toString() + ); + }); +}); diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/Overview/column.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/column.tsx index f573a4776..3d55292b1 100644 --- a/packages/sqle/src/page/SqlManagementConf/Detail/Overview/column.tsx +++ b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/column.tsx @@ -153,6 +153,11 @@ export const ConfDetailOverviewColumnActions: (params: { permissions: (record) => record?.active_status === InstanceAuditPlanInfoActiveStatusEnum.normal, + buttonProps: () => { + return { + disabled: disabledActionPending + }; + }, confirm: (record) => { return { disabled: disabledActionPending, @@ -172,6 +177,7 @@ export const ConfDetailOverviewColumnActions: (params: { text: t('managementConf.detail.overview.actions.delete'), buttonProps: () => { return { + disabled: deleteActionPending, danger: true }; }, diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/Overview/index.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/index.tsx index ce9809503..92b58496b 100644 --- a/packages/sqle/src/page/SqlManagementConf/Detail/Overview/index.tsx +++ b/packages/sqle/src/page/SqlManagementConf/Detail/Overview/index.tsx @@ -85,6 +85,7 @@ const ConfDetailOverview: React.FC = ({ {messageContextHolder} record.audit_plan_type?.audit_plan_id!} className="table-row-cursor" dataSource={data?.list} errorMessage={requestErrorMessage} diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..5bd92c3f2 --- /dev/null +++ b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,4386 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test ScanTypeSqlCollection should match snapshot 1`] = ` +
+
+
+
+
+
+ + + + + + +
+
+
+
+
+
+ + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`test ScanTypeSqlCollection should match snapshot 2`] = ` +
+
+
+
+ + + SQL + + + +
+
+
+
+ + + + + + + 审核规则 + + + 请选择{{name}} + + + +
+
+
+
+
+
+ + + + + + + 用户 + + + 请选择{{name}} + + + +
+
+
+
+
+
+ + + + + + + schema + + + 请选择{{name}} + + + +
+
+
+
+ + + 出现次数 > + + + +
+
+ + + 平均执行时间 > + + + +
+
+ + + 平均扫描行数 > + + + +
+
+
+
+
+ +
+
+ + + +
+
+ +
+
+ + + 最后匹配时间 + + +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SQL指纹 + + SQL + + 审核结果 + +
+ + 出现次数 + + + + +
+
+
+ + 最后匹配时间 + + + + +
+
+
+ + 平均执行时间 + + + + +
+
+
+ + 最长执行时间 + + + + +
+
+
+ + 平均扫描行数 + + + + +
+
+ 用户 + + Schema +
+
+
+ + + SELECT + + ?,SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + select + + + + 1 + + ,SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 598 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + test123 +
+
+
+ + + SELECT + + ?,?,?,?,?,SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + select + + + + 1 + + , + + 1 + + , + + 1 + + , + + 1 + + , + + 1 + + ,SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + - +
+
+
+ + + SELECT + + SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + SELECT + + SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + - +
+
+
+ + + Prepare + + +
+
+ + + +
+
+
+
+
+ + + Prepare + + +
+
+ + + +
+
+
+
+
+
+ + + + + + + 语法错误或者解析器不支持,请人工确认SQL正确性 + +
+
+
+
+ 2388 + + 2024-08-06 13:31:20 + + 0 + + 0 + + 0 + + - + + sqle +
+
+
+ + + COMMIT + + +
+
+ + + +
+
+
+
+
+ + + COMMIT + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 8559 + + 2024-08-06 13:31:21 + + 0 + + 0 + + 0 + + - + + sqle +
+
+
+ + + INSERT + + + + INTO + + \`audit_plan_sqls_v2\` (\`audit_plan_id\`,\`fingerprint_md5\`,\`fingerprint\`,\`sql_content\`,\`info\`,\`schema\`) + + VALUES + + (?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?) + +
+
+ + + +
+
+
+
+
+ + + INSERT + + + + INTO + + \`audit_plan_sqls_v2\` (\`audit_plan_id\`,\`fingerprint_md5\`, \`fingerprint\`, \`sql_content\`, \`info\`, \`schema\`) + + VALUES + + ( + + 3 + + , + + 'a62860c563df5399c2acb800673ae715' + + , + + 'CREATE TABLE \`audit_files\` (\\n \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,\\n \`created_at\` datetime DEFAULT CURRENT_TIMESTAMP,\\n \`updated_at\` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\\n \`deleted_at\` datetime DEFAULT NULL,\\n \`task_id\` int(10) unsigned DEFAULT NULL,\\n \`unique_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n \`file_host\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n \`file_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n PRIMARY KEY (\`id\`) + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 1 + + 1 + + 0 + + - + + sqle +
+
+
+
+
    +
  • + + 共 6 条数据 + +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+`; + +exports[`test ScanTypeSqlCollection should open report drawer and set current audit result record on audit result click 1`] = ` + +
+
+
+
+ + + SQL + + + +
+
+
+
+ + + + + + + 审核规则 + + + 请选择{{name}} + + + +
+
+
+
+
+
+ + + + + + + 用户 + + + 请选择{{name}} + + + +
+
+
+
+
+
+ + + + + + + schema + + + 请选择{{name}} + + + +
+
+
+
+ + + 出现次数 > + + + +
+
+ + + 平均执行时间 > + + + +
+
+ + + 平均扫描行数 > + + + +
+
+
+
+
+ +
+
+ + + +
+
+ +
+
+ + + 最后匹配时间 + + +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ SQL指纹 + + SQL + + 审核结果 + +
+ + 出现次数 + + + + +
+
+
+ + 最后匹配时间 + + + + +
+
+
+ + 平均执行时间 + + + + +
+
+
+ + 最长执行时间 + + + + +
+
+
+ + 平均扫描行数 + + + + +
+
+ 用户 + + Schema +
+
+
+ + + SELECT + + ?,SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + select + + + + 1 + + ,SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 598 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + test123 +
+
+
+ + + SELECT + + ?,?,?,?,?,SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + select + + + + 1 + + , + + 1 + + , + + 1 + + , + + 1 + + , + + 1 + + ,SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + - +
+
+
+ + + SELECT + + SLEEP(?) LIMIT ?,? + +
+
+ + + +
+
+
+
+
+ + + /* ApplicationName=DBeaver 24.1.2 - SQLEditor <Console> */ + + + + SELECT + + SLEEP( + + 11 + + ) +LIMIT + + 0 + + , + + 200 + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 11 + + 11 + + 0 + + - + + - +
+
+
+ + + Prepare + + +
+
+ + + +
+
+
+
+
+ + + Prepare + + +
+
+ + + +
+
+
+
+
+
+ + + + + + + 语法错误或者解析器不支持,请人工确认SQL正确性 + +
+
+
+
+ 2388 + + 2024-08-06 13:31:20 + + 0 + + 0 + + 0 + + - + + sqle +
+
+
+ + + COMMIT + + +
+
+ + + +
+
+
+
+
+ + + COMMIT + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 8559 + + 2024-08-06 13:31:21 + + 0 + + 0 + + 0 + + - + + sqle +
+
+
+ + + INSERT + + + + INTO + + \`audit_plan_sqls_v2\` (\`audit_plan_id\`,\`fingerprint_md5\`,\`fingerprint\`,\`sql_content\`,\`info\`,\`schema\`) + + VALUES + + (?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?) + +
+
+ + + +
+
+
+
+
+ + + INSERT + + + + INTO + + \`audit_plan_sqls_v2\` (\`audit_plan_id\`,\`fingerprint_md5\`, \`fingerprint\`, \`sql_content\`, \`info\`, \`schema\`) + + VALUES + + ( + + 3 + + , + + 'a62860c563df5399c2acb800673ae715' + + , + + 'CREATE TABLE \`audit_files\` (\\n \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,\\n \`created_at\` datetime DEFAULT CURRENT_TIMESTAMP,\\n \`updated_at\` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\\n \`deleted_at\` datetime DEFAULT NULL,\\n \`task_id\` int(10) unsigned DEFAULT NULL,\\n \`unique_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n \`file_host\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n \`file_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n PRIMARY KEY (\`id\`) + + +
+
+ + + +
+
+
+
+
+ + + + + + + 审核通过 + +
+
+
+ 299 + + 2024-08-06 13:29:54 + + 1 + + 1 + + 0 + + - + + sqle +
+
+
+
+
    +
  • + + 共 6 条数据 + +
  • +
  • + +
  • +
  • + + 1 + +
  • +
  • + +
  • +
  • + +
  • +
+
+
+
+
+
+
+
+
+ + +`; diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/index.test.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/index.test.tsx new file mode 100644 index 000000000..58a8aeeaa --- /dev/null +++ b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/__tests__/index.test.tsx @@ -0,0 +1,132 @@ +import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; +import instanceAuditPlan from '../../../../../testUtils/mockApi/instanceAuditPlan'; +import { superRender } from '../../../../../testUtils/customRender'; +import ScanTypeSqlCollection from '../indx'; +import { act, fireEvent } from '@testing-library/react'; +import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import { getAllBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; +import rule_template from '../../../../../testUtils/mockApi/rule_template'; + +describe('test ScanTypeSqlCollection', () => { + let getInstanceAuditPlanSQLMetaSpy: jest.SpyInstance; + let getInstanceAuditPlanSQLDataSpy: jest.SpyInstance; + beforeEach(() => { + jest.useFakeTimers(); + mockUseCurrentProject(); + mockUseCurrentUser(); + instanceAuditPlan.mockAllApi(); + getInstanceAuditPlanSQLMetaSpy = + instanceAuditPlan.getInstanceAuditPlanSQLMeta(); + getInstanceAuditPlanSQLDataSpy = + instanceAuditPlan.getInstanceAuditPlanSQLData(); + }); + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + + const instanceAuditPlanId = '1'; + const auditPlanId = '121343'; + const instanceType = 'MySQL'; + + const customRender = () => { + return superRender( + + ); + }; + it('should match snapshot', async () => { + const { container } = customRender(); + + expect(getInstanceAuditPlanSQLMetaSpy).toHaveBeenCalledTimes(1); + expect(getInstanceAuditPlanSQLMetaSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: auditPlanId + }); + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenCalledTimes(1); + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: auditPlanId, + page_index: 1, + page_size: 20, + filter_list: [] + }); + expect(container).toMatchSnapshot(); + await act(async () => jest.advanceTimersByTime(3000)); + expect(container).toMatchSnapshot(); + }); + + it('should render the table filter container with dynamic filter meta', async () => { + const { getByText } = customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect( + getByText('SQL', { + selector: '.ant-input-prefix' + }) + ).toBeInTheDocument(); + + fireEvent.change(getAllBySelector('.ant-input')[0], { + target: { value: 'select *' } + }); + fireEvent.keyDown(getAllBySelector('.ant-input')[0], { + key: 'Enter', + code: 'Enter', + keyCode: 13 + }); + + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenCalledTimes(2); + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenNthCalledWith(2, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: auditPlanId, + page_index: 1, + page_size: 20, + filter_list: [ + { + filter_compare_value: 'select *', + filter_name: 'sql' + } + ] + }); + + fireEvent.change(getAllBySelector('.ant-input')[0], { + target: { value: '' } + }); + fireEvent.keyDown(getAllBySelector('.ant-input')[0], { + key: 'Enter', + code: 'Enter', + keyCode: 13 + }); + + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenCalledTimes(3); + expect(getInstanceAuditPlanSQLDataSpy).toHaveBeenNthCalledWith(3, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: auditPlanId, + page_index: 1, + page_size: 20, + filter_list: [] + }); + }); + + it('should open report drawer and set current audit result record on audit result click', async () => { + rule_template.getRuleList(); + const { getAllByTestId, baseElement } = customRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click(getAllByTestId('trigger-open-report-drawer')[0]); + await act(async () => jest.advanceTimersByTime(3000)); + expect(baseElement).toMatchSnapshot(); + }); +}); diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/indx.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/indx.tsx index 4bedb158c..4ecc17c6a 100644 --- a/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/indx.tsx +++ b/packages/sqle/src/page/SqlManagementConf/Detail/ScanTypeSqlCollection/indx.tsx @@ -176,7 +176,6 @@ const ScanTypeSqlCollection: React.FC = ({ total: res.data.total_nums ?? 0 })); }, - { refreshDeps: [pagination, tableFilterInfo, sortInfo], ready: activeTabKey === auditPlanId @@ -265,6 +264,7 @@ const ScanTypeSqlCollection: React.FC = ({ {messageContextHolder} record.sql} errorMessage={getTableRowError && getErrorMessage(getTableRowError)} loading={getFilterMetaListLoading || getTableRowLoading} columns={sortableTableColumnFactory(tableMetas?.head ?? [], { @@ -279,7 +279,10 @@ const ScanTypeSqlCollection: React.FC = ({ results = []; } return ( -
onClickAuditResult(record)}> +
onClickAuditResult(record)} + > {results?.length > 1 ? ( { diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/__snapshots__/index.test.tsx.snap new file mode 100644 index 000000000..c57ccfe08 --- /dev/null +++ b/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/__snapshots__/index.test.tsx.snap @@ -0,0 +1,1213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test SqlManagementConf/Detail/index.tsx should display error message when data request fails 1`] = ` +
+
+
+ 静态扫描 智能扫描详情 +
+ +
+
+ + + +
+
+ 请求错误 +
+
+ error +
+
+
+`; + +exports[`test SqlManagementConf/Detail/index.tsx should match snapshot 1`] = ` +
+
+
+ 静态扫描 智能扫描详情 +
+ +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 智能扫描类型 + + 审核规则模板 + + 任务状态 + + 连接信息 + + 采集到的SQL数 + + 审核有问题的SQL数 + + 最近一次采集时间 + + 操作 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; + +exports[`test SqlManagementConf/Detail/index.tsx should match snapshot 2`] = ` +
+
+
+ mysql-1 智能扫描详情 +
+ +
+
+
+
+ + + +
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 智能扫描类型 + + 审核规则模板 + + 任务状态 + + 连接信息 + + 采集到的SQL数 + + 审核有问题的SQL数 + + 最近一次采集时间 + + 操作 +
+ 库表元数据 + + + default_MySQL + + +
+ + + + + 启用 + +
+
+ - + + 0 + + 0 + + 2024-08-06 11:30:43 + +
+
+
+
+ +
+
+ +
+
+
+
+ 慢日志 + + + mysql_rule_template + + +
+ + + + + + 停用 + +
+
+ +
+
+ + + + + +
+
+ +
+ + + +
+
+
+
+ 0 + + 0 + + - + +
+
+
+ +
+
+
+ +
+
+
+
+ processlist 列表 + + + default_MySQL + + +
+ + + + + 启用 + +
+
+ - + + 0 + + 0 + + - + +
+
+
+
+ +
+
+ +
+
+
+
+ 自定义 + + + default_MySQL + + +
+ + + + + 启用 + +
+
+ - + + 0 + + 0 + + - + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`; diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/index.test.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/index.test.tsx new file mode 100644 index 000000000..b86ba5243 --- /dev/null +++ b/packages/sqle/src/page/SqlManagementConf/Detail/__tests__/index.test.tsx @@ -0,0 +1,167 @@ +import { + useLocation, + useNavigate, + useParams, + useSearchParams +} from 'react-router-dom'; +import instanceAuditPlan from '../../../../testUtils/mockApi/instanceAuditPlan'; +import { superRender } from '../../../../testUtils/customRender'; +import ConfDetail from '..'; +import { act, fireEvent } from '@testing-library/react'; +import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject'; +import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser'; +import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data'; +import { mockAuditPlanDetailData } from '../../../../testUtils/mockApi/instanceAuditPlan/data'; +import { createSpyErrorResponse } from '@actiontech/shared/lib/testUtil/mockApi'; +import { getBySelector } from '@actiontech/shared/lib/testUtil/customQuery'; +import eventEmitter from '../../../../utils/EventEmitter'; +import EmitterKey from '../../../../data/EmitterKey'; + +jest.mock('react-router-dom', () => { + return { + ...jest.requireActual('react-router-dom'), + useSearchParams: jest.fn(), + useLocation: jest.fn(), + useNavigate: jest.fn(), + useParams: jest.fn() + }; +}); + +describe('test SqlManagementConf/Detail/index.tsx', () => { + const mockUseSearchParams = useSearchParams as jest.Mock; + const mockUseLocation = useLocation as jest.Mock; + const mockUseNavigate = useNavigate as jest.Mock; + const mockUseParams = useParams as jest.Mock; + const navigateSpy = jest.fn(); + const instanceAuditPlanId = '1'; + const pathname = '/sql-management-conf/1'; + let getInstanceAuditPlanDetailSpy: jest.SpyInstance; + beforeEach(() => { + jest.useFakeTimers(); + mockUseCurrentProject(); + mockUseCurrentUser(); + instanceAuditPlan.mockAllApi(); + getInstanceAuditPlanDetailSpy = + instanceAuditPlan.getInstanceAuditPlanDetail(); + mockUseLocation.mockReturnValue({ + pathname + }); + mockUseParams.mockReturnValue({ id: instanceAuditPlanId }); + mockUseNavigate.mockReturnValue(navigateSpy); + mockUseSearchParams.mockReturnValue([new URLSearchParams()]); + }); + afterEach(() => { + jest.useRealTimers(); + jest.clearAllMocks(); + jest.clearAllTimers(); + }); + it('should match snapshot', async () => { + const { container } = superRender(); + expect(container).toMatchSnapshot(); + expect(getInstanceAuditPlanDetailSpy).toHaveBeenCalledTimes(1); + expect(getInstanceAuditPlanDetailSpy).toHaveBeenNthCalledWith(1, { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId + }); + await act(async () => jest.advanceTimersByTime(3000)); + expect(container).toMatchSnapshot(); + }); + + it('should render extra action buttons in the page header', async () => { + const getInstanceAuditPlanSQLExportSpy = + instanceAuditPlan.getInstanceAuditPlanSQLExport(); + const { getByText, queryByText, getAllByText } = superRender( + + ); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(queryByText('导出')).not.toBeInTheDocument(); + + fireEvent.click(getAllByText('自定义')[0]); + await act(async () => jest.advanceTimersByTime(0)); + expect(queryByText('导 出')).toBeInTheDocument(); + fireEvent.click(getByText('导 出')); + expect(getInstanceAuditPlanSQLExportSpy).toHaveBeenCalledTimes(1); + expect(getInstanceAuditPlanSQLExportSpy).toHaveBeenNthCalledWith( + 1, + { + project_name: mockProjectInfo.projectName, + instance_audit_plan_id: instanceAuditPlanId, + audit_plan_id: + mockAuditPlanDetailData.audit_plans[0].audit_plan_type.audit_plan_id.toString(), + filter_list: [] + }, + { responseType: 'blob' } + ); + expect(getByText('导 出').closest('button')).toBeDisabled(); + + await act(async () => jest.advanceTimersByTime(3000)); + expect(getByText('导 出').closest('button')).not.toBeDisabled(); + }); + + it('should display error message when data request fails', async () => { + const originOutputError = console.error; + // 过滤 react-hooks 的 console error 输出 + console.error = () => { + return; + }; + getInstanceAuditPlanDetailSpy.mockImplementation(() => + createSpyErrorResponse({}) + ); + const { container, getByText } = superRender(); + + await act(async () => jest.advanceTimersByTime(3000)); + + expect(container).toMatchSnapshot(); + expect(getByText('error')).toBeInTheDocument(); + console.error = originOutputError; + }); + + it('should render the segmented tabs with correct items and active key', async () => { + mockUseSearchParams.mockReturnValue([ + new URLSearchParams({ + active_audit_plan_id: '9' + }) + ]); + const { getAllByText, getByText } = superRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + expect(getAllByText('自定义')[0].parentNode).toHaveClass( + 'ant-segmented-item-selected' + ); + + fireEvent.click(getByText('概览')); + expect(navigateSpy).toHaveBeenCalledTimes(1); + expect(navigateSpy).toHaveBeenNthCalledWith(1, pathname, { replace: true }); + + fireEvent.click(getAllByText('自定义')[0]); + expect(navigateSpy).toHaveBeenCalledTimes(2); + expect(navigateSpy).toHaveBeenNthCalledWith( + 2, + { pathname, search: `active_audit_plan_id=9` }, + { replace: true } + ); + }); + + it('should trigger refresh event for the correct component when refresh button is clicked', async () => { + const emitSpy = jest.spyOn(eventEmitter, 'emit'); + const { getAllByText } = superRender(); + await act(async () => jest.advanceTimersByTime(3000)); + + fireEvent.click(getBySelector('.custom-icon-refresh')); + expect(emitSpy).toHaveBeenCalledTimes(1); + expect(emitSpy).toHaveBeenNthCalledWith( + 1, + EmitterKey.Refresh_Sql_Management_Conf_Overview_List + ); + + fireEvent.click(getAllByText('自定义')[0]); + + fireEvent.click(getBySelector('.custom-icon-refresh')); + expect(emitSpy).toHaveBeenCalledTimes(2); + expect(emitSpy).toHaveBeenNthCalledWith( + 2, + EmitterKey.Refresh_Sql_Management_Conf_Detail_Sql_List + ); + }); +}); diff --git a/packages/sqle/src/page/SqlManagementConf/Detail/index.tsx b/packages/sqle/src/page/SqlManagementConf/Detail/index.tsx index 0f31d7bca..35c2018dc 100644 --- a/packages/sqle/src/page/SqlManagementConf/Detail/index.tsx +++ b/packages/sqle/src/page/SqlManagementConf/Detail/index.tsx @@ -28,7 +28,6 @@ import EmitterKey from '../../../data/EmitterKey'; const ConfDetail: React.FC = () => { const { t } = useTranslation(); - const { id } = useParams<{ id: string }>(); const [searchParams] = useSearchParams(); const location = useLocation(); diff --git a/packages/sqle/src/page/SqlManagementConf/List/__tests__/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/SqlManagementConf/List/__tests__/__snapshots__/index.test.tsx.snap index d5eacd5f6..d2f9f5cfc 100644 --- a/packages/sqle/src/page/SqlManagementConf/List/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/sqle/src/page/SqlManagementConf/List/__tests__/__snapshots__/index.test.tsx.snap @@ -3,40 +3,40 @@ exports[`test sqle/SqlManagementConf/List render close task type filter field 1`] = `
-
-
-
+ + + 为数据源开启扫描任务 + + + +
+
+
+
@@ -1141,9 +1141,9 @@ exports[`test sqle/SqlManagementConf/List render close task type filter field 1`
- +
-
+
`; @@ -1151,27 +1151,27 @@ exports[`test sqle/SqlManagementConf/List render close task type filter field 1` exports[`test sqle/SqlManagementConf/List render create button when project is archived 1`] = `
-
-
+ SQL管控配置 +
+
+
+
+
-
-
- SQL管控配置 -
-
-
@@ -1630,9 +1630,9 @@ exports[`test sqle/SqlManagementConf/List render create button when project is a
- +
-
+
`; @@ -1640,40 +1640,40 @@ exports[`test sqle/SqlManagementConf/List render create button when project is a exports[`test sqle/SqlManagementConf/List render filter list data by active status 1`] = `
-
-
-
+ + + 为数据源开启扫描任务 + + + +
+
+
+
@@ -2357,9 +2357,9 @@ exports[`test sqle/SqlManagementConf/List render filter list data by active stat
- +
-
+
`; @@ -2367,40 +2367,40 @@ exports[`test sqle/SqlManagementConf/List render filter list data by active stat exports[`test sqle/SqlManagementConf/List render init snap shot 1`] = `
-
-
-
+ + + 为数据源开启扫描任务 + + + +
+
+
+
@@ -3506,9 +3506,9 @@ exports[`test sqle/SqlManagementConf/List render init snap shot 1`] = `
- +
-
+
`; diff --git a/packages/sqle/src/page/SqlManagementConf/List/index.tsx b/packages/sqle/src/page/SqlManagementConf/List/index.tsx index 4e9204bcb..f53632e52 100644 --- a/packages/sqle/src/page/SqlManagementConf/List/index.tsx +++ b/packages/sqle/src/page/SqlManagementConf/List/index.tsx @@ -150,22 +150,20 @@ const List: React.FC = () => { }, [updateInstanceList, projectName]); return ( - - - - - - {t('managementConf.list.pageAction.enableAuditPlan')} - - - - } - /> + + + + + {t('managementConf.list.pageAction.enableAuditPlan')} + + + + } + /> + { isProjectManager: isProjectManager(projectName) })} /> - - + + ); }; diff --git a/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/data.ts b/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/data.ts index d5421a300..b03486c4e 100644 --- a/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/data.ts +++ b/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/data.ts @@ -1,12 +1,19 @@ import { IInstanceAuditPlanResV1, IAuditPlanTypesV1, - IAuditPlanMetaV1 + IAuditPlanMetaV1, + IInstanceAuditPlanInfo, + IAuditPlanSQLDataResV1, + IAuditPlanSQLMetaResV1 } from '@actiontech/shared/lib/api/sqle/service/common'; import { InstanceAuditPlanResV1ActiveStatusEnum, AuditPlanTypesV1InstanceTypeEnum, - AuditPlanParamResV1TypeEnum + AuditPlanParamResV1TypeEnum, + InstanceAuditPlanInfoActiveStatusEnum, + AuditPlanSQLHeadV1TypeEnum, + FilterMetaFilterInputTypeEnum, + FilterMetaFilterOpTypeEnum } from '@actiontech/shared/lib/api/sqle/service/common.enum'; export const mockInstanceAuditPlanListData: IInstanceAuditPlanResV1[] = [ @@ -263,3 +270,345 @@ export const mockAuditPlanDetailData = { } ] }; + +export const mockInstanceAuditPlanInfo: IInstanceAuditPlanInfo[] = [ + { + id: 1, + audit_plan_type: { + audit_plan_id: 1, + type: 'mysql_schema_meta', + desc: '库表元数据' + }, + audit_plan_db_type: 'MySQL', + audit_plan_instance_name: 'mysql-1', + exec_cmd: '', + audit_plan_rule_template: { + name: 'default_MySQL', + is_global_rule_template: true + }, + total_sql_nums: 0, + unsolved_sql_nums: 0, + last_collection_time: '2024-08-06T11:30:43+08:00', + active_status: InstanceAuditPlanInfoActiveStatusEnum.normal + }, + { + id: 2, + audit_plan_type: { + audit_plan_id: 2, + type: 'mysql_slow_log', + desc: '慢日志' + }, + audit_plan_db_type: 'MySQL', + audit_plan_instance_name: 'mysql-1', + exec_cmd: + './scannerd mysql_slow_log --project=default --host=127.0.0.1 --port=10000 --audit_plan_id=2 --token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcG4iOiJjNGNhNDIzOGEwYjkyMzgyMGRjYzUwOWE2Zjc1ODQ5YiIsImV4cCI6MTc1NDM3Njk1MywiaXNzIjoiYWN0aW9udGVjaCBkbXMiLCJ1aWQiOiI3MDAyMDAifQ.nNEiVaSfy8XWapN4mv_VwXib4qJy6FVZxw0bx01XsRA', + audit_plan_rule_template: { + name: 'mysql_rule_template', + is_global_rule_template: false + }, + total_sql_nums: 0, + unsolved_sql_nums: 0, + last_collection_time: undefined, + active_status: InstanceAuditPlanInfoActiveStatusEnum.disabled + }, + { + id: 3, + audit_plan_type: { + audit_plan_id: 3, + type: 'mysql_processlist', + desc: 'processlist 列表' + }, + audit_plan_db_type: 'MySQL', + audit_plan_instance_name: 'mysql-1', + exec_cmd: '', + audit_plan_rule_template: { + name: 'default_MySQL', + is_global_rule_template: true + }, + total_sql_nums: 0, + unsolved_sql_nums: 0, + last_collection_time: undefined, + active_status: InstanceAuditPlanInfoActiveStatusEnum.normal + }, + { + id: 4, + audit_plan_type: { + audit_plan_id: 4, + type: 'default', + desc: '自定义' + }, + audit_plan_db_type: 'MySQL', + audit_plan_instance_name: 'mysql-1', + exec_cmd: '', + audit_plan_rule_template: { + name: 'default_MySQL', + is_global_rule_template: true + }, + total_sql_nums: 0, + unsolved_sql_nums: 0, + last_collection_time: undefined, + active_status: InstanceAuditPlanInfoActiveStatusEnum.normal + } +]; + +export const mockAuditPlanSQLMeta: IAuditPlanSQLMetaResV1 = { + head: [ + { + field_name: 'fingerprint', + desc: 'SQL指纹', + type: AuditPlanSQLHeadV1TypeEnum.sql, + sortable: false + }, + { + field_name: 'sql', + desc: 'SQL', + type: AuditPlanSQLHeadV1TypeEnum.sql, + sortable: false + }, + { + field_name: 'audit_results', + desc: '审核结果', + sortable: false + }, + { + field_name: 'counter', + desc: '出现次数', + sortable: true + }, + { + field_name: 'last_receive_timestamp', + desc: '最后匹配时间', + sortable: true + }, + { + field_name: 'query_time_avg', + desc: '平均执行时间', + sortable: true + }, + { + field_name: 'query_time_max', + desc: '最长执行时间', + sortable: true + }, + { + field_name: 'row_examined_avg', + desc: '平均扫描行数', + sortable: true + }, + { + field_name: 'db_user', + desc: '用户', + sortable: false + }, + { + field_name: 'schema_name', + desc: 'Schema', + sortable: false + } + ], + filter_meta_list: [ + { + filter_name: 'sql', + desc: 'SQL', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'rule_name', + desc: '审核规则', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'all_check_where_is_invalid', + desc: '禁止使用没有WHERE条件或者WHERE条件恒为TRUE的SQL', + group: 'MySQL' + }, + { + value: 'ddl_check_char_length', + desc: '禁止char, varchar类型字段字符长度总和超过阈值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_timestamp_without_default', + desc: 'TIMESTAMP 类型的列必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_column_without_default', + desc: '除了自增列及大字段列之外,每个列都必须添加默认值', + group: 'MySQL' + }, + { + value: 'ddl_check_table_without_if_not_exists', + desc: '新建表建议加入 IF NOT EXISTS,保证重复执行不报错', + group: 'MySQL' + }, + { + value: 'dml_check_fuzzy_search', + desc: '禁止使用全模糊搜索或左模糊搜索', + group: 'MySQL' + }, + { + value: 'dml_check_join_field_use_index', + desc: 'JOIN字段必须包含索引', + group: 'MySQL' + }, + { + value: 'dml_check_math_computation_or_func_on_index', + desc: '禁止对索引列进行数学运算和使用函数', + group: 'MySQL' + } + ] + }, + { + filter_name: 'db_user', + desc: '用户', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: '', + desc: '', + group: '' + } + ] + }, + { + filter_name: 'schema_name', + desc: 'schema', + filter_input_type: FilterMetaFilterInputTypeEnum.string, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [ + { + value: 'test123', + desc: 'test123', + group: '' + }, + { + value: '', + desc: '', + group: '' + }, + { + value: 'sqle', + desc: 'sqle', + group: '' + }, + { + value: 'test', + desc: 'test', + group: '' + } + ] + }, + { + filter_name: 'counter', + desc: '出现次数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'query_time_avg', + desc: '平均执行时间 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'row_examined_avg', + desc: '平均扫描行数 \u003e ', + filter_input_type: FilterMetaFilterInputTypeEnum.int, + filter_op_type: FilterMetaFilterOpTypeEnum.equal, + filter_tip_list: [] + }, + { + filter_name: 'last_receive_timestamp', + desc: '最后匹配时间', + filter_input_type: FilterMetaFilterInputTypeEnum.date_time, + filter_op_type: FilterMetaFilterOpTypeEnum.between, + filter_tip_list: [] + } + ] +}; + +export const mockAuditPlanSQLData: IAuditPlanSQLDataResV1 = { + rows: [ + { + audit_results: 'null', + counter: '598', + db_user: '', + fingerprint: 'SELECT ?,SLEEP(?) LIMIT ?,?', + last_receive_timestamp: '2024-08-06T05:29:54Z', + query_time_avg: '11', + query_time_max: '11', + row_examined_avg: '0', + schema_name: 'test123', + sql: '/* ApplicationName=DBeaver 24.1.2 - SQLEditor \u003cConsole\u003e */ select 1,SLEEP(11)\nLIMIT 0, 200' + }, + { + audit_results: 'null', + counter: '299', + db_user: '', + fingerprint: 'SELECT ?,?,?,?,?,SLEEP(?) LIMIT ?,?', + last_receive_timestamp: '2024-08-06T05:29:54Z', + query_time_avg: '11', + query_time_max: '11', + row_examined_avg: '0', + schema_name: '', + sql: '/* ApplicationName=DBeaver 24.1.2 - SQLEditor \u003cConsole\u003e */ select 1,1,1,1,1,SLEEP(11)\nLIMIT 0, 200' + }, + { + audit_results: 'null', + counter: '299', + db_user: '', + fingerprint: 'SELECT SLEEP(?) LIMIT ?,?', + last_receive_timestamp: '2024-08-06T05:29:54Z', + query_time_avg: '11', + query_time_max: '11', + row_examined_avg: '0', + schema_name: '', + sql: '/* ApplicationName=DBeaver 24.1.2 - SQLEditor \u003cConsole\u003e */ SELECT SLEEP(11) \nLIMIT 0, 200' + }, + { + audit_results: + '[{"level": "warn", "message": "语法错误或者解析器不支持,请人工确认SQL正确性", "rule_name": ""}]', + counter: '2388', + db_user: '', + fingerprint: 'Prepare', + last_receive_timestamp: '2024-08-06T05:31:20Z', + query_time_avg: '0', + query_time_max: '0', + row_examined_avg: '0', + schema_name: 'sqle', + sql: 'Prepare' + }, + { + audit_results: 'null', + counter: '8559', + db_user: '', + fingerprint: 'COMMIT', + last_receive_timestamp: '2024-08-06T05:31:21Z', + query_time_avg: '0', + query_time_max: '0', + row_examined_avg: '0', + schema_name: 'sqle', + sql: 'COMMIT' + }, + { + audit_results: 'null', + counter: '299', + db_user: '', + fingerprint: + 'INSERT INTO `audit_plan_sqls_v2` (`audit_plan_id`,`fingerprint_md5`,`fingerprint`,`sql_content`,`info`,`schema`) VALUES (?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?),(?,?,?,?,?,?)', + last_receive_timestamp: '2024-08-06T05:29:54Z', + query_time_avg: '1', + query_time_max: '1', + row_examined_avg: '0', + schema_name: 'sqle', + sql: "INSERT INTO `audit_plan_sqls_v2` (`audit_plan_id`,`fingerprint_md5`, `fingerprint`, `sql_content`, `info`, `schema`) VALUES (3, 'a62860c563df5399c2acb800673ae715', 'CREATE TABLE `audit_files` (\\n `id` int(10) unsigned NOT NULL AUTO_INCREMENT,\\n `created_at` datetime DEFAULT CURRENT_TIMESTAMP,\\n `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\\n `deleted_at` datetime DEFAULT NULL,\\n `task_id` int(10) unsigned DEFAULT NULL,\\n `unique_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n `file_host` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n `file_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,\\n PRIMARY KEY (`id`)" + } + ] +}; diff --git a/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/index.ts b/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/index.ts index 4ec547919..336bbff11 100644 --- a/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/index.ts +++ b/packages/sqle/src/testUtils/mockApi/instanceAuditPlan/index.ts @@ -7,7 +7,10 @@ import { mockInstanceAuditPlanListData, mockAuditPlanTypesData, mockAuditPlanMetaData, - mockAuditPlanDetailData + mockAuditPlanDetailData, + mockInstanceAuditPlanInfo, + mockAuditPlanSQLMeta, + mockAuditPlanSQLData } from './data'; import audit_plan from '@actiontech/shared/lib/api/sqle/service/audit_plan'; @@ -21,6 +24,12 @@ class MockInstanceAuditPlanApi implements MockSpyApy { this.getAuditPlanMeta(); this.getInstanceAuditPlanDetail(); this.updateInstanceAuditPlan(); + this.getInstanceAuditPlanOverview(); + this.getInstanceAuditPlanSQLMeta(); + this.getInstanceAuditPlanSQLData(); + this.getInstanceAuditPlanSQLExport(); + this.deleteAuditPlanByType(); + this.updateAuditPlanStatus(); } public getInstanceAuditPlans() { @@ -90,6 +99,67 @@ class MockInstanceAuditPlanApi implements MockSpyApy { spy.mockImplementation(() => createSpySuccessResponse({})); return spy; } + + public getInstanceAuditPlanOverview() { + const spy = jest.spyOn( + instance_audit_plan, + 'getInstanceAuditPlanOverviewV1' + ); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: mockInstanceAuditPlanInfo + }) + ); + return spy; + } + + public getInstanceAuditPlanSQLMeta() { + const spy = jest.spyOn( + instance_audit_plan, + 'getInstanceAuditPlanSQLMetaV1' + ); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: mockAuditPlanSQLMeta + }) + ); + return spy; + } + + public getInstanceAuditPlanSQLData() { + const spy = jest.spyOn( + instance_audit_plan, + 'getInstanceAuditPlanSQLDataV1' + ); + spy.mockImplementation(() => + createSpySuccessResponse({ + data: mockAuditPlanSQLData, + total_nums: mockAuditPlanSQLData.rows?.length + }) + ); + return spy; + } + + public getInstanceAuditPlanSQLExport() { + const spy = jest.spyOn( + instance_audit_plan, + 'getInstanceAuditPlanSQLExportV1' + ); + spy.mockImplementation(() => createSpySuccessResponse({})); + return spy; + } + + public updateAuditPlanStatus() { + const spy = jest.spyOn(instance_audit_plan, 'updateAuditPlanStatusV1'); + spy.mockImplementation(() => createSpySuccessResponse({})); + return spy; + } + + public deleteAuditPlanByType() { + const spy = jest.spyOn(instance_audit_plan, 'deleteAuditPlanByTypeV1'); + spy.mockImplementation(() => createSpySuccessResponse({})); + return spy; + } } export default new MockInstanceAuditPlanApi();