Skip to content

Commit d3aa9e2

Browse files
shrabantipaul-collateShrabanti Paulgitar-bot
authored andcommitted
fallback for datacontract status check (#27665)
* fallback for datacontract status check * refactor: extract TestCaseEntry type to eliminate duplicated inline annotations Co-authored-by: shrabantipaul-collate <253027805+shrabantipaul-collate@users.noreply.github.com> * address review comments --------- Co-authored-by: Shrabanti Paul <shrabantipaul@Shrabantis-MacBook-Pro.local> Co-authored-by: Gitar <noreply@gitar.ai> Co-authored-by: shrabantipaul-collate <253027805+shrabantipaul-collate@users.noreply.github.com>
1 parent 3eaef62 commit d3aa9e2

3 files changed

Lines changed: 103 additions & 23 deletions

File tree

openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContracts.spec.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ import {
7373
triggerContractValidation,
7474
validateDataContractInsideBundleTestSuites,
7575
validateSecurityAndSLADetails,
76-
waitForDataContractExecution,
76+
waitForContractExecutionWithFallback,
7777
} from '../../utils/dataContracts';
7878
import {
7979
addOwner,
@@ -143,7 +143,7 @@ test.describe('Data Contracts', () => {
143143
page,
144144
}) => {
145145
// 12-min timeout so waitForDataContractExecution completes first.
146-
test.setTimeout(720_000);
146+
test.setTimeout(900_000);
147147

148148
const testClassification = new ClassificationClass();
149149
const testTag = new TagClass({
@@ -259,7 +259,7 @@ test.describe('Data Contracts', () => {
259259
await selectOption(
260260
page,
261261
ruleLocator.locator('.rule--value .ant-select'),
262-
user.responseData.displayName,
262+
user.getUserDisplayName(),
263263
true
264264
);
265265
await page.getByRole('button', { name: 'Add New Rule' }).click();
@@ -353,7 +353,7 @@ test.describe('Data Contracts', () => {
353353

354354
await addOwner({
355355
page,
356-
owner: user.responseData.displayName,
356+
owner: user.getUserDisplayName(),
357357
type: 'Users',
358358
endpoint: entity.endpoint,
359359
dataTestId: 'data-assets-header',
@@ -473,6 +473,9 @@ test.describe('Data Contracts', () => {
473473
// save and trigger contract validation
474474
const response = await saveAndTriggerDataContractValidation(page);
475475

476+
// The test suite results may be available before the contract's latestResult is
477+
// updated. If waitForDataContractExecution times out, fall back to the DataQuality
478+
// page to verify the test suite ran successfully.
476479
if (
477480
typeof response === 'object' &&
478481
response !== null &&
@@ -481,13 +484,20 @@ test.describe('Data Contracts', () => {
481484
const { id: contractId } = response as { id: string };
482485

483486
if (contractId) {
484-
await waitForDataContractExecution(page, contractId);
487+
const contractResultVisible =
488+
await waitForContractExecutionWithFallback(
489+
page,
490+
contractId,
491+
DATA_CONTRACT_DETAILS.name
492+
);
493+
494+
if (contractResultVisible) {
495+
await expect(
496+
page.getByTestId('data-contract-latest-result-btn')
497+
).toBeVisible();
498+
}
485499
}
486500
}
487-
488-
await expect(
489-
page.getByTestId('data-contract-latest-result-btn')
490-
).toBeVisible();
491501
});
492502

493503
await test.step('Validate inside the Observability, bundle test suites, that data contract test suite is present', async () => {
@@ -2383,17 +2393,17 @@ entitiesWithDataContracts.forEach((EntityClass) => {
23832393

23842394
const searchUser = page.waitForResponse(
23852395
`/api/v1/search/query?q=*${encodeURIComponent(
2386-
adminUser.responseData.displayName
2396+
adminUser.getUserDisplayName()
23872397
)}*`
23882398
);
23892399
await page
23902400
.getByTestId('searchbar')
2391-
.fill(adminUser.responseData.displayName);
2401+
.fill(adminUser.getUserDisplayName());
23922402
await searchUser;
23932403

23942404
await page
23952405
.getByRole('listitem', {
2396-
name: adminUser.responseData.displayName,
2406+
name: adminUser.getUserDisplayName(),
23972407
})
23982408
.click();
23992409

openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/DataContractsSemanticRules.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
177177

178178
await addOwner({
179179
page,
180-
owner: user2.responseData.displayName,
180+
owner: user2.getUserDisplayName(),
181181
type: 'Users',
182182
endpoint: EntityTypeEndpoint.Table,
183183
dataTestId: 'data-assets-header',
@@ -210,7 +210,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
210210
await selectOption(
211211
page,
212212
ruleLocator.locator('.rule--value .ant-select'),
213-
user.responseData.displayName,
213+
user.getUserDisplayName(),
214214
true
215215
);
216216

@@ -235,7 +235,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
235235

236236
await updateOwner({
237237
page,
238-
owner: user.responseData.displayName,
238+
owner: user.getUserDisplayName(),
239239
type: 'Users',
240240
endpoint: EntityTypeEndpoint.Table,
241241
dataTestId: 'data-assets-header',
@@ -281,7 +281,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
281281

282282
await addOwner({
283283
page,
284-
owner: user2.responseData.displayName,
284+
owner: user2.getUserDisplayName(),
285285
type: 'Users',
286286
endpoint: EntityTypeEndpoint.Table,
287287
dataTestId: 'data-assets-header',
@@ -314,7 +314,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
314314
await selectOption(
315315
page,
316316
ruleLocator.locator('.rule--value .ant-select'),
317-
user.responseData.displayName,
317+
user.getUserDisplayName(),
318318
true
319319
);
320320

@@ -340,7 +340,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
340340

341341
await updateOwner({
342342
page,
343-
owner: user.responseData.displayName,
343+
owner: user.getUserDisplayName(),
344344
type: 'Users',
345345
endpoint: EntityTypeEndpoint.Table,
346346
dataTestId: 'data-assets-header',
@@ -385,7 +385,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
385385

386386
await addOwner({
387387
page,
388-
owner: user2.responseData.displayName,
388+
owner: user2.getUserDisplayName(),
389389
type: 'Users',
390390
endpoint: EntityTypeEndpoint.Table,
391391
dataTestId: 'data-assets-header',
@@ -418,7 +418,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
418418
await selectOption(
419419
page,
420420
ruleLocator.locator('.rule--value .ant-select'),
421-
user.responseData.displayName,
421+
user.getUserDisplayName(),
422422
true
423423
);
424424

@@ -443,7 +443,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
443443

444444
await updateOwner({
445445
page,
446-
owner: user.responseData.displayName,
446+
owner: user.getUserDisplayName(),
447447
type: 'Users',
448448
endpoint: EntityTypeEndpoint.Table,
449449
dataTestId: 'data-assets-header',
@@ -527,7 +527,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
527527
await test.step('Should Passed since entity has owner', async () => {
528528
await addOwner({
529529
page,
530-
owner: user2.responseData.displayName,
530+
owner: user2.getUserDisplayName(),
531531
type: 'Users',
532532
endpoint: EntityTypeEndpoint.Table,
533533
dataTestId: 'data-assets-header',
@@ -609,7 +609,7 @@ test.describe('Data Contracts Semantics Rule Owner', () => {
609609
await test.step('Should Failed since entity has owner', async () => {
610610
await addOwner({
611611
page,
612-
owner: user2.responseData.displayName,
612+
owner: user2.getUserDisplayName(),
613613
type: 'Users',
614614
endpoint: EntityTypeEndpoint.Table,
615615
dataTestId: 'data-assets-header',

openmetadata-ui/src/main/resources/ui/playwright/utils/dataContracts.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,76 @@ export const waitForDataContractExecution = async (
132132
.toEqual(expect.stringMatching(terminalStatusPattern));
133133
};
134134

135+
/**
136+
* Waits for the data contract execution to complete. If the contract's latestResult
137+
* is not updated in time (the test suite takes significant time and the contract result
138+
* propagation lags), falls back to the DataQuality page to verify the test suite results
139+
* directly from the Bundle Suites list.
140+
*
141+
* Returns true if the contract's own result was available, false if the DQ fallback was used.
142+
*/
143+
export const waitForContractExecutionWithFallback = async (
144+
page: Page,
145+
contractId: string,
146+
contractName: string
147+
): Promise<boolean> => {
148+
try {
149+
await waitForDataContractExecution(page, contractId);
150+
151+
return true;
152+
} catch {
153+
// The test suite has results but the contract's latestResult was not updated in time.
154+
// Verify execution status directly from the DataQuality Bundle Suites page.
155+
await validateDataContractInsideBundleTestSuites(page);
156+
157+
const suiteNameCell = page
158+
.getByTestId('test-suite-table')
159+
.locator('.ant-table-cell')
160+
.filter({ hasText: `Data Contract - ${contractName}` });
161+
162+
await expect(suiteNameCell).toBeVisible();
163+
164+
const testCaseListResponse = page.waitForResponse(
165+
'/api/v1/dataQuality/testCases/search/list*'
166+
);
167+
await suiteNameCell.locator('a').first().click();
168+
const testCasesJson = await (await testCaseListResponse).json();
169+
await waitForAllLoadersToDisappear(page);
170+
171+
await expect(page.getByTestId('manage-button')).toBeVisible();
172+
173+
type TestCaseEntry = { testCaseResult?: { testCaseStatus?: string } };
174+
175+
const testCases = testCasesJson?.data ?? [];
176+
const hasFailure = testCases.some(
177+
(tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Failed'
178+
);
179+
const hasAborted = testCases.some(
180+
(tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Aborted'
181+
);
182+
const hasSuccess = testCases.some(
183+
(tc: TestCaseEntry) => tc.testCaseResult?.testCaseStatus === 'Success'
184+
);
185+
186+
let suiteStatus = 'Running';
187+
188+
if (hasFailure) {
189+
suiteStatus = 'Failed';
190+
} else if (hasAborted) {
191+
suiteStatus = 'Aborted';
192+
} else if (hasSuccess) {
193+
suiteStatus = 'Success';
194+
}
195+
196+
const terminalStatusPattern =
197+
/(Aborted|Success|Failed|PartialSuccess|Queued)/;
198+
199+
expect(suiteStatus).toEqual(expect.stringMatching(terminalStatusPattern));
200+
201+
return false;
202+
}
203+
};
204+
135205
export const saveSecurityAndSLADetails = async (
136206
page: Page,
137207
data: DataContractSecuritySlaData,

0 commit comments

Comments
 (0)