ISSUE #16676 - Add Tag to CreateTestCase (#21366)

* refactor: removed testSuite field from CreateTestCase

BREAKING CHANGE: when creating a test case, testsuite is now derived from entityLink (fetch or created)

* feat: allow setting tags when creating a test case

* style: ran linters

* fix: compiling error

* fix: failing test case

* fix: failing tests

* removed testSuite from required filed

* fixed ui side

* style: ran java linting

* deprecation: remove testSuite param from ingestion

* fix: remove test suite filed

* fix: remove test_suite field

---------

Co-authored-by: Shailesh Parmar <shailesh.parmar.webdev@gmail.com>
This commit is contained in:
Teddy 2025-06-11 09:59:08 +02:00 committed by GitHub
parent 492479f8e3
commit c09a8b27ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 319 additions and 273 deletions

View File

@ -41,14 +41,13 @@ from metadata.generated.schema.tests.testDefinition import (
TestDefinition,
TestPlatform,
)
from metadata.generated.schema.tests.testSuite import TestSuite
from metadata.generated.schema.type.basic import EntityLink, FullyQualifiedEntityName
from metadata.ingestion.api.models import Either
from metadata.ingestion.api.parser import parse_workflow_config_gracefully
from metadata.ingestion.api.step import Step
from metadata.ingestion.api.steps import Processor
from metadata.ingestion.ometa.ometa_api import OpenMetadata
from metadata.utils import entity_link, fqn
from metadata.utils import entity_link
from metadata.utils.logger import test_suite_logger
logger = test_suite_logger()
@ -83,11 +82,6 @@ class TestCaseRunner(Processor):
# Add the test cases from the YAML file, if any
test_cases = self.get_test_cases(
test_cases=record.test_cases,
test_suite_fqn=fqn.build(
None,
TestSuite,
table_fqn=record.table.fullyQualifiedName.root,
),
table_fqn=record.table.fullyQualifiedName.root,
)
openmetadata_test_cases = self.filter_for_om_test_cases(test_cases)
@ -113,7 +107,7 @@ class TestCaseRunner(Processor):
return Either(right=TestCaseResults(test_results=test_results))
def get_test_cases(
self, test_cases: List[TestCase], test_suite_fqn: str, table_fqn: str
self, test_cases: List[TestCase], table_fqn: str
) -> List[TestCase]:
"""
Based on the test suite test cases that we already know, pick up
@ -124,7 +118,6 @@ class TestCaseRunner(Processor):
return self.compare_and_create_test_cases(
cli_test_cases_definitions=cli_test_cases,
test_cases=test_cases,
test_suite_fqn=test_suite_fqn,
table_fqn=table_fqn,
)
@ -141,7 +134,6 @@ class TestCaseRunner(Processor):
cli_test_cases_definitions: List[TestCaseDefinition],
test_cases: List[TestCase],
table_fqn: str,
test_suite_fqn: str,
) -> List[TestCase]:
"""
compare test cases defined in CLI config workflow with test cases
@ -198,7 +190,6 @@ class TestCaseRunner(Processor):
column_name=test_case_to_create.columnName,
)
),
testSuite=test_suite_fqn,
parameterValues=(
list(test_case_to_create.parameterValues)
if test_case_to_create.parameterValues

View File

@ -153,9 +153,8 @@ class OpenMetadataValidationAction(ValidationAction):
)
if table_entity:
test_suite = self._check_or_create_test_suite(table_entity)
for result in validation_result_suite.results:
self._handle_test_case(result, table_entity, test_suite)
self._handle_test_case(result, table_entity)
@staticmethod
def _get_checkpoint_batch_spec(
@ -376,9 +375,7 @@ class OpenMetadataValidationAction(ValidationAction):
return [test_result_value]
def _handle_test_case(
self, result: Dict, table_entity: Table, test_suite: TestSuite
):
def _handle_test_case(self, result: Dict, table_entity: Table):
"""Handle adding test to table entity based on the test case.
Test Definitions will be created on the fly from the results of the
great expectations run. We will then write the test case results to the
@ -387,7 +384,6 @@ class OpenMetadataValidationAction(ValidationAction):
Args:
result: GE test result
table_entity: table entity object
test_suite: test suite object
"""
try:
@ -417,7 +413,6 @@ class OpenMetadataValidationAction(ValidationAction):
fqn=table_entity.fullyQualifiedName.root,
column_name=fqn.split_test_case_fqn(test_case_fqn).column,
),
test_suite_fqn=test_suite.fullyQualifiedName.root,
test_definition_fqn=test_definition.fullyQualifiedName.root,
test_case_parameter_values=self._get_test_case_params_value(result),
)

View File

@ -119,9 +119,8 @@ class OpenMetadataValidationAction1xx(ValidationAction):
)
if table_entity:
test_suite = self._check_or_create_test_suite(table_entity)
for result in v.results:
self._handle_test_case(result, table_entity, test_suite)
self._handle_test_case(result, table_entity)
@staticmethod
def _get_checkpoint_batch_spec(
@ -334,9 +333,7 @@ class OpenMetadataValidationAction1xx(ValidationAction):
return [test_result_value]
def _handle_test_case(
self, result: Dict, table_entity: Table, test_suite: TestSuite
):
def _handle_test_case(self, result: Dict, table_entity: Table):
"""Handle adding test to table entity based on the test case.
Test Definitions will be created on the fly from the results of the
great expectations run. We will then write the test case results to the
@ -345,7 +342,6 @@ class OpenMetadataValidationAction1xx(ValidationAction):
Args:
result: GE test result
table_entity: table entity object
test_suite: test suite object
"""
try:
@ -375,7 +371,6 @@ class OpenMetadataValidationAction1xx(ValidationAction):
fqn=table_entity.fullyQualifiedName.root,
column_name=fqn.split_test_case_fqn(test_case_fqn).column,
),
test_suite_fqn=test_suite.fullyQualifiedName.root,
test_definition_fqn=test_definition.fullyQualifiedName.root,
test_case_parameter_values=self._get_test_case_params_value(result),
)

View File

@ -167,7 +167,6 @@ class OMetaTestsMixin:
self,
test_case_fqn: str,
entity_link: Optional[str] = None,
test_suite_fqn: Optional[str] = None,
test_definition_fqn: Optional[str] = None,
test_case_parameter_values: Optional[List[TestCaseParameterValue]] = None,
):
@ -196,7 +195,6 @@ class OMetaTestsMixin:
CreateTestCaseRequest(
name=test_case_fqn.split(".")[-1],
entityLink=entity_link,
testSuite=test_suite_fqn,
testDefinition=test_definition_fqn,
parameterValues=test_case_parameter_values,
) # type: ignore

View File

@ -83,7 +83,6 @@ from metadata.ingestion.source.database.dbt.dbt_service import (
)
from metadata.ingestion.source.database.dbt.dbt_utils import (
check_ephemeral_node,
check_or_create_test_suite,
create_test_case_parameter_definitions,
create_test_case_parameter_values,
generate_entity_link,
@ -1024,9 +1023,6 @@ class DbtSource(DbtServiceSource):
logger.debug(f"Processing DBT Test Case for node: {manifest_node.name}")
entity_link_list = generate_entity_link(dbt_test)
for entity_link_str in entity_link_list:
test_suite = check_or_create_test_suite(
self.metadata, entity_link_str
)
table_fqn = get_table_fqn(entity_link_str)
logger.debug(f"Table fqn found: {table_fqn}")
source_elements = table_fqn.split(fqn.FQN_SEPARATOR)
@ -1056,7 +1052,6 @@ class DbtSource(DbtServiceSource):
manifest_node.name
),
entityLink=entity_link_str,
testSuite=test_suite.fullyQualifiedName,
parameterValues=create_test_case_parameter_values(
dbt_test
),

View File

@ -1603,7 +1603,6 @@ class SampleDataSource(
description=test_case["description"],
testDefinition=test_case["testDefinitionName"],
entityLink=test_case["entityLink"],
testSuite=suite.fullyQualifiedName.root,
parameterValues=[
TestCaseParameterValue(**param_values)
for param_values in test_case["parameterValues"]

View File

@ -377,7 +377,6 @@ def get_create_test_suite(
def get_create_test_case(
entity_link: str,
test_suite: FullyQualifiedEntityName,
test_definition: FullyQualifiedEntityName,
parameter_values: List[TestCaseParameterValue],
name: Optional[EntityName] = None,
@ -387,7 +386,6 @@ def get_create_test_case(
return CreateTestCaseRequest(
name=TestCaseEntityName(name),
entityLink=EntityLink(entity_link),
testSuite=test_suite,
testDefinition=test_definition,
parameterValues=parameter_values,
)

View File

@ -168,7 +168,6 @@ class OMetaTableTest(TestCase):
cls.test_case = cls.metadata.create_or_update(
get_create_test_case(
entity_link=f"<#E::table::{cls.table.fullyQualifiedName.root}>",
test_suite=cls.test_suite.fullyQualifiedName,
test_definition=cls.test_definition.fullyQualifiedName,
parameter_values=[TestCaseParameterValue(name="foo", value="10")],
)

View File

@ -112,7 +112,6 @@ class OMetaTestSuiteTest(TestCase):
entityLink=EntityLink(
"<#E::table::sample_data.ecommerce_db.shopify.dim_address>"
),
testSuite=cls.test_suite.fullyQualifiedName,
testDefinition=cls.test_definition.fullyQualifiedName,
parameterValues=[TestCaseParameterValue(name="foo", value="10")],
)
@ -169,7 +168,6 @@ class OMetaTestSuiteTest(TestCase):
test_case = self.metadata.get_or_create_test_case(
test_case_fqn,
test_suite_fqn=self.test_suite.fullyQualifiedName.root,
test_definition_fqn="columnValuesToMatchRegex",
entity_link="<#E::table::sample_data.ecommerce_db.shopify.dim_address::columns::last_name>",
test_case_parameter_values=[

View File

@ -142,7 +142,6 @@ class TestSuiteWorkflowTests(unittest.TestCase):
CreateTestCaseRequest(
name="testCaseForIntegration",
entityLink=f"<#E::table::{cls.table_with_suite.fullyQualifiedName.root}>",
testSuite=cls.test_suite.fullyQualifiedName,
testDefinition="tableRowCountToEqual",
parameterValues=[TestCaseParameterValue(name="value", value="10")],
)
@ -266,7 +265,6 @@ class TestSuiteWorkflowTests(unittest.TestCase):
test_cases: List[TestCase] = workflow.steps[0].get_test_cases(
test_cases=table_and_tests.right.test_cases,
test_suite_fqn=self.table_with_suite.fullyQualifiedName.root + ".testSuite",
table_fqn=self.table_with_suite.fullyQualifiedName.root,
)
@ -385,7 +383,6 @@ class TestSuiteWorkflowTests(unittest.TestCase):
created_test_case = workflow.steps[0].compare_and_create_test_cases(
cli_test_cases_definitions=config_test_cases_def,
test_cases=table_and_tests.right.test_cases,
test_suite_fqn=f"{self.table_with_suite.fullyQualifiedName.root}.testSuite",
table_fqn=self.table_with_suite.fullyQualifiedName.root,
)

View File

@ -153,7 +153,6 @@ Given an entity fqn, retrieve the link test suite if it exists or create a new o
get_or_create_test_case(
test_case_fqn: str,
entity_link: Optional[str] = None,
test_suite_fqn: Optional[str] = None,
test_definition_fqn: Optional[str] = None,
test_case_parameter_values: Optional[List[TestCaseParameterValue]] = None
)

View File

@ -6,6 +6,8 @@ import static org.openmetadata.schema.type.EventType.LOGICAL_TEST_CASE_ADDED;
import static org.openmetadata.schema.type.Include.ALL;
import static org.openmetadata.service.Entity.FIELD_OWNERS;
import static org.openmetadata.service.Entity.FIELD_TAGS;
import static org.openmetadata.service.Entity.INGESTION_BOT_NAME;
import static org.openmetadata.service.Entity.TABLE;
import static org.openmetadata.service.Entity.TEST_CASE;
import static org.openmetadata.service.Entity.TEST_CASE_RESULT;
import static org.openmetadata.service.Entity.TEST_DEFINITION;
@ -34,6 +36,7 @@ import org.openmetadata.schema.EntityInterface;
import org.openmetadata.schema.EntityTimeSeriesInterface;
import org.openmetadata.schema.api.feed.CloseTask;
import org.openmetadata.schema.api.feed.ResolveTask;
import org.openmetadata.schema.api.tests.CreateTestSuite;
import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.tests.TestCase;
@ -60,6 +63,7 @@ import org.openmetadata.schema.type.change.ChangeSource;
import org.openmetadata.schema.utils.EntityInterfaceUtil;
import org.openmetadata.service.Entity;
import org.openmetadata.service.exception.EntityNotFoundException;
import org.openmetadata.service.resources.dqtests.TestSuiteMapper;
import org.openmetadata.service.resources.feeds.MessageParser.EntityLink;
import org.openmetadata.service.search.SearchListFilter;
import org.openmetadata.service.util.EntityUtil;
@ -99,7 +103,10 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
public void setFields(TestCase test, Fields fields) {
test.setTestSuites(
fields.contains(Entity.FIELD_TEST_SUITES) ? getTestSuites(test) : test.getTestSuites());
test.setTestSuite(fields.contains(TEST_SUITE_FIELD) ? getTestSuite(test) : test.getTestSuite());
test.setTestSuite(
fields.contains(TEST_SUITE_FIELD)
? getTestSuite(test.getId(), entityType, TEST_SUITE, Direction.FROM)
: test.getTestSuite());
test.setTestDefinition(
fields.contains(TEST_DEFINITION) ? getTestDefinition(test) : test.getTestDefinition());
test.setTestCaseResult(
@ -135,7 +142,13 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
@Override
public EntityInterface getParentEntity(TestCase entity, String fields) {
return Entity.getEntity(entity.getTestSuite(), fields, ALL);
EntityReference testSuite = entity.getTestSuite();
if (testSuite == null) {
EntityLink entityLink = EntityLink.parse(entity.getEntityLink());
return Entity.getEntity(entityLink, fields, ALL);
} else {
return Entity.getEntity(testSuite, fields, ALL);
}
}
@Override
@ -161,10 +174,11 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
EntityLink entityLink = EntityLink.parse(test.getEntityLink());
EntityUtil.validateEntityLink(entityLink);
// validate test definition and test suite
TestSuite testSuite = Entity.getEntity(test.getTestSuite(), "", Include.NON_DELETED);
test.setTestSuite(testSuite.getEntityReference());
// Get existing basic test suite or create a new one if it doesn't exist
EntityReference testSuite = getOrCreateTestSuite(test);
test.setTestSuite(testSuite);
// validate test definition
TestDefinition testDefinition =
Entity.getEntity(test.getTestDefinition(), "", Include.NON_DELETED);
test.setTestDefinition(testDefinition.getEntityReference());
@ -172,10 +186,42 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
validateTestParameters(test.getParameterValues(), testDefinition.getParameterDefinition());
}
private EntityReference getTestSuite(TestCase test) throws EntityNotFoundException {
/*
* Get the test suite for a test case. We'll use the entity linked to the test case
* to find the basic test suite. If it doesn't exist, create a new one.
*/
private EntityReference getOrCreateTestSuite(TestCase test) {
EntityReference entityReference = null;
try {
EntityLink entityLink = EntityLink.parse(test.getEntityLink());
EntityInterface entity = Entity.getEntity(entityLink, "", ALL);
return getTestSuite(entity.getId(), TEST_SUITE, TABLE, Direction.TO);
} catch (EntityNotFoundException e) {
// If the test suite is not found, we'll create a new one
EntityLink entityLink = EntityLink.parse(test.getEntityLink());
TestSuiteRepository testSuiteRepository =
(TestSuiteRepository) Entity.getEntityRepository(Entity.TEST_SUITE);
TestSuiteMapper mapper = new TestSuiteMapper();
CreateTestSuite createTestSuite =
new CreateTestSuite()
.withName(entityLink.getEntityFQN() + ".testSuite")
.withBasicEntityReference(entityLink.getEntityFQN());
TestSuite testSuite = mapper.createToEntity(createTestSuite, INGESTION_BOT_NAME);
testSuite.setBasic(true);
testSuiteRepository.create(null, testSuite);
entityReference = testSuite.getEntityReference();
}
return entityReference;
}
private EntityReference getTestSuite(UUID id, String to, String from, Direction direction)
throws EntityNotFoundException {
// `testSuite` field returns the executable `testSuite` linked to that testCase
List<CollectionDAO.EntityRelationshipRecord> records =
findFromRecords(test.getId(), entityType, Relationship.CONTAINS, TEST_SUITE);
List<CollectionDAO.EntityRelationshipRecord> records = new ArrayList<>();
switch (direction) {
case FROM -> records = findFromRecords(id, to, Relationship.CONTAINS, from);
case TO -> records = findToRecords(id, from, Relationship.CONTAINS, to);
}
for (CollectionDAO.EntityRelationshipRecord testSuiteId : records) {
TestSuite testSuite = Entity.getEntity(TEST_SUITE, testSuiteId.getId(), "", Include.ALL);
if (Boolean.TRUE.equals(testSuite.getBasic())) {
@ -185,7 +231,7 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
throw new EntityNotFoundException(
String.format(
"Error occurred when retrieving executable test suite for testCase %s. ",
test.getName())
id.toString())
+ "No executable test suite was found.");
}
@ -431,6 +477,11 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
}
public void isTestSuiteBasic(String testSuiteFqn) {
if (testSuiteFqn == null) {
// If the test suite FQN is not provided, we'll assume it's a basic test suite
return;
}
TestSuite testSuite = Entity.getEntityByName(Entity.TEST_SUITE, testSuiteFqn, null, null);
if (Boolean.FALSE.equals(testSuite.getBasic())) {
throw new IllegalArgumentException(
@ -740,8 +791,7 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
}
// Set the column tags. Will be used to mask the sample data
if (!authorizePII) {
populateEntityFieldTags(
Entity.TABLE, table.getColumns(), table.getFullyQualifiedName(), true);
populateEntityFieldTags(TABLE, table.getColumns(), table.getFullyQualifiedName(), true);
List<TagLabel> tags = daoCollection.tagUsageDAO().getTags(table.getFullyQualifiedName());
table.setTags(tags);
return maskSampleData(sampleData, table, table.getColumns());
@ -831,4 +881,9 @@ public class TestCaseRepository extends EntityRepository<TestCase> {
}
}
}
private enum Direction {
TO,
FROM
}
}

View File

@ -21,8 +21,8 @@ public class TestCaseMapper implements EntityMapper<TestCase, CreateTestCase> {
.withComputePassedFailedRowCount(create.getComputePassedFailedRowCount())
.withUseDynamicAssertion(create.getUseDynamicAssertion())
.withEntityFQN(entityLink.getFullyQualifiedFieldValue())
.withTestSuite(getEntityReference(Entity.TEST_SUITE, create.getTestSuite()))
.withTestDefinition(getEntityReference(Entity.TEST_DEFINITION, create.getTestDefinition()))
.withTags(create.getTags())
.withCreatedBy(user);
}
}

View File

@ -687,7 +687,6 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
new AuthRequest(tableOpContext, tableResourceContext),
new AuthRequest(testCaseOpContext, testCaseResourceContext));
authorizer.authorizeRequests(securityContext, requests, AuthorizationLogic.ANY);
repository.isTestSuiteBasic(create.getTestSuite());
test = addHref(uriInfo, repository.create(uriInfo, test));
return Response.created(test.getHref()).entity(test).build();
}
@ -718,8 +717,6 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
List<TestCase> testCases = new ArrayList<>();
Set<String> entityLinks =
createTestCases.stream().map(CreateTestCase::getEntityLink).collect(Collectors.toSet());
Set<String> testSuites =
createTestCases.stream().map(CreateTestCase::getTestSuite).collect(Collectors.toSet());
OperationContext operationContext = new OperationContext(entityType, MetadataOperation.CREATE);
@ -731,7 +728,6 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
authorizer.authorize(securityContext, operationContext, resourceContext);
});
testSuites.forEach(repository::isTestSuiteBasic);
limits.enforceBulkSizeLimit(entityType, createTestCases.size());
createTestCases.forEach(
@ -836,7 +832,6 @@ public class TestCaseResource extends EntityResource<TestCase, TestCaseRepositor
new AuthRequest(testCaseOpUpdate, testCaseRC));
authorizer.authorizeRequests(securityContext, requests, AuthorizationLogic.ANY);
TestCase test = mapper.createToEntity(create, securityContext.getUserPrincipal().getName());
repository.isTestSuiteBasic(create.getTestSuite());
repository.prepareInternal(test, true);
PutResponse<TestCase> response =
repository.createOrUpdate(uriInfo, test, securityContext.getUserPrincipal().getName());

View File

@ -550,41 +550,6 @@ public class TestSuiteResource extends EntityResource<TestSuite, TestSuiteReposi
return create(uriInfo, securityContext, authRequests, AuthorizationLogic.ANY, testSuite);
}
@POST
@Path("/executable")
@Operation(
operationId = "createExecutableTestSuite",
summary = "Create an executable test suite",
description = "Create an executable test suite.",
responses = {
@ApiResponse(
responseCode = "200",
description = "Executable test suite",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = TestSuite.class))),
@ApiResponse(responseCode = "400", description = "Bad request")
})
public Response createExecutable(
@Context UriInfo uriInfo,
@Context SecurityContext securityContext,
@Context HttpServletResponse response,
@Valid CreateTestSuite create) {
TestSuite testSuite =
mapper.createToEntity(create, securityContext.getUserPrincipal().getName());
if (testSuite.getBasicEntityReference() == null) {
throw new IllegalArgumentException(BASIC_TEST_SUITE_WITHOUT_REF_ERROR);
}
testSuite.setBasic(true);
// Set the deprecation header based on draft specification from IETF
// https://datatracker.ietf.org/doc/html/draft-ietf-httpapi-deprecation-header-02
response.setHeader("Deprecation", "Monday, March 24, 2025");
response.setHeader("Link", "api/v1/dataQuality/testSuites/basic; rel=\"alternate\"");
List<AuthRequest> authRequests = getAuthRequestsForPost(testSuite);
return create(uriInfo, securityContext, authRequests, AuthorizationLogic.ANY, testSuite);
}
@POST
@Path("/basic")
@Operation(

View File

@ -2396,7 +2396,6 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
testCaseResourceTest
.createRequest(test)
.withEntityLink(String.format("<#E::table::%s>", table.getFullyQualifiedName()))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION2.getFullyQualifiedName());
TestCase testCase = testCaseResourceTest.assertOwnerInheritance(createTestCase, USER1_REF);
@ -2451,7 +2450,6 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
testCaseResourceTest
.createRequest(test)
.withEntityLink(String.format("<#E::table::%s>", table.getFullyQualifiedName()))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION2.getFullyQualifiedName());
TestCase testCase = testCaseResourceTest.createEntity(createTestCase, ADMIN_AUTH_HEADERS);
@ -2504,7 +2502,6 @@ public class TableResourceTest extends EntityResourceTest<Table, CreateTable> {
testCaseResourceTest
.createRequest(test)
.withEntityLink(String.format("<#E::table::%s>", table.getFullyQualifiedName()))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION2.getFullyQualifiedName());
TestCase testCase =
testCaseResourceTest.assertDomainInheritance(createTestCase, DOMAIN.getEntityReference());

View File

@ -121,6 +121,7 @@ import org.openmetadata.schema.type.MetadataOperation;
import org.openmetadata.schema.type.TableData;
import org.openmetadata.schema.type.TagLabel;
import org.openmetadata.schema.type.TaskStatus;
import org.openmetadata.schema.type.TestDefinitionEntityType;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.EntityResourceTest;
import org.openmetadata.service.resources.databases.TableResourceTest;
@ -474,35 +475,21 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
void post_testWithInvalidEntityTestSuite_4xx(TestInfo test) throws IOException {
CreateTestCase create = createRequest(test);
TestSuiteResourceTest testSuiteResourceTest = new TestSuiteResourceTest();
CreateTestSuite createTestSuite =
testSuiteResourceTest.createRequest(test).withName(TEST_TABLE1.getFullyQualifiedName());
TestSuite testSuite =
testSuiteResourceTest.createBasicTestSuite(createTestSuite, ADMIN_AUTH_HEADERS);
create.withEntityLink(INVALID_LINK1).withTestSuite(testSuite.getFullyQualifiedName());
create.withEntityLink(INVALID_LINK1);
assertResponseContains(
() -> createAndCheckEntity(create, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
ENTITY_LINK_MATCH_ERROR);
create.withEntityLink(INVALID_LINK2).withTestSuite(testSuite.getFullyQualifiedName());
create.withEntityLink(INVALID_LINK2);
assertResponseContains(
() -> createAndCheckEntity(create, ADMIN_AUTH_HEADERS),
NOT_FOUND,
"table instance for non-existent not found");
CreateTestCase create1 = createRequest(test);
create1.withTestSuite(TEST_DEFINITION1.getFullyQualifiedName());
assertResponseContains(
() -> createAndCheckEntity(create1, ADMIN_AUTH_HEADERS),
NOT_FOUND,
"testSuite instance for " + TEST_DEFINITION1.getFullyQualifiedName() + " not found");
CreateTestCase create2 = createRequest(test);
create2
.withEntityLink(TABLE_LINK)
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_SUITE1.getFullyQualifiedName());
create2.withEntityLink(TABLE_LINK).withTestDefinition(TEST_SUITE1.getFullyQualifiedName());
assertResponseContains(
() -> createAndCheckEntity(create2, ADMIN_AUTH_HEADERS),
NOT_FOUND,
@ -514,7 +501,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(test);
create
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION2.getFullyQualifiedName())
.withParameterValues(List.of(new TestCaseParameterValue().withName("col").withValue("x")));
assertResponseContains(
@ -523,10 +509,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
"Parameter Values doesn't match Test Definition Parameters");
CreateTestCase create1 = createRequest(test);
create1
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName());
create1.withEntityLink(TABLE_LINK).withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName());
assertResponseContains(
() -> createAndCheckEntity(create1, ADMIN_AUTH_HEADERS),
BAD_REQUEST,
@ -539,7 +522,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(test);
create
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -564,7 +546,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(test);
create
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -658,7 +639,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(test);
create
.withEntityLink(sensitiveColumnLink)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -705,7 +685,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK_2)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -715,7 +694,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create1 =
createRequest(test, 1)
.withEntityLink(TABLE_LINK_2)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -732,7 +710,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create3 =
createRequest(test, 2)
.withEntityLink(TABLE_COLUMN_LINK_2)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -751,7 +728,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create4 =
createRequest(test, i)
.withEntityLink(TABLE_COLUMN_LINK_2)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -775,7 +751,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
queryParams.remove("includeAllTests");
queryParams.remove("include");
queryParams.remove("entityLink");
queryParams.put("testSuiteId", TEST_SUITE1.getId().toString());
queryParams.put("testSuiteId", testCase.getTestSuite().getId().toString());
testCaseList = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
verifyTestCases(testCaseList, expectedTestCaseList, 12);
@ -833,7 +809,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(testInfo, i)
.withEntityLink(String.format("<#E::table::%s>", tableFQN))
.withTestSuite(testSuiteFQN)
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -924,7 +899,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(testInfo, i)
.withEntityLink(String.format("<#E::table::%s>", tableFQN))
.withTestSuite(testSuiteFQN)
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -1157,7 +1131,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(testInfo)
.withEntityLink(String.format("<#E::table::%s>", table.getFullyQualifiedName()))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION2.getFullyQualifiedName());
createEntity(create, ADMIN_AUTH_HEADERS);
create =
@ -1165,7 +1138,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withEntityLink(
String.format(
"<#E::table::%s::columns::%s>", table.getFullyQualifiedName(), columnName))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -1295,16 +1267,12 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestSuite createLogicalTestSuite = testSuiteResourceTest.createRequest(test);
TestSuite logicalTestSuite =
testSuiteResourceTest.createEntity(createLogicalTestSuite, ADMIN_AUTH_HEADERS);
// Create an executable test suite
TestSuite executableTestSuite = createExecutableTestSuite(test);
List<TestCase> testCases = new ArrayList<>();
// Create the test cases (need to be created against an executable test suite)
for (int i = 0; i < 5; i++) {
CreateTestCase create =
createRequest("test_testSuite__" + i)
.withTestSuite(executableTestSuite.getFullyQualifiedName());
CreateTestCase create = createRequest("test_testSuite__" + i);
TestCase testCase = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
testCases.add(testCase);
}
@ -1328,10 +1296,15 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
logicalTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertTrue(assertTestCaseIdNotInList(logicalTestSuiteTestCases, logicalTestCaseIdToDelete));
queryParams.put("testSuiteId", executableTestSuite.getId().toString());
String testSuiteID = testCases.get(0).getTestSuite().getId().toString();
queryParams.put("testSuiteId", testSuiteID);
ResultList<TestCase> executableTestSuiteTestCases =
getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(testCases.size(), executableTestSuiteTestCases.getData().size());
Integer initialSize = executableTestSuiteTestCases.getData().size();
assertTrue(
executableTestSuiteTestCases.getData().stream()
.allMatch(t -> t.getTestSuite().getId().toString().equals(testSuiteID)));
// Soft Delete a test case from the executable test suite and check that it is deleted from the
// executable test suite and from the logical test suite
@ -1347,18 +1320,30 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
logicalTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(4, logicalTestSuiteTestCases.getData().size());
queryParams.put("testSuiteId", executableTestSuite.getId().toString());
queryParams.put("testSuiteId", testSuiteID);
queryParams.remove("includeAllTests");
queryParams.remove("include");
executableTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(4, executableTestSuiteTestCases.getData().size());
assertEquals(initialSize - 1, executableTestSuiteTestCases.getData().size());
assertTrue(
assertTestCaseIdNotInList(executableTestSuiteTestCases, executableTestCaseIdToDelete));
queryParams.put("includeAllTests", true);
queryParams.put("include", "all");
executableTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(5, executableTestSuiteTestCases.getData().size());
boolean hasDeletedTrue = false;
boolean hasDeletedFalse = false;
for (TestCase testCase : executableTestSuiteTestCases.getData()) {
if (!hasDeletedTrue && testCase.getDeleted()) {
hasDeletedTrue = true;
} else if (!hasDeletedFalse && !testCase.getDeleted()) {
hasDeletedFalse = true;
}
}
assertTrue(
hasDeletedTrue
&& hasDeletedFalse); // We should have both deleted and non-deleted test cases in the
// executable test suite
// Hard Delete a test case from the executable test suite and check that it is deleted from the
// executable test suite and from the logical test suite
@ -1366,12 +1351,27 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
queryParams.put("testSuiteId", logicalTestSuite.getId().toString());
logicalTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(3, logicalTestSuiteTestCases.getData().size());
assertTrue(
logicalTestSuiteTestCases.getData().stream()
.allMatch(
t -> {
List<TestSuite> testSuites = t.getTestSuites();
return testSuites.stream()
.anyMatch(
ts -> ts.getId().toString().equals(logicalTestSuite.getId().toString()));
}));
assertTrue(assertTestCaseIdNotInList(logicalTestSuiteTestCases, executableTestCaseIdToDelete));
queryParams.put("testSuiteId", executableTestSuite.getId().toString());
queryParams.put("testSuiteId", testSuiteID);
executableTestSuiteTestCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(4, executableTestSuiteTestCases.getData().size());
assertTrue(
executableTestSuiteTestCases.getData().stream()
.allMatch(
t -> {
List<TestSuite> testSuites = t.getTestSuites();
return testSuites.stream()
.anyMatch(ts -> ts.getId().toString().equals(testSuiteID));
}));
assertTrue(
assertTestCaseIdNotInList(executableTestSuiteTestCases, executableTestCaseIdToDelete));
}
@ -1383,16 +1383,9 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestSuite createLogicalTestSuite = testSuiteResourceTest.createRequest(test);
TestSuite logicalTestSuite =
testSuiteResourceTest.createEntity(createLogicalTestSuite, ADMIN_AUTH_HEADERS);
// Create an executable test suite
CreateTestSuite createTestSuite =
testSuiteResourceTest.createRequest(test).withName(TEST_TABLE2.getFullyQualifiedName());
TestSuite executableTestSuite =
testSuiteResourceTest.createBasicTestSuite(createTestSuite, ADMIN_AUTH_HEADERS);
// Create the test cases (need to be created against an executable test suite)
CreateTestCase create =
createRequest("test_testSuite__" + test.getDisplayName())
.withTestSuite(executableTestSuite.getFullyQualifiedName());
CreateTestCase create = createRequest("test_testSuite__" + test.getDisplayName());
TestCase testCase = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
List<UUID> testCaseIds = listOf(testCase.getId());
@ -1402,14 +1395,15 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
TestCase testCaseWithSuites =
getEntityByName(testCase.getFullyQualifiedName(), "*", ADMIN_AUTH_HEADERS);
assertEquals(
executableTestSuite.getFullyQualifiedName(),
testCase.getTestSuite().getFullyQualifiedName(),
testCaseWithSuites.getTestSuite().getFullyQualifiedName());
assertEquals(2, testCaseWithSuites.getTestSuites().size());
// Verify both our testSuites are in the list of TestSuite Entities
Map<String, TestSuite> testSuiteFQNs = new HashMap<>();
testSuiteFQNs.put(logicalTestSuite.getFullyQualifiedName(), logicalTestSuite);
testSuiteFQNs.put(executableTestSuite.getFullyQualifiedName(), executableTestSuite);
Map<String, EntityReference> testSuiteFQNs = new HashMap<>();
testSuiteFQNs.put(
logicalTestSuite.getFullyQualifiedName(), logicalTestSuite.getEntityReference());
testSuiteFQNs.put(testCase.getTestSuite().getFullyQualifiedName(), testCase.getTestSuite());
for (TestSuite testSuite : testCaseWithSuites.getTestSuites()) {
assertNotNull(testSuiteFQNs.get(testSuite.getFullyQualifiedName()));
@ -1903,7 +1897,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
@Test
void get_listTestCaseWithStatusAndType(TestInfo test) throws ParseException, IOException {
TestSuite testSuite = createExecutableTestSuite(test);
int testCaseEntries = 15;
@ -1913,16 +1906,10 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
// Create column level test case
createdTestCase.add(
createEntity(
createRequest(test, i + 1)
.withEntityLink(TABLE_COLUMN_LINK)
.withTestSuite(testSuite.getFullyQualifiedName()),
ADMIN_AUTH_HEADERS));
createRequest(test, i + 1).withEntityLink(TABLE_COLUMN_LINK), ADMIN_AUTH_HEADERS));
continue;
}
createdTestCase.add(
createEntity(
createRequest(test, i + 1).withTestSuite(testSuite.getFullyQualifiedName()),
ADMIN_AUTH_HEADERS));
createdTestCase.add(createEntity(createRequest(test, i + 1), ADMIN_AUTH_HEADERS));
}
for (int i = 0; i < testCaseEntries; i++) {
@ -1944,39 +1931,73 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
createdTestCase.get(i).getFullyQualifiedName(), createTestCaseResult, ADMIN_AUTH_HEADERS);
}
String testSuiteId = createdTestCase.get(0).getTestSuite().getId().toString();
Map<String, Object> queryParams = new HashMap<>();
queryParams.put("limit", 100);
queryParams.put("testSuiteId", testSuite.getId().toString());
queryParams.put("testSuiteId", testSuiteId);
queryParams.put("fields", "testSuite");
// Assert we get all 15 test cases
ResultList<TestCase> testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(testCaseEntries, testCases.getData().size());
assertTrue(
testCases.getData().stream()
.allMatch(t -> t.getTestSuite().getId().toString().equals(testSuiteId)));
// Assert we get 8 failed test cases
queryParams.put("testCaseStatus", TestCaseStatus.Failed);
testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(8, testCases.getData().size());
assertTrue(
testCases.getData().stream()
.allMatch(t -> t.getTestCaseStatus().equals(TestCaseStatus.Failed)));
// Assert we get 7 success test cases
queryParams.put("testCaseStatus", TestCaseStatus.Success);
testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(6, testCases.getData().size());
assertTrue(
testCases.getData().stream()
.allMatch(t -> t.getTestCaseStatus().equals(TestCaseStatus.Success)));
// Assert we get 1 aborted test cases
queryParams.put("testCaseStatus", TestCaseStatus.Aborted);
testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(1, testCases.getData().size());
assertTrue(
testCases.getData().stream()
.allMatch(t -> t.getTestCaseStatus().equals(TestCaseStatus.Aborted)));
queryParams.remove("testCaseStatus");
// Assert we get 7 column level test cases
queryParams.put("testCaseType", "column");
queryParams.put("fields", "testDefinition");
testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(8, testCases.getData().size());
assertTrue(
testCases.getData().stream()
.allMatch(
t -> {
TestDefinition testDefinition =
Entity.getEntity(t.getTestDefinition(), "", Include.ALL);
return testDefinition.getEntityType().equals(TestDefinitionEntityType.COLUMN);
}));
// Assert we get 8 table level test cases
queryParams.put("testCaseType", "table");
queryParams.put("fields", "testDefinition");
testCases = getTestCases(queryParams, ADMIN_AUTH_HEADERS);
assertEquals(7, testCases.getData().size());
testCases.getData().stream()
.filter(
t -> {
TestDefinition testDefinition =
Entity.getEntity(t.getTestDefinition(), "", Include.ALL);
return testDefinition.getEntityType().equals(TestDefinitionEntityType.TABLE);
});
assertTrue(
testCases.getData().stream()
.allMatch(
t -> {
MessageParser.EntityLink entityLink =
MessageParser.EntityLink.parse(t.getEntityLink());
return entityLink.getFieldName() == null; // should be empty for table test cases
}));
}
@Test
@ -2053,7 +2074,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2109,7 +2129,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2150,7 +2169,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2199,7 +2217,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(sensitiveColumnLink)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2243,7 +2260,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2299,7 +2315,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(test);
create
.withEntityLink(TABLE_COLUMN_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -2460,7 +2475,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withEntityLink(
String.format(
"<#E::table::%s>", testSuite.getBasicEntityReference().getFullyQualifiedName()))
.withTestSuite(testSuite.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName());
TestCase testCase = createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
UUID testSuiteId = testCase.getTestSuite().getId();
@ -2555,9 +2569,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
// Add a new test case
CreateTestCase create = createRequest(test, 3);
create
.withEntityLink(testCase.getEntityLink())
.withTestSuite(testCase.getTestSuite().getFullyQualifiedName());
create.withEntityLink(testCase.getEntityLink());
TestCase testCase1 = createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
for (int i = 19; i <= 20; i++) {
@ -2676,7 +2688,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create =
createRequest(test)
.withEntityLink(TABLE_LINK_2)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(
@ -2808,9 +2819,9 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
for (int i = 0; i < 10; i++) {
CreateTestCase createTestCase = createRequest(test, i);
if (i % 2 == 0) {
createTestCase.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
createTestCase.withEntityLink(TABLE_LINK);
} else {
createTestCase.withTestSuite(TEST_SUITE2.getFullyQualifiedName());
createTestCase.withEntityLink(TABLE_LINK_2);
}
createTestCases.add(createTestCase);
}
@ -2847,8 +2858,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("TestCase_OrPerms")
.withDescription("Simple test case")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK);
// 1) user-table-edit-tests -> Allowed
TestCase testCase1 = createEntity(createReq, authHeaders("user-table-edit-tests"));
@ -2875,8 +2885,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("MyTestCaseUpdate")
.withDescription("Initial desc")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK);
TestCase testCase = createEntity(createReq, ADMIN_AUTH_HEADERS);
@ -2916,8 +2925,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("TempOwnerTestCase")
.withDescription("TestCase by temporarily assigned table owner")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK);
TestCase created = createEntity(createReq, authHeaders(tableOwnerUsername));
assertNotNull(created);
@ -2947,8 +2955,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("OwnerFailOtherTableCase")
.withDescription("Fail if referencing a table not owned by user-table-owner")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK_2) // A table the user does not own
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK_2); // A table the user does not own
TestUtils.assertResponse(
() -> createEntity(createReq, authHeaders("user-table-owner")),
@ -2963,8 +2970,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("AllOpsTestCase")
.withDescription("TestCase with direct testCase perms")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK);
TestCase testCase = createEntity(createReq, authHeaders("user-test-case-all-ops"));
assertNotNull(testCase);
@ -2985,8 +2991,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName("NoDeleteUserCase")
.withDescription("Will fail on deletion")
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName())
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
.withEntityLink(TABLE_LINK);
TestCase testCase = createEntity(createReq, authHeaders("user-test-case-create"));
assertNotNull(testCase);
@ -3363,7 +3368,14 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
.withName(name)
.withDescription(name)
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName());
}
public CreateTestCase createRequest(String name, MessageParser.EntityLink entityLink) {
return new CreateTestCase()
.withName(name)
.withDescription(name)
.withEntityLink(entityLink.getLinkString())
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName());
}
@ -3372,7 +3384,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
TestCase createdEntity, CreateTestCase request, Map<String, String> authHeaders) {
validateCommonEntityFields(createdEntity, request, getPrincipalName(authHeaders));
assertEquals(request.getEntityLink(), createdEntity.getEntityLink());
assertReference(request.getTestSuite(), createdEntity.getTestSuite());
assertReference(request.getTestDefinition(), createdEntity.getTestDefinition());
assertEquals(request.getParameterValues(), createdEntity.getParameterValues());
}
@ -3583,7 +3594,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(testInfo);
create
.withEntityLink(TABLE_COLUMN_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -3629,7 +3639,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(testInfo);
create
.withEntityLink(TABLE_COLUMN_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -3664,7 +3673,7 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
queryParams.put("startTimestamp", ts.toString());
queryParams.put("endTimestamp", TestUtils.dateToTimestamp("2021-09-01").toString());
queryParams.put("latest", "true");
queryParams.put("testSuiteId", TEST_SUITE1.getId().toString());
queryParams.put("testSuiteId", testCase.getTestSuite().getId().toString());
testCaseResultResultList =
listTestCaseResultsFromSearch(
@ -3726,7 +3735,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
CreateTestCase create = createRequest(testInfo);
create
.withEntityLink(entityLink)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION3.getFullyQualifiedName())
.withParameterValues(
List.of(new TestCaseParameterValue().withValue("100").withName("missingCountValue")));
@ -3833,7 +3841,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
createRequest(testInfo)
.withName("adminTestCase")
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName());
TestCase adminTestCase = createAndCheckEntity(adminTestCaseReq, ADMIN_AUTH_HEADERS);
@ -3842,7 +3849,6 @@ public class TestCaseResourceTest extends EntityResourceTest<TestCase, CreateTes
createRequest(testInfo)
.withName("testUserTestCase")
.withEntityLink(TABLE_LINK)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName())
.withTestDefinition(TEST_DEFINITION1.getFullyQualifiedName());
TestCase testUserTestCase =
createAndCheckEntity(testUserTestCaseReq, INGESTION_BOT_AUTH_HEADERS);

View File

@ -59,6 +59,7 @@ import org.openmetadata.schema.type.Include;
import org.openmetadata.service.Entity;
import org.openmetadata.service.resources.EntityResourceTest;
import org.openmetadata.service.resources.databases.TableResourceTest;
import org.openmetadata.service.resources.feeds.MessageParser;
import org.openmetadata.service.resources.services.ingestionpipelines.IngestionPipelineResourceTest;
import org.openmetadata.service.resources.teams.TeamResourceTest;
import org.openmetadata.service.resources.teams.UserResourceTest;
@ -144,10 +145,7 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
.withTimestamp(TestUtils.dateToTimestamp("2021-09-09"));
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest("test_testSuite_" + i)
.withTestSuite(TEST_SUITE1.getFullyQualifiedName());
CreateTestCase createTestCase = testCaseResourceTest.createRequest("test_testSuite_" + i);
TestCase testCase =
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
testCases1.add(testCase.getEntityReference());
@ -156,10 +154,7 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
}
for (int i = 5; i < 10; i++) {
CreateTestCase create =
testCaseResourceTest
.createRequest("test_testSuite_2_" + i)
.withTestSuite(TEST_SUITE2.getFullyQualifiedName());
CreateTestCase create = testCaseResourceTest.createRequest("test_testSuite_2_" + i);
TestCase testCase = testCaseResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
testCaseResourceTest.postTestCaseResult(
testCase.getFullyQualifiedName(), createTestCaseResult, ADMIN_AUTH_HEADERS);
@ -217,9 +212,7 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
TestSuite testSuite = createBasicTestSuite(createTestSuite, ADMIN_AUTH_HEADERS);
for (int j = 0; j < 3; j++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest("test_" + RandomStringUtils.randomAlphabetic(10))
.withTestSuite(testSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest("test_" + RandomStringUtils.randomAlphabetic(10));
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
}
testSuites.add(createTestSuite);
@ -369,9 +362,9 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
// We'll create tests cases for testSuite1
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest(String.format("test_testSuite_2_%s_", test.getDisplayName()) + i)
.withTestSuite(executableTestSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest(
String.format("test_testSuite_2_%s_", test.getDisplayName()) + i,
new MessageParser.EntityLink(Entity.TABLE, table.getFullyQualifiedName()));
TestCase testCase =
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
testCases1.add(testCase.getEntityReference());
@ -522,9 +515,8 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
// We'll create tests cases for testSuite1
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest(String.format("test_testSuite_2_%s_", test.getDisplayName()) + i)
.withTestSuite(testSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest(
String.format("test_testSuite_2_%s_", test.getDisplayName()) + i);
TestCase testCase =
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
testCases1.add(testCase.getEntityReference());
@ -572,9 +564,9 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
// We'll create tests cases for testSuite
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest(String.format("test_testSuite_2_%s_", test.getDisplayName()) + i)
.withTestSuite(testSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest(
String.format("test_testSuite_2_%s_", test.getDisplayName()) + i,
new MessageParser.EntityLink(Entity.TABLE, table.getFullyQualifiedName()));
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
}
@ -724,9 +716,9 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
// We'll create tests cases for testSuite1
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest(String.format("test_testSuite_2_%s_", test.getDisplayName()) + i)
.withTestSuite(executableTestSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest(
String.format("test_testSuite_2_%s_", test.getDisplayName()) + i,
new MessageParser.EntityLink(Entity.TABLE, table.getFullyQualifiedName()));
TestCase testCase =
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
testCases.add(testCase.getEntityReference());
@ -770,9 +762,8 @@ public class TestSuiteResourceTest extends EntityResourceTest<TestSuite, CreateT
// We'll create tests cases for testSuite1
for (int i = 0; i < 5; i++) {
CreateTestCase createTestCase =
testCaseResourceTest
.createRequest(String.format("test_testSuite_2_%s_", test.getDisplayName()) + i)
.withTestSuite(executableTestSuite.getFullyQualifiedName());
testCaseResourceTest.createRequest(
String.format("test_testSuite_2_%s_", test.getDisplayName()) + i);
TestCase testCase =
testCaseResourceTest.createAndCheckEntity(createTestCase, ADMIN_AUTH_HEADERS);
testCases1.add(testCase.getEntityReference());

View File

@ -519,11 +519,9 @@ public class LineageResourceTest extends OpenMetadataApplicationTest {
CreateTestCase create6 = testCaseResourceTest.createRequest(test, 2);
create4
.withEntityLink(TABLE4_LINK.getLinkString())
.withTestSuite(testSuite4.getFullyQualifiedName())
.withTestDefinition(testDefinition.getFullyQualifiedName());
create6
.withEntityLink(TABLE6_LINK.getLinkString())
.withTestSuite(testSuite6.getFullyQualifiedName())
.withTestDefinition(testDefinition.getFullyQualifiedName());
TestCase testCase4 = testCaseResourceTest.createEntity(create4, ADMIN_AUTH_HEADERS);
TestCase testCase6 = testCaseResourceTest.createEntity(create6, ADMIN_AUTH_HEADERS);

View File

@ -26,10 +26,6 @@
"entityLink": {
"$ref": "../../type/basic.json#/definitions/entityLink"
},
"testSuite": {
"description": "Fully qualified name of the testSuite",
"$ref": "../../type/basic.json#/definitions/fullyQualifiedEntityName"
},
"parameterValues": {
"type": "array",
"items": {
@ -49,8 +45,16 @@
"description": "If the test definition supports it, use dynamic assertion to evaluate the test case.",
"type": "boolean",
"default": false
},
"tags": {
"description": "Tags for this test case. This is an inherited field from the parent entity and is not set directly on the test case.",
"type": "array",
"items": {
"$ref": "../../type/tagLabel.json"
},
"default": []
}
},
"required": ["name", "testDefinition", "entityLink", "testSuite"],
"required": ["name", "testDefinition", "entityLink"],
"additionalProperties": false
}

View File

@ -50,7 +50,6 @@ test.beforeAll(async ({ browser }) => {
{ name: 'allowedValues', value: '["gmail","yahoo","collate"]' },
],
testDefinition: 'columnValuesToBeInSet',
testSuite: testSuiteData?.['fullyQualifiedName'],
});
await afterAction();
});
@ -724,7 +723,6 @@ test('TestCase filters', PLAYWRIGHT_INGESTION_TAG_OBJ, async ({ page }) => {
const testCase2 = await filterTable1.createTestCase(apiContext, {
name: smilerNameTestCase[i],
entityLink: `<#E::table::${filterTable2Response?.['fullyQualifiedName']}>`,
testSuite: testSuite2Response?.['fullyQualifiedName'],
});
await filterTable1.addTestCaseResult(
apiContext,

View File

@ -324,7 +324,6 @@ export class TableClass extends EntityClass {
name: `pw-test-case-${uuid()}`,
entityLink: `<#E::table::${this.entityResponseData?.['fullyQualifiedName']}>`,
testDefinition: 'tableRowCountToBeBetween',
testSuite: this.testSuiteResponseData?.['fullyQualifiedName'],
parameterValues: [
{ name: 'minValue', value: 12 },
{ name: 'maxValue', value: 34 },

View File

@ -36,16 +36,11 @@ import { FormSubmitType } from '../../../enums/form.enum';
import { ProfilerDashboardType } from '../../../enums/table.enum';
import { OwnerType } from '../../../enums/user.enum';
import { CreateTestCase } from '../../../generated/api/tests/createTestCase';
import { CreateTestSuite } from '../../../generated/api/tests/createTestSuite';
import { TestCase } from '../../../generated/tests/testCase';
import { TestSuite } from '../../../generated/tests/testSuite';
import { useApplicationStore } from '../../../hooks/useApplicationStore';
import { useFqn } from '../../../hooks/useFqn';
import {
createExecutableTestSuite,
createTestCase,
getTestSuiteByName,
} from '../../../rest/testAPI';
import { createTestCase, getTestSuiteByName } from '../../../rest/testAPI';
import {
getEntityBreadcrumbs,
getEntityName,
@ -127,18 +122,6 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({
});
};
const createTestSuite = async () => {
const testSuite: CreateTestSuite = {
name: `${table.fullyQualifiedName}.testSuite`,
basicEntityReference: table.fullyQualifiedName,
owners,
};
const response = await createExecutableTestSuite(testSuite);
setTestSuiteData(response);
return response;
};
const fetchTestSuiteByFqn = async (fqn: string) => {
try {
const response = await getTestSuiteByName(fqn);
@ -158,17 +141,21 @@ const AddDataQualityTestV1: React.FC<AddDataQualityTestProps> = ({
setTestCaseData(data);
try {
const testSuite = isUndefined(testSuiteData)
? await createTestSuite()
: table.testSuite;
const testCasePayload: CreateTestCase = {
...data,
owners,
testSuite: testSuite?.fullyQualifiedName ?? '',
};
const testCaseResponse = await createTestCase(testCasePayload);
if (
testCaseResponse.testSuite.fullyQualifiedName &&
isUndefined(table.testSuite)
) {
await fetchTestSuiteByFqn(
testCaseResponse.testSuite.fullyQualifiedName
);
}
// Update current count when Create / Delete operation performed
await getResourceLimit('dataQuality', true, true);
setActiveServiceStep(2);

View File

@ -184,7 +184,6 @@ describe('TestCaseForm', () => {
name: 'dim_address_column_value_lengths_to_be_between_4B3B',
parameterValues: [],
testDefinition: 'columnValueLengthsToBeBetween',
testSuite: '',
});
});

View File

@ -143,14 +143,14 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
};
const getSelectedTestDefinition = useCallback(() => {
const testType = isEmpty(initialValue?.testSuite)
const testType = isEmpty(initialValue?.testDefinition)
? selectedTestType
: initialValue?.testSuite;
: initialValue?.testDefinition;
return testDefinitions.find(
(definition) => definition.fullyQualifiedName === testType
);
}, [initialValue?.testSuite, selectedTestType, testDefinitions]);
}, [initialValue?.testDefinition, selectedTestType, testDefinitions]);
const isComputeRowCountFieldVisible = useMemo(() => {
const selectedDefinition = getSelectedTestDefinition();
@ -188,7 +188,6 @@ const TestCaseForm: React.FC<TestCaseFormProps> = ({
),
testDefinition: value.testTypeId,
description: isEmpty(value.description) ? undefined : value.description,
testSuite: '',
...testCaseClassBase.getCreateTestCaseObject(value, selectedDefinition),
};
};

View File

@ -36,14 +36,15 @@ export interface CreateTestCase {
*/
owners?: EntityReference[];
parameterValues?: TestCaseParameterValue[];
/**
* Tags for this test case. This is an inherited field from the parent entity and is not set
* directly on the test case.
*/
tags?: TagLabel[];
/**
* Fully qualified name of the test definition.
*/
testDefinition: string;
/**
* Fully qualified name of the testSuite
*/
testSuite: string;
/**
* If the test definition supports it, use dynamic assertion to evaluate the test case.
*/
@ -121,3 +122,91 @@ export interface TestCaseParameterValue {
value?: string;
[property: string]: any;
}
/**
* This schema defines the type for labeling an entity with a Tag.
*/
export interface TagLabel {
/**
* Description for the tag label.
*/
description?: string;
/**
* Display Name that identifies this tag.
*/
displayName?: string;
/**
* Link to the tag resource.
*/
href?: string;
/**
* Label type describes how a tag label was applied. 'Manual' indicates the tag label was
* applied by a person. 'Derived' indicates a tag label was derived using the associated tag
* relationship (see Classification.json for more details). 'Propagated` indicates a tag
* label was propagated from upstream based on lineage. 'Automated' is used when a tool was
* used to determine the tag label.
*/
labelType: LabelType;
/**
* Name of the tag or glossary term.
*/
name?: string;
/**
* Label is from Tags or Glossary.
*/
source: TagSource;
/**
* 'Suggested' state is used when a tag label is suggested by users or tools. Owner of the
* entity must confirm the suggested labels before it is marked as 'Confirmed'.
*/
state: State;
style?: Style;
tagFQN: string;
}
/**
* Label type describes how a tag label was applied. 'Manual' indicates the tag label was
* applied by a person. 'Derived' indicates a tag label was derived using the associated tag
* relationship (see Classification.json for more details). 'Propagated` indicates a tag
* label was propagated from upstream based on lineage. 'Automated' is used when a tool was
* used to determine the tag label.
*/
export enum LabelType {
Automated = "Automated",
Derived = "Derived",
Generated = "Generated",
Manual = "Manual",
Propagated = "Propagated",
}
/**
* Label is from Tags or Glossary.
*/
export enum TagSource {
Classification = "Classification",
Glossary = "Glossary",
}
/**
* 'Suggested' state is used when a tag label is suggested by users or tools. Owner of the
* entity must confirm the suggested labels before it is marked as 'Confirmed'.
*/
export enum State {
Confirmed = "Confirmed",
Suggested = "Suggested",
}
/**
* UI Style is used to associate a color code and/or icon to entity to customize the look of
* that entity in UI.
*/
export interface Style {
/**
* Hex Color Code to mark an entity such as GlossaryTerm, Tag, Domain or Data Product.
*/
color?: string;
/**
* An icon to associate with GlossaryTerm, Tag, Domain or Data Product.
*/
iconURL?: string;
}