Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/base/src/hooks/useDbService/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,21 @@ describe('useDbService', () => {
await screen.findAllByText('123123');
expect(baseElementWithOptions).toMatchSnapshot();
});

it('should return correct db_type when service id exists', async () => {
const { result } = superRenderHook(() => useDbService(), {});

act(() => {
result.current.updateDbServiceList({ project_uid: projectID });
});
await act(async () => jest.advanceTimersByTime(3000));

// Test with first service id
const dbType1 = result.current.getServiceDbType('123123');
expect(dbType1).toBe('MySQL');

// Test with second service id
const dbType2 = result.current.getServiceDbType('300123');
expect(dbType2).toBe('MySQL');
});
});
13 changes: 11 additions & 2 deletions packages/base/src/hooks/useDbService/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import { useBoolean } from 'ahooks';
import { Select } from 'antd';
import { useDbServiceDriver } from '@actiontech/shared/lib/features';
Expand Down Expand Up @@ -112,14 +112,23 @@
const dbServiceIDOptions = useMemo(() => {
return generateCommonDbServiceOptions('id');
}, [generateCommonDbServiceOptions]);

const getServiceDbType = useCallback(
(id: string) => {
return dbServiceList.find((i) => i.id === id)?.db_type ?? '';

Check warning on line 118 in packages/base/src/hooks/useDbService/index.tsx

View workflow job for this annotation

GitHub Actions / Coverage annotations (🧪 jest-coverage-report-action)

🌿 Branch is not covered

Warning! Not covered branch
},
[dbServiceList]
);

return {
dbServiceList,
dbServiceOptions,
loading,
updateDbServiceList,
generateDbServiceSelectOptions,
generateDbServiceIDSelectOptions,
dbServiceIDOptions
dbServiceIDOptions,
getServiceDbType
};
};
export default useDbService;
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,33 @@ exports[`first should match snapshot when pageState is equal CREATE_TASK 1`] = `
</div>
</div>
</div>
<div
class="ant-form-item css-1jlm9cy ant-form-item-hidden"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-col-11 ant-col-push-2 ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
class="ant-input basic-input-wrapper css-1pd1cd5"
id="dbType"
placeholder="请输入{{name}}"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item has-required-style css-1jlm9cy"
>
Expand Down Expand Up @@ -612,24 +639,114 @@ exports[`first should match snapshot when pageState is equal CREATE_TASK 1`] = `
class="ant-form-item-control-input-content"
>
<div
class="custom-monaco-editor-wrapper css-1esok7i"
class="css-1sm8xw8"
>
<input
aria-required="true"
class="custom-monaco-editor"
height="400px"
id="sql"
language="sql"
options="{"automaticLayout":true,"minimap":{"enabled":false},"fontFamily":"SF Mono","fontSize":14,"fontWeight":"400","lineNumbersMinChars":2,"suggestFontSize":14,"scrollBeyondLastLine":false,"lineHeight":24,"letterSpacing":0.8,"overviewRulerBorder":false,"wordWrap":"on","wrappingStrategy":"advanced","wrappingIndent":"indent","scrollbar":{"vertical":"visible","horizontal":"visible","useShadows":false,"verticalScrollbarSize":12,"horizontalScrollbarSize":12,"alwaysConsumeMouseWheel":false},"folding":false,"lineNumbers":"on","glyphMargin":false,"lineDecorationsWidth":50,"renderValidationDecorations":"on","guides":{"indentation":true,"highlightActiveIndentation":true}}"
value="/* input your sql */"
width="100%"
/>
<div
class="custom-monaco-editor-wrapper css-1esok7i"
>
<input
aria-required="true"
class="custom-monaco-editor"
height="400px"
id="sql"
language="sql"
options="{"automaticLayout":true,"minimap":{"enabled":false},"fontFamily":"SF Mono","fontSize":14,"fontWeight":"400","lineNumbersMinChars":2,"suggestFontSize":14,"scrollBeyondLastLine":false,"lineHeight":24,"letterSpacing":0.8,"overviewRulerBorder":false,"wordWrap":"on","wrappingStrategy":"advanced","wrappingIndent":"indent","scrollbar":{"vertical":"visible","horizontal":"visible","useShadows":false,"verticalScrollbarSize":12,"horizontalScrollbarSize":12,"alwaysConsumeMouseWheel":false},"folding":false,"lineNumbers":"on","glyphMargin":false,"lineDecorationsWidth":50,"renderValidationDecorations":"on","guides":{"indentation":true,"highlightActiveIndentation":true}}"
value="/* input your sql */"
width="100%"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item css-15d6abf ant-form-item-hidden"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-form-item-label-left"
>
<label
class="ant-form-item-no-colon"
for="formatted"
title=""
/>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<button
aria-checked="false"
class="ant-switch basic-switch-wrapper css-g6dhbn"
id="formatted"
role="switch"
type="button"
>
<div
class="ant-switch-handle"
/>
<span
class="ant-switch-inner"
>
<span
class="ant-switch-inner-checked"
/>
<span
class="ant-switch-inner-unchecked"
/>
</span>
</button>
</div>
</div>
</div>
</div>
</div>
<div
class="ant-form-item css-15d6abf ant-form-item-hidden"
>
<div
class="ant-row ant-form-item-row"
>
<div
class="ant-col ant-form-item-label ant-form-item-label-left"
>
<label
class="ant-form-item-no-colon"
for="originSql"
title=""
/>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
class="ant-input basic-input-wrapper css-1pd1cd5"
id="originSql"
placeholder="请输入{{name}}"
type="text"
value=""
/>
</div>
</div>
</div>
</div>
</div>
</section>
</form>
<section
Expand Down Expand Up @@ -657,7 +774,7 @@ exports[`first should match snapshot when pageState is equal CREATE_TASK 1`] = `
style="margin-right: 12px;"
>
<button
class="ant-btn ant-btn-default basic-button-wrapper css-geipcv"
class="ant-btn ant-btn-default basic-button-wrapper css-1h90et6"
type="button"
>
<span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,22 @@ import CreateDataExport from '..';
import { CreateDataExportPageEnum } from '../../../../store/dataExport';
import { baseSuperRender } from '../../../../testUtils/superRender';
import { mockUseCreateDataExportReduxManage } from '../testUtils/mockUseCreateDataExportReduxManage';
import { cleanup } from '@testing-library/react';
import { cleanup, act, fireEvent, screen } from '@testing-library/react';
import { ModalName } from 'sqle/src/data/ModalName';
import { useDispatch, useSelector } from 'react-redux';
import { mockUseCurrentUser } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentUser';
import { mockUseCurrentProject } from '@actiontech/shared/lib/testUtil/mockHook/mockUseCurrentProject';
import { mockUseDbServiceDriver } from '@actiontech/shared/lib/testUtil/mockHook/mockUseDbServiceDriver';
import {
getBySelector,
queryBySelector
} from '@actiontech/shared/lib/testUtil/customQuery';
import dataExport from '@actiontech/shared/lib/testUtil/mockApi/base/dataExport';
import dbServices from '@actiontech/shared/lib/testUtil/mockApi/base/dbServices';
import instance from '@actiontech/shared/lib/testUtil/mockApi/sqle/instance';
import { createSpySuccessResponse } from '@actiontech/shared/lib/testUtil/mockApi';
import { mockProjectInfo } from '@actiontech/shared/lib/testUtil/mockHook/data';
import { formatterSQL } from '@actiontech/dms-kit';

jest.mock('react-redux', () => {
return {
Expand All @@ -24,6 +34,7 @@ jest.mock('react-redux', () => {
describe('first', () => {
const dispatchSpy = jest.fn();
beforeEach(() => {
jest.useFakeTimers();
mockUseCurrentProject();
mockUseCurrentUser();
mockUseDbServiceDriver();
Expand All @@ -39,6 +50,12 @@ describe('first', () => {
);
(useDispatch as jest.Mock).mockImplementation(() => dispatchSpy);
});
afterEach(() => {
jest.clearAllMocks();
jest.clearAllTimers();
jest.useRealTimers();
cleanup();
});
ignoreConsoleErrors([
UtilsConsoleErrorStringsEnum.INVALID_CSS_VALUE,
UtilsConsoleErrorStringsEnum.UNKNOWN_EVENT_HANDLER
Expand Down Expand Up @@ -90,4 +107,107 @@ describe('first', () => {

expect(clearAllStateSpy).toHaveBeenCalledTimes(1);
});

it('should use original SQL when database type does not support formatting', async () => {
const addDataExportTaskSpy = dataExport.AddDataExportTask();
const getDBServicesListSpy = dbServices.ListDBServicesTips();
instance.getInstanceSchemas().mockImplementation(() =>
createSpySuccessResponse({
data: { schema_name_list: ['test_schema'] }
})
);
const mockServiceData = {
db_type: 'TiDB',
host: '127.0.0.1',
name: 'test',
port: '3306',
id: '123123'
};
// Mock TiDB 数据库服务(不支持 SQL 格式化)
getDBServicesListSpy.mockImplementation(() =>
createSpySuccessResponse({
data: [mockServiceData]
})
);

const updatePageStateSpy = jest.fn();
mockUseCreateDataExportReduxManage({
updatePageState: updatePageStateSpy
});

const { baseElement } = baseSuperRender(<CreateDataExport />);
await act(async () => jest.advanceTimersByTime(3000));

// 填写工单名称
const workflowNameInput = getBySelector('#workflow_subject', baseElement);
fireEvent.change(workflowNameInput, {
target: { value: 'test' }
});
await act(async () => jest.advanceTimersByTime(0));

// 选择数据源
const dbServiceSelect = getBySelector('#dbService', baseElement);
fireEvent.mouseDown(dbServiceSelect);
await act(async () => jest.advanceTimersByTime(300));
const dbServiceLabel = `${mockServiceData.name} (${mockServiceData.host}:${mockServiceData.port})`;
fireEvent.click(
getBySelector(`div[title="${dbServiceLabel}"]`, baseElement)
);
await act(async () => jest.advanceTimersByTime(3000));

// 选择 Schema

const SchemaNameEle = getBySelector('#schema', baseElement);
fireEvent.mouseDown(SchemaNameEle);
await act(async () => jest.advanceTimersByTime(0));
fireEvent.click(getBySelector(`div[title="test_schema"]`));
await act(async () => jest.advanceTimersByTime(3000));

// 输入原始 SQL(包含多余空格)
const originalSql = 'SELECT * FROM users WHERE id=1';
const sqlEditor = queryBySelector('.custom-monaco-editor', baseElement);
if (sqlEditor) {
fireEvent.input(sqlEditor, {
target: { value: originalSql }
});
await act(async () => jest.advanceTimersByTime(100));
}

// 点击 SQL 美化按钮
const formatButton = screen.getByText('SQL美化');
fireEvent.click(formatButton);
await act(async () => jest.advanceTimersByTime(0));
expect(sqlEditor).toHaveAttribute('value', formatterSQL(originalSql));
// 验证显示只读提示信息
fireEvent.click(formatButton);
await act(async () => jest.advanceTimersByTime(0));
expect(sqlEditor).toHaveAttribute('value', originalSql);

fireEvent.click(formatButton);
await act(async () => jest.advanceTimersByTime(0));
// 点击审核按钮
const auditButton = screen.getByText('审 核');
fireEvent.click(auditButton);
await act(async () => jest.advanceTimersByTime(0));

await act(async () => jest.advanceTimersByTime(3000));

// 验证:提交时应该使用原始 SQL,而不是格式化后的 SQL
expect(addDataExportTaskSpy).toHaveBeenCalledTimes(1);
expect(addDataExportTaskSpy).toHaveBeenCalledWith({
project_uid: mockProjectInfo.projectID,
data_export_tasks: [
{
database_name: 'test_schema',
db_service_uid: '123123',
export_sql: originalSql // 应该使用原始 SQL,不是格式化后的
}
]
});

expect(updatePageStateSpy).toHaveBeenCalledTimes(1);
expect(updatePageStateSpy).toHaveBeenCalledWith(
CreateDataExportPageEnum.SUBMIT_WORKFLOW
);
});
});
Loading