Minor: fixed testSuite dependency on table entity object (#16147)

* Minor: fixed testSuite dependency on table entity object

* fixed failing unit
 test case
This commit is contained in:
Shailesh Parmar 2024-05-07 22:19:24 +05:30 committed by GitHub
parent b3e0470882
commit 1acf7d92d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 166 additions and 74 deletions

View File

@ -43,6 +43,7 @@ import { useFqn } from '../../../hooks/useFqn';
import {
createExecutableTestSuite,
createTestCase,
getTestSuiteByName,
} from '../../../rest/testAPI';
import {
getEntityBreadcrumbs,
@ -133,8 +134,19 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({
return response;
};
const fetchTestSuiteByFqn = async (fqn: string) => {
try {
const response = await getTestSuiteByName(fqn);
setTestSuiteData(response);
} catch (error) {
setTestSuiteData(undefined);
}
};
useEffect(() => {
setTestSuiteData(table.testSuite);
if (table.testSuite?.fullyQualifiedName) {
fetchTestSuiteByFqn(table.testSuite.fullyQualifiedName);
}
}, [table.testSuite]);
const handleFormSubmit = async (data: CreateTestCase) => {

View File

@ -36,9 +36,8 @@ import {
SORT_ORDER,
} from '../../../../enums/common.enum';
import { EntityTabs, EntityType } from '../../../../enums/entity.enum';
import { TestSummary } from '../../../../generated/entity/data/table';
import { EntityReference } from '../../../../generated/entity/type';
import { TestSuite } from '../../../../generated/tests/testCase';
import { TestSuite, TestSummary } from '../../../../generated/tests/testCase';
import { usePaging } from '../../../../hooks/paging/usePaging';
import { DataQualityPageTabs } from '../../../../pages/DataQuality/DataQualityPage.interface';
import {

View File

@ -94,9 +94,10 @@ const ColumnProfileTable = () => {
dateRangeObject,
onDateRangeChange,
table,
testCaseSummary,
} = useTableProfiler();
const testCaseCounts = useMemo(
() => table?.testSuite?.summary?.columnTestSummary ?? [],
() => testCaseSummary?.columnTestSummary ?? [],
[table]
);
const isLoading = isTestsLoading || isProfilerDataLoading;

View File

@ -53,6 +53,7 @@ export const QualityTab = () => {
isTableDeleted,
testCasePaging,
table,
testCaseSummary,
} = useTableProfiler();
const {
@ -248,9 +249,7 @@ export const QualityTab = () => {
</Row>
</Col>
<Col span={24}>
<SummaryPanel
testSummary={testSuite?.summary ?? INITIAL_TEST_SUMMARY}
/>
<SummaryPanel testSummary={testCaseSummary ?? INITIAL_TEST_SUMMARY} />
</Col>
<Col span={24}>
<Tabs items={tabs} />

View File

@ -24,13 +24,14 @@ import {
TableProfile,
TableProfilerConfig,
} from '../../../../generated/entity/data/table';
import { TestCase } from '../../../../generated/tests/testCase';
import { TestCase, TestSummary } from '../../../../generated/tests/testCase';
import { UsePagingInterface } from '../../../../hooks/paging/usePaging';
import { ListTestCaseParams } from '../../../../rest/testAPI';
export interface TableProfilerProps {
permissions: OperationPermission;
table?: Table;
testCaseSummary?: TestSummary;
}
export interface TableProfilerProviderProps extends TableProfilerProps {
@ -39,6 +40,7 @@ export interface TableProfilerProviderProps extends TableProfilerProps {
export interface TableProfilerContextInterface {
isTableDeleted?: boolean;
testCaseSummary?: TestSummary;
permissions: OperationPermission;
isTestsLoading: boolean;
isProfilerDataLoading: boolean;

View File

@ -59,6 +59,7 @@ export const TableProfilerProvider = ({
children,
permissions,
table,
testCaseSummary,
}: TableProfilerProviderProps) => {
const { t } = useTranslation();
const { fqn: datasetFQN } = useFqn();
@ -272,6 +273,7 @@ export const TableProfilerProvider = ({
dateRangeObject,
testCasePaging,
table,
testCaseSummary,
};
}, [
isTestsLoading,
@ -286,6 +288,7 @@ export const TableProfilerProvider = ({
dateRangeObject,
testCasePaging,
table,
testCaseSummary,
]);
return (

View File

@ -25,6 +25,7 @@ const SchemaTab: FunctionComponent<Props> = ({
hasTagEditAccess,
onThreadLinkSelect,
isReadOnly = false,
testCaseSummary,
}: Props) => {
const [searchText, setSearchText] = useState('');
@ -51,6 +52,7 @@ const SchemaTab: FunctionComponent<Props> = ({
isReadOnly={isReadOnly}
searchText={lowerCase(searchText)}
table={table}
testCaseSummary={testCaseSummary}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={onUpdate}
/>

View File

@ -13,6 +13,7 @@
import { ThreadType } from '../../../generated/api/feed/createThread';
import { Table } from '../../../generated/entity/data/table';
import { TestSummary } from '../../../generated/tests/testCase';
export type Props = {
table?: Table;
@ -21,4 +22,5 @@ export type Props = {
isReadOnly?: boolean;
onThreadLinkSelect: (value: string, threadType?: ThreadType) => void;
onUpdate: (columns: Table['columns']) => Promise<void>;
testCaseSummary?: TestSummary;
};

View File

@ -85,17 +85,18 @@ const SchemaTable = ({
isReadOnly = false,
onThreadLinkSelect,
table,
testCaseSummary,
}: SchemaTableProps) => {
const { theme } = useApplicationStore();
const { t } = useTranslation();
const { testCaseCounts, tableColumns, joins, tableConstraints } = useMemo(
() => ({
testCaseCounts: table?.testSuite?.summary?.columnTestSummary ?? [],
testCaseCounts: testCaseSummary?.columnTestSummary ?? [],
tableColumns: table?.columns ?? [],
joins: table?.joins?.columnJoins ?? [],
tableConstraints: table?.tableConstraints,
}),
[table]
[table, testCaseSummary]
);
const [searchedColumns, setSearchedColumns] = useState<Column[]>([]);

View File

@ -14,6 +14,7 @@
import { ReactNode } from 'react';
import { ThreadType } from '../../../generated/api/feed/createThread';
import { Column, Table } from '../../../generated/entity/data/table';
import { TestSummary } from '../../../generated/tests/testCase';
export interface SchemaTableProps {
hasDescriptionEditAccess: boolean;
@ -23,6 +24,7 @@ export interface SchemaTableProps {
onUpdate: (columns: Column[]) => Promise<void>;
onThreadLinkSelect: (value: string, threadType?: ThreadType) => void;
table?: Table;
testCaseSummary?: TestSummary;
}
export type TableCellRendered<T, K extends keyof T> = (

View File

@ -12,14 +12,12 @@
*/
import { Button } from 'antd';
import classNames from 'classnames';
import { isEmpty } from 'lodash';
import React, { Fragment } from 'react';
import { Handle, HandleProps, HandleType, Position } from 'reactflow';
import { ReactComponent as MinusIcon } from '../../../assets/svg/control-minus.svg';
import { ReactComponent as PlusIcon } from '../../../assets/svg/plus-outlined.svg';
import { EntityLineageNodeType } from '../../../enums/entity.enum';
import { Column, TestSuite } from '../../../generated/entity/data/table';
import { formTwoDigitNumber } from '../../../utils/CommonUtils';
import { Column } from '../../../generated/entity/data/table';
import { encodeLineageHandles } from '../../../utils/EntityLineageUtils';
import { getEntityName } from '../../../utils/EntityUtils';
import { getConstraintIcon } from '../../../utils/TableUtils';
@ -118,32 +116,6 @@ export const getCollapseHandle = (
);
};
export const getTestSuiteSummary = (testSuite?: TestSuite) => {
if (isEmpty(testSuite)) {
return null;
}
return (
<div className="d-flex justify-end">
<div className="profiler-item green" data-testid="test-passed">
<div className="font-medium" data-testid="test-passed-value">
{formTwoDigitNumber(testSuite?.summary?.success ?? 0)}
</div>
</div>
<div className="profiler-item amber" data-testid="test-aborted">
<div className="font-medium" data-testid="test-aborted-value">
{formTwoDigitNumber(testSuite?.summary?.aborted ?? 0)}
</div>
</div>
<div className="profiler-item red" data-testid="test-failed">
<div className="font-medium" data-testid="test-failed-value">
{formTwoDigitNumber(testSuite?.summary?.failed ?? 0)}
</div>
</div>
</div>
);
};
export const getColumnContent = (
column: Column,
isColumnTraced: boolean,

View File

@ -24,7 +24,8 @@ import { Column, Table } from '../../../../generated/entity/data/table';
import { getEntityChildrenAndLabel } from '../../../../utils/EntityLineageUtils';
import { getEntityName } from '../../../../utils/EntityUtils';
import { getEntityIcon } from '../../../../utils/TableUtils';
import { getColumnContent, getTestSuiteSummary } from '../CustomNode.utils';
import { getColumnContent } from '../CustomNode.utils';
import TestSuiteSummaryWidget from '../TestSuiteSummaryWidget/TestSuiteSummaryWidget.component';
import { EntityChildren, NodeChildrenProps } from './NodeChildren.interface';
const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
@ -163,9 +164,9 @@ const NodeChildren = ({ node, isConnectable }: NodeChildrenProps) => {
</Button>
)}
</div>
{showDataQuality &&
entityType === EntityType.TABLE &&
getTestSuiteSummary((node as Table).testSuite)}
{showDataQuality && entityType === EntityType.TABLE && (
<TestSuiteSummaryWidget testSuite={(node as Table).testSuite} />
)}
</div>
{showColumns && isExpanded && (

View File

@ -0,0 +1,76 @@
/*
* Copyright 2024 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Skeleton } from 'antd';
import { isUndefined } from 'lodash';
import React, { useEffect, useState } from 'react';
import {
EntityReference,
TestSummary,
} from '../../../../generated/tests/testCase';
import { getTestCaseExecutionSummary } from '../../../../rest/testAPI';
import { formTwoDigitNumber } from '../../../../utils/CommonUtils';
const TestSuiteSummaryWidget = ({
testSuite,
}: {
testSuite?: EntityReference;
}) => {
const [summary, setSummary] = useState<TestSummary>();
const [isLoading, setIsLoading] = useState(true);
const fetchTestSuiteSummary = async (testSuite: EntityReference) => {
setIsLoading(true);
try {
const response = await getTestCaseExecutionSummary(testSuite.id);
setSummary(response);
} catch (error) {
setSummary(undefined);
} finally {
setIsLoading(false);
}
};
useEffect(() => {
if (testSuite && isUndefined(summary)) {
fetchTestSuiteSummary(testSuite);
} else {
setIsLoading(false);
}
}, [testSuite]);
if (isLoading) {
return <Skeleton.Input active />;
}
return (
<div className="d-flex justify-end">
<div className="profiler-item green" data-testid="test-passed">
<div className="font-medium" data-testid="test-passed-value">
{formTwoDigitNumber(summary?.success ?? 0)}
</div>
</div>
<div className="profiler-item amber" data-testid="test-aborted">
<div className="font-medium" data-testid="test-aborted-value">
{formTwoDigitNumber(summary?.aborted ?? 0)}
</div>
</div>
<div className="profiler-item red" data-testid="test-failed">
<div className="font-medium" data-testid="test-failed-value">
{formTwoDigitNumber(summary?.failed ?? 0)}
</div>
</div>
</div>
);
};
export default TestSuiteSummaryWidget;

View File

@ -31,11 +31,10 @@ import {
} from '../../../../context/PermissionProvider/PermissionProvider.interface';
import { SummaryEntityType } from '../../../../enums/EntitySummary.enum';
import { ExplorePageTabs } from '../../../../enums/Explore.enum';
import { Table, TestSummary } from '../../../../generated/entity/data/table';
import {
getLatestTableProfileByFqn,
getTableDetailsByFQN,
} from '../../../../rest/tableAPI';
import { Table } from '../../../../generated/entity/data/table';
import { TestSummary } from '../../../../generated/tests/testCase';
import { getLatestTableProfileByFqn } from '../../../../rest/tableAPI';
import { getTestCaseExecutionSummary } from '../../../../rest/testAPI';
import { formTwoDigitNumber } from '../../../../utils/CommonUtils';
import {
getFormattedEntityData,
@ -88,17 +87,16 @@ function TableSummary({
const isTableDeleted = useMemo(() => tableDetails.deleted, [tableDetails]);
const fetchAllTests = async () => {
try {
const res = await getTableDetailsByFQN(
tableDetails.fullyQualifiedName ?? '',
{ fields: 'testSuite' }
);
if (tableDetails?.testSuite?.id) {
try {
const res = await getTestCaseExecutionSummary(
tableDetails.testSuite.id
);
if (res?.testSuite?.summary) {
setTestSuiteSummary(res?.testSuite?.summary);
setTestSuiteSummary(res);
} catch (error) {
// Error
}
} catch (error) {
// Error
}
};

View File

@ -14,11 +14,8 @@
import { act, render, screen } from '@testing-library/react';
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { MOCK_TABLE } from '../../../../mocks/TableData.mock';
import {
getLatestTableProfileByFqn,
getTableDetailsByFQN,
} from '../../../../rest/tableAPI';
import { getLatestTableProfileByFqn } from '../../../../rest/tableAPI';
import { getTestCaseExecutionSummary } from '../../../../rest/testAPI';
import { DRAWER_NAVIGATION_OPTIONS } from '../../../../utils/EntityUtils';
import { mockTableEntityDetails } from '../mocks/TableSummary.mock';
import TableSummary from './TableSummary.component';
@ -43,9 +40,13 @@ jest.mock('../../../../rest/tableAPI', () => ({
getLatestTableProfileByFqn: jest
.fn()
.mockImplementation(() => mockTableEntityDetails),
getTableDetailsByFQN: jest
.fn()
.mockImplementation(() => Promise.resolve(MOCK_TABLE)),
}));
jest.mock('../../../../rest/testAPI', () => ({
getTestCaseExecutionSummary: jest.fn().mockImplementation(() => ({
success: 0,
failed: 0,
aborted: 0,
})),
}));
jest.mock('../SummaryList/SummaryList.component', () =>
@ -195,16 +196,11 @@ describe('TableSummary component tests', () => {
profile: { rowCount: 30, timestamp: 38478857 },
})
);
(getTableDetailsByFQN as jest.Mock).mockImplementationOnce(() =>
(getTestCaseExecutionSummary as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({
...MOCK_TABLE,
testSuite: {
summary: {
success: 3,
failed: 1,
aborted: 1,
},
},
success: 3,
failed: 1,
aborted: 1,
})
);
await act(async () => {

View File

@ -121,4 +121,9 @@ export const mockTableEntityDetails: Table = {
href: 'http://localhost:8585/api/v1/databases/78a58be0-26a9-4ac8-b515-067db85bbb41',
},
followers: [],
testSuite: {
id: 'id',
name: 'dim.api/client',
type: 'testSuite',
},
};

View File

@ -71,6 +71,7 @@ import {
} from '../../generated/entity/data/table';
import { Suggestion } from '../../generated/entity/feed/suggestion';
import { ThreadType } from '../../generated/entity/feed/thread';
import { TestSummary } from '../../generated/tests/testCase';
import { TagLabel } from '../../generated/type/tagLabel';
import { useApplicationStore } from '../../hooks/useApplicationStore';
import { useFqn } from '../../hooks/useFqn';
@ -86,6 +87,7 @@ import {
restoreTable,
updateTablesVotes,
} from '../../rest/tableAPI';
import { getTestCaseExecutionSummary } from '../../rest/testAPI';
import {
addToRecentViewed,
getFeedCounts,
@ -130,6 +132,7 @@ const TableDetailsPageV1: React.FC = () => {
const [tablePermissions, setTablePermissions] = useState<OperationPermission>(
DEFAULT_ENTITY_PERMISSION
);
const [testCaseSummary, setTestCaseSummary] = useState<TestSummary>();
const extraDropdownContent = entityUtilClassBase.getManageExtraOptions(
EntityType.TABLE,
@ -190,6 +193,21 @@ const TableDetailsPageV1: React.FC = () => {
}
}, [tableFqn, viewUsagePermission]);
const fetchTestCaseSummary = async () => {
if (isUndefined(tableDetails?.testSuite?.id)) {
return;
}
try {
const response = await getTestCaseExecutionSummary(
tableDetails?.testSuite?.id
);
setTestCaseSummary(response);
} catch (error) {
setTestCaseSummary(undefined);
}
};
const fetchQueryCount = async () => {
if (!tableDetails?.id) {
return;
@ -549,6 +567,7 @@ const TableDetailsPageV1: React.FC = () => {
hasTagEditAccess={editTagsPermission}
isReadOnly={deleted}
table={tableDetails}
testCaseSummary={testCaseSummary}
onThreadLinkSelect={onThreadLinkSelect}
onUpdate={onColumnsUpdate}
/>
@ -690,6 +709,7 @@ const TableDetailsPageV1: React.FC = () => {
<TableProfiler
permissions={tablePermissions}
table={tableDetails}
testCaseSummary={testCaseSummary}
/>
),
},
@ -987,6 +1007,7 @@ const TableDetailsPageV1: React.FC = () => {
useEffect(() => {
if (tableDetails) {
fetchQueryCount();
fetchTestCaseSummary();
}
}, [tableDetails?.fullyQualifiedName]);