updated rule evealutor tests (#9711)

* updated rule evealutor tests

* Update - review comments
This commit is contained in:
Mohit Yadav 2023-01-13 14:43:55 +05:30 committed by GitHub
parent c3197beaf4
commit bbfb50f298
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 165 additions and 119 deletions

View File

@ -51,12 +51,12 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has source as mentioned in condition",
examples = {"matchAnySource('bot', 'user')"},
paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchAnySource(String... originEntity) {
if (changeEvent == null) {
public boolean matchAnySource(String... originEntities) {
if (changeEvent == null || changeEvent.getEntityType() == null) {
return false;
}
String changeEventEntity = changeEvent.getEntityType();
for (String entityType : originEntity) {
for (String entityType : originEntities) {
if (changeEventEntity.equals(entityType)) {
return true;
}
@ -74,14 +74,7 @@ public class AlertsRuleEvaluator {
if (changeEvent == null || changeEvent.getEntity() == null) {
return false;
}
Class<? extends EntityInterface> entityClass = Entity.getEntityClassFromType(changeEvent.getEntityType());
EntityInterface entity;
if (changeEvent.getEntity() instanceof String) {
entity = JsonUtils.readValue((String) changeEvent.getEntity(), entityClass);
} else {
entity = JsonUtils.convertValue(changeEvent.getEntity(), entityClass);
}
EntityInterface entity = getEntity(changeEvent);
EntityReference ownerReference = entity.getOwner();
if (ownerReference != null) {
if (USER.equals(ownerReference.getType())) {
@ -109,11 +102,11 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has following entityName from the List.",
examples = {"matchAnyEntityFqn('Name1', 'Name')"},
paramInputType = ALL_INDEX_ELASTIC_SEARCH)
public boolean matchAnyEntityFqn(String... entityNames) {
public boolean matchAnyEntityFqn(String... entityNames) throws IOException {
if (changeEvent == null || changeEvent.getEntity() == null) {
return false;
}
EntityInterface entity = (EntityInterface) changeEvent.getEntity();
EntityInterface entity = getEntity(changeEvent);
for (String name : entityNames) {
if (entity.getFullyQualifiedName().equals(name)) {
return true;
@ -128,11 +121,11 @@ public class AlertsRuleEvaluator {
description = "Returns true if the change event entity being accessed has following entityId from the List.",
examples = {"matchAnyEntityId('uuid1', 'uuid2')"},
paramInputType = ALL_INDEX_ELASTIC_SEARCH)
public boolean matchAnyEntityId(String... entityIds) {
public boolean matchAnyEntityId(String... entityIds) throws IOException {
if (changeEvent == null || changeEvent.getEntity() == null) {
return false;
}
EntityInterface entity = (EntityInterface) changeEvent.getEntity();
EntityInterface entity = getEntity(changeEvent);
for (String id : entityIds) {
if (entity.getId().equals(UUID.fromString(id))) {
return true;
@ -148,7 +141,7 @@ public class AlertsRuleEvaluator {
examples = {"matchAnyEventType('entityCreated', 'entityUpdated', 'entityDeleted', 'entitySoftDeleted')"},
paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchAnyEventType(String... eventTypesList) {
if (changeEvent == null || changeEvent.getEntity() == null) {
if (changeEvent == null || changeEvent.getEventType() == null) {
return false;
}
String eventType = changeEvent.getEventType().toString();
@ -167,7 +160,7 @@ public class AlertsRuleEvaluator {
examples = {"matchTestResult('Success', 'Failed', 'Aborted')"},
paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchTestResult(String... testResults) {
if (changeEvent == null || changeEvent.getEntity() == null) {
if (changeEvent == null || changeEvent.getChangeDescription() == null) {
return false;
}
if (!changeEvent.getEntityType().equals(TEST_CASE)) {
@ -195,7 +188,7 @@ public class AlertsRuleEvaluator {
examples = {"matchUpdatedBy('user1', 'user2')"},
paramInputType = READ_FROM_PARAM_CONTEXT)
public boolean matchUpdatedBy(String... updatedByUserList) {
if (changeEvent == null || changeEvent.getEntity() == null) {
if (changeEvent == null || changeEvent.getUserName() == null) {
return false;
}
String entityUpdatedBy = changeEvent.getUserName();
@ -214,7 +207,7 @@ public class AlertsRuleEvaluator {
examples = {"matchAnyFieldChange('fieldName1', 'fieldName')"},
paramInputType = NOT_REQUIRED)
public boolean matchAnyFieldChange(String... fieldChangeUpdate) {
if (changeEvent == null || changeEvent.getEntity() == null) {
if (changeEvent == null || changeEvent.getChangeDescription() == null) {
return false;
}
Set<String> fields = ChangeEventParser.getUpdatedField(changeEvent);
@ -225,4 +218,15 @@ public class AlertsRuleEvaluator {
}
return false;
}
private EntityInterface getEntity(ChangeEvent event) throws IOException {
Class<? extends EntityInterface> entityClass = Entity.getEntityClassFromType(event.getEntityType());
EntityInterface entity;
if (event.getEntity() instanceof String) {
entity = JsonUtils.readValue((String) event.getEntity(), entityClass);
} else {
entity = JsonUtils.convertValue(event.getEntity(), entityClass);
}
return entity;
}
}

View File

@ -3,138 +3,180 @@ package org.openmetadata.service.alerts;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openmetadata.service.security.policyevaluator.CompiledRule.parseExpression;
import static org.openmetadata.service.util.TestUtils.ADMIN_AUTH_HEADERS;
import java.util.ArrayList;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.junit.jupiter.api.TestInfo;
import org.openmetadata.schema.api.data.CreateTable;
import org.openmetadata.schema.entity.data.Table;
import org.openmetadata.schema.entity.teams.Team;
import org.openmetadata.schema.entity.teams.User;
import org.openmetadata.schema.tests.type.TestCaseResult;
import org.openmetadata.schema.tests.type.TestCaseStatus;
import org.openmetadata.schema.type.ChangeDescription;
import org.openmetadata.schema.type.ChangeEvent;
import org.openmetadata.schema.type.EntityReference;
import org.openmetadata.schema.type.TagLabel;
import org.openmetadata.schema.type.Column;
import org.openmetadata.schema.type.ColumnDataType;
import org.openmetadata.schema.type.EventType;
import org.openmetadata.schema.type.FieldChange;
import org.openmetadata.service.Entity;
import org.openmetadata.service.jdbi3.CollectionDAO.TeamDAO;
import org.openmetadata.service.jdbi3.CollectionDAO.UserDAO;
import org.openmetadata.service.jdbi3.TableRepository;
import org.openmetadata.service.jdbi3.TeamRepository;
import org.openmetadata.service.jdbi3.UserRepository;
import org.openmetadata.service.security.policyevaluator.ResourceContext;
import org.openmetadata.service.OpenMetadataApplicationTest;
import org.openmetadata.service.resources.EntityResourceTest;
import org.openmetadata.service.resources.databases.TableResourceTest;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.support.StandardEvaluationContext;
class AlertsRuleEvaluatorTest {
private static Table table;
private static User user;
private static EvaluationContext evaluationContext;
class AlertsRuleEvaluatorTest extends OpenMetadataApplicationTest {
private static TableResourceTest tableResourceTest;
@BeforeAll
public static void setup() {
Entity.registerEntity(User.class, Entity.USER, Mockito.mock(UserDAO.class), Mockito.mock(UserRepository.class));
Entity.registerEntity(Team.class, Entity.TEAM, Mockito.mock(TeamDAO.class), Mockito.mock(TeamRepository.class));
table = new Table().withName("table");
user = new User().withId(UUID.randomUUID()).withName("user");
ResourceContext resourceContext =
ResourceContext.builder()
.resource("table")
.entity(table)
.entityRepository(Mockito.mock(TableRepository.class))
.build();
// SubjectContext subjectContext = new SubjectContext(user);
// RuleEvaluator ruleEvaluator = new RuleEvaluator(null, subjectContext, resourceContext);
ChangeEvent event = new ChangeEvent();
// evaluationContext = new StandardEvaluationContext(ruleEvaluator);
public static void setup(TestInfo test) throws URISyntaxException, IOException {
tableResourceTest = new TableResourceTest();
tableResourceTest.setup(test);
}
@Test
void test_noOwner() {
// Set no owner to the entity and test noOwner method
table.setOwner(null);
assertTrue(evaluateExpression("noOwner()"));
assertFalse(evaluateExpression("!noOwner()"));
// Set owner to the entity and test noOwner method
table.setOwner(new EntityReference().withId(UUID.randomUUID()).withType(Entity.USER));
assertFalse(evaluateExpression("noOwner()"));
assertTrue(evaluateExpression("!noOwner()"));
void test_matchAnySource() {
// Create a change Event with Entity Type and test for source in list and not in list
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType("alert");
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchAnySource('alert')", evaluationContext));
assertFalse(evaluateExpression("matchAnySource('bot')", evaluationContext));
}
@Test
void test_isOwner() {
// Table owner is a different user (random ID) and hence isOwner returns false
table.setOwner(new EntityReference().withId(UUID.randomUUID()).withType(Entity.USER).withName("otherUser"));
assertFalse(evaluateExpression("isOwner()"));
assertTrue(evaluateExpression("!isOwner()"));
void test_matchAnyOwnerName(TestInfo test) throws IOException {
// Create Table Entity
List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
CreateTable create =
tableResourceTest.createRequest(test).withColumns(columns).withOwner(EntityResourceTest.USER1_REF);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// Table owner is same as the user in subjectContext and hence isOwner returns true
table.setOwner(new EntityReference().withId(user.getId()).withType(Entity.USER).withName(user.getName()));
assertTrue(evaluateExpression("isOwner()"));
assertFalse(evaluateExpression("!isOwner()"));
// Create a change Event with the Entity Table
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType(Entity.TABLE);
changeEvent.setEntity(createdTable);
// noOwner() || isOwner() - with noOwner being true and isOwner false
table.setOwner(null);
assertTrue(evaluateExpression("noOwner() || isOwner()"));
assertFalse(evaluateExpression("!noOwner() && !isOwner()"));
// noOwner() || isOwner() - with noOwner is false and isOwner true
table.setOwner(new EntityReference().withId(user.getId()).withType(Entity.USER).withName(user.getName()));
assertTrue(evaluateExpression("noOwner() || isOwner()"));
assertFalse(evaluateExpression("!noOwner() && !isOwner()"));
// Test Owner Name Present in list and not present in list
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(
evaluateExpression("matchAnyOwnerName('" + EntityResourceTest.USER1.getName() + "')", evaluationContext));
assertFalse(evaluateExpression("matchAnyOwnerName('tempName')", evaluationContext));
}
@Test
void test_matchAllTags() {
table.withTags(getTags("tag1", "tag2", "tag3"));
void test_matchAnyEntityFqn(TestInfo test) throws IOException {
// Create Table Entity
List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
CreateTable create = tableResourceTest.createRequest(test).withColumns(columns);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// All tags present
assertTrue(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag3')"));
assertFalse(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag3')"));
assertTrue(evaluateExpression("matchAllTags('tag1', 'tag2')"));
assertFalse(evaluateExpression("!matchAllTags('tag1', 'tag2')"));
assertTrue(evaluateExpression("matchAllTags('tag1')"));
assertFalse(evaluateExpression("!matchAllTags('tag1')"));
// Create a change Event with the Entity Table
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType(Entity.TABLE);
changeEvent.setEntity(createdTable);
// Tag 'tag4' is missing
assertFalse(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag3', 'tag4')"));
assertTrue(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag3', 'tag4')"));
assertFalse(evaluateExpression("matchAllTags('tag1', 'tag2', 'tag4')"));
assertTrue(evaluateExpression("!matchAllTags('tag1', 'tag2', 'tag4')"));
assertFalse(evaluateExpression("matchAllTags('tag2', 'tag4')"));
assertTrue(evaluateExpression("!matchAllTags('tag2', 'tag4')"));
assertFalse(evaluateExpression("matchAllTags('tag4')"));
assertTrue(evaluateExpression("!matchAllTags('tag4')"));
// Test Entity Fqn in List of match and not present in list
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
String fqn = createdTable.getFullyQualifiedName();
assertTrue(evaluateExpression("matchAnyEntityFqn('" + fqn + "')", evaluationContext));
assertFalse(evaluateExpression("matchAnyEntityFqn('testFQN1')", evaluationContext));
}
@Test
void test_matchAnyTag() {
table.withTags(getTags("tag1", "tag2", "tag3"));
void test_matchAnyEntityId(TestInfo test) throws IOException {
// Create Table Entity
List<Column> columns = List.of(TableResourceTest.getColumn("c1", ColumnDataType.INT, null));
CreateTable create = tableResourceTest.createRequest(test).withColumns(columns);
Table createdTable = tableResourceTest.createAndCheckEntity(create, ADMIN_AUTH_HEADERS);
// Tag is present
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag3', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag3', 'tag4')"));
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag4')"));
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag2', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag2', 'tag4')"));
assertTrue(evaluateExpression("matchAnyTag('tag1', 'tag4')"));
assertFalse(evaluateExpression("!matchAnyTag('tag1', 'tag4')"));
// Create a change Event with Table Entity and Type
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType(Entity.TABLE);
changeEvent.setEntity(createdTable);
// Tag `tag4` is not present
assertFalse(evaluateExpression("matchAnyTag('tag4')"));
assertTrue(evaluateExpression("!matchAnyTag('tag4')"));
// Test Entity Id in List of match and not present in list
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
String id = createdTable.getId().toString();
assertTrue(evaluateExpression("matchAnyEntityId('" + id + "')", evaluationContext));
assertFalse(evaluateExpression("matchAnyEntityId('" + UUID.randomUUID() + "')", evaluationContext));
}
private Boolean evaluateExpression(String condition) {
@Test
void test_matchAnyEventType() {
// Create a change Event with EventType
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEventType(EventType.ENTITY_CREATED);
// Check if eventType present in list or absent from the list
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchAnyEventType('entityCreated')", evaluationContext));
assertFalse(evaluateExpression("matchAnyEventType('entityUpdated')", evaluationContext));
}
@Test
void test_matchTestResult() {
// Create a change Description with Test Result Field
ChangeDescription changeDescription = new ChangeDescription();
changeDescription.setFieldsUpdated(
List.of(
new FieldChange()
.withName("testCaseResult")
.withOldValue("test1")
.withNewValue(new TestCaseResult().withTestCaseStatus(TestCaseStatus.Success))));
// Create a change event with Test Case and Test Result Change Description
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setEntityType(Entity.TEST_CASE);
changeEvent.setChangeDescription(changeDescription);
// Test If Test Result status matches in list and if status not matches
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchTestResult('Success')", evaluationContext));
assertFalse(evaluateExpression("matchTestResult('Failed')", evaluationContext));
}
@Test
void test_matchUpdatedBy() {
// Create a change Event with updatedBy username
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setUserName("test");
// Test if the username is in list or not
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchUpdatedBy('test')", evaluationContext));
assertFalse(evaluateExpression("matchUpdatedBy('test1')", evaluationContext));
}
@Test
void test_matchAnyFieldChange() {
// Create a change Event with some Change Description and Field Change
ChangeDescription changeDescription = new ChangeDescription();
changeDescription.setFieldsUpdated(
List.of(new FieldChange().withName("test").withOldValue("test1").withNewValue("test2")));
ChangeEvent changeEvent = new ChangeEvent();
changeEvent.setChangeDescription(changeDescription);
// Test if the updated field matches from the list or not
AlertsRuleEvaluator alertsRuleEvaluator = new AlertsRuleEvaluator(changeEvent);
EvaluationContext evaluationContext = new StandardEvaluationContext(alertsRuleEvaluator);
assertTrue(evaluateExpression("matchAnyFieldChange('test')", evaluationContext));
assertFalse(evaluateExpression("matchAnyFieldChange('temp')", evaluationContext));
}
private Boolean evaluateExpression(String condition, EvaluationContext evaluationContext) {
return parseExpression(condition).getValue(evaluationContext, Boolean.class);
}
private List<TagLabel> getTags(String... tags) {
List<TagLabel> tagLabels = new ArrayList<>();
for (String tag : tags) {
tagLabels.add(new TagLabel().withTagFQN(tag));
}
return tagLabels;
}
}