mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-08-05 15:48:03 +00:00
Fix : Activity Feed Not Showing Column-Level Metadata Changes (#22245)
* Fix : Activity Feed Not Showing Column-Level Metadata Changes * Add backend test * fix checkstyle --------- Co-authored-by: Sriharsha Chintalapani <harshach@users.noreply.github.com>
This commit is contained in:
parent
91ae7c4882
commit
9a65ae1a50
@ -15,6 +15,8 @@ package org.openmetadata.service.jdbi3;
|
|||||||
|
|
||||||
import static org.openmetadata.service.Entity.DASHBOARD_DATA_MODEL;
|
import static org.openmetadata.service.Entity.DASHBOARD_DATA_MODEL;
|
||||||
import static org.openmetadata.service.Entity.TABLE;
|
import static org.openmetadata.service.Entity.TABLE;
|
||||||
|
import static org.openmetadata.service.events.ChangeEventHandler.copyChangeEvent;
|
||||||
|
import static org.openmetadata.service.formatter.util.FormatterUtil.createChangeEventForEntity;
|
||||||
import static org.openmetadata.service.resources.tags.TagLabelUtil.addDerivedTags;
|
import static org.openmetadata.service.resources.tags.TagLabelUtil.addDerivedTags;
|
||||||
|
|
||||||
import jakarta.json.JsonPatch;
|
import jakarta.json.JsonPatch;
|
||||||
@ -22,11 +24,14 @@ import jakarta.ws.rs.core.SecurityContext;
|
|||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.openmetadata.schema.EntityInterface;
|
||||||
import org.openmetadata.schema.api.data.UpdateColumn;
|
import org.openmetadata.schema.api.data.UpdateColumn;
|
||||||
import org.openmetadata.schema.entity.data.DashboardDataModel;
|
import org.openmetadata.schema.entity.data.DashboardDataModel;
|
||||||
import org.openmetadata.schema.entity.data.Table;
|
import org.openmetadata.schema.entity.data.Table;
|
||||||
|
import org.openmetadata.schema.type.ChangeEvent;
|
||||||
import org.openmetadata.schema.type.Column;
|
import org.openmetadata.schema.type.Column;
|
||||||
import org.openmetadata.schema.type.EntityReference;
|
import org.openmetadata.schema.type.EntityReference;
|
||||||
|
import org.openmetadata.schema.type.EventType;
|
||||||
import org.openmetadata.schema.type.Include;
|
import org.openmetadata.schema.type.Include;
|
||||||
import org.openmetadata.schema.utils.JsonUtils;
|
import org.openmetadata.schema.utils.JsonUtils;
|
||||||
import org.openmetadata.service.Entity;
|
import org.openmetadata.service.Entity;
|
||||||
@ -36,6 +41,7 @@ import org.openmetadata.service.security.policyevaluator.OperationContext;
|
|||||||
import org.openmetadata.service.security.policyevaluator.ResourceContext;
|
import org.openmetadata.service.security.policyevaluator.ResourceContext;
|
||||||
import org.openmetadata.service.security.policyevaluator.ResourceContextInterface;
|
import org.openmetadata.service.security.policyevaluator.ResourceContextInterface;
|
||||||
import org.openmetadata.service.util.FullyQualifiedName;
|
import org.openmetadata.service.util.FullyQualifiedName;
|
||||||
|
import org.openmetadata.service.util.RestUtil;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ColumnRepository {
|
public class ColumnRepository {
|
||||||
@ -144,7 +150,9 @@ public class ColumnRepository {
|
|||||||
TABLE, parentEntityRef.getId(), null, ResourceContextInterface.Operation.PATCH);
|
TABLE, parentEntityRef.getId(), null, ResourceContextInterface.Operation.PATCH);
|
||||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||||
|
|
||||||
tableRepository.patch(uriInfo, parentEntityRef.getId(), user, jsonPatch);
|
RestUtil.PatchResponse<Table> patchResponse =
|
||||||
|
tableRepository.patch(uriInfo, parentEntityRef.getId(), user, jsonPatch);
|
||||||
|
triggerParentChangeEvent(patchResponse.entity(), user);
|
||||||
|
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
@ -211,7 +219,9 @@ public class ColumnRepository {
|
|||||||
ResourceContextInterface.Operation.PATCH);
|
ResourceContextInterface.Operation.PATCH);
|
||||||
authorizer.authorize(securityContext, operationContext, resourceContext);
|
authorizer.authorize(securityContext, operationContext, resourceContext);
|
||||||
|
|
||||||
dataModelRepository.patch(uriInfo, parentEntityRef.getId(), user, jsonPatch);
|
RestUtil.PatchResponse<DashboardDataModel> patchResponse =
|
||||||
|
dataModelRepository.patch(uriInfo, parentEntityRef.getId(), user, jsonPatch);
|
||||||
|
triggerParentChangeEvent(patchResponse.entity(), user);
|
||||||
|
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
@ -263,4 +273,13 @@ public class ColumnRepository {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void triggerParentChangeEvent(Object parent, String user) {
|
||||||
|
ChangeEvent changeEvent =
|
||||||
|
createChangeEventForEntity(user, EventType.ENTITY_UPDATED, (EntityInterface) parent);
|
||||||
|
Object entity = changeEvent.getEntity();
|
||||||
|
changeEvent = copyChangeEvent(changeEvent);
|
||||||
|
changeEvent.setEntity(JsonUtils.pojoToMaskedJson(entity));
|
||||||
|
Entity.getCollectionDAO().changeEventDAO().insert(JsonUtils.pojoToJson(changeEvent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ package org.openmetadata.service.resources.columns;
|
|||||||
|
|
||||||
import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
|
import static jakarta.ws.rs.core.Response.Status.NOT_FOUND;
|
||||||
import static jakarta.ws.rs.core.Response.Status.OK;
|
import static jakarta.ws.rs.core.Response.Status.OK;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@ -28,11 +29,16 @@ import jakarta.ws.rs.client.WebTarget;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
import org.junit.jupiter.api.TestInstance;
|
import org.junit.jupiter.api.TestInstance;
|
||||||
|
import org.openmetadata.schema.EntityInterface;
|
||||||
import org.openmetadata.schema.api.classification.CreateClassification;
|
import org.openmetadata.schema.api.classification.CreateClassification;
|
||||||
import org.openmetadata.schema.api.classification.CreateTag;
|
import org.openmetadata.schema.api.classification.CreateTag;
|
||||||
import org.openmetadata.schema.api.data.CreateDashboardDataModel;
|
import org.openmetadata.schema.api.data.CreateDashboardDataModel;
|
||||||
@ -52,6 +58,7 @@ import org.openmetadata.schema.entity.data.DatabaseSchema;
|
|||||||
import org.openmetadata.schema.entity.data.Glossary;
|
import org.openmetadata.schema.entity.data.Glossary;
|
||||||
import org.openmetadata.schema.entity.data.GlossaryTerm;
|
import org.openmetadata.schema.entity.data.GlossaryTerm;
|
||||||
import org.openmetadata.schema.entity.data.Table;
|
import org.openmetadata.schema.entity.data.Table;
|
||||||
|
import org.openmetadata.schema.entity.feed.Thread;
|
||||||
import org.openmetadata.schema.entity.services.DashboardService;
|
import org.openmetadata.schema.entity.services.DashboardService;
|
||||||
import org.openmetadata.schema.entity.services.DatabaseService;
|
import org.openmetadata.schema.entity.services.DatabaseService;
|
||||||
import org.openmetadata.schema.type.Column;
|
import org.openmetadata.schema.type.Column;
|
||||||
@ -59,11 +66,13 @@ import org.openmetadata.schema.type.ColumnConstraint;
|
|||||||
import org.openmetadata.schema.type.ColumnDataType;
|
import org.openmetadata.schema.type.ColumnDataType;
|
||||||
import org.openmetadata.schema.type.DataModelType;
|
import org.openmetadata.schema.type.DataModelType;
|
||||||
import org.openmetadata.schema.type.TagLabel;
|
import org.openmetadata.schema.type.TagLabel;
|
||||||
|
import org.openmetadata.service.Entity;
|
||||||
import org.openmetadata.service.OpenMetadataApplicationTest;
|
import org.openmetadata.service.OpenMetadataApplicationTest;
|
||||||
import org.openmetadata.service.resources.databases.DatabaseResourceTest;
|
import org.openmetadata.service.resources.databases.DatabaseResourceTest;
|
||||||
import org.openmetadata.service.resources.databases.DatabaseSchemaResourceTest;
|
import org.openmetadata.service.resources.databases.DatabaseSchemaResourceTest;
|
||||||
import org.openmetadata.service.resources.databases.TableResourceTest;
|
import org.openmetadata.service.resources.databases.TableResourceTest;
|
||||||
import org.openmetadata.service.resources.datamodels.DashboardDataModelResourceTest;
|
import org.openmetadata.service.resources.datamodels.DashboardDataModelResourceTest;
|
||||||
|
import org.openmetadata.service.resources.feeds.FeedResourceTest;
|
||||||
import org.openmetadata.service.resources.glossary.GlossaryResourceTest;
|
import org.openmetadata.service.resources.glossary.GlossaryResourceTest;
|
||||||
import org.openmetadata.service.resources.glossary.GlossaryTermResourceTest;
|
import org.openmetadata.service.resources.glossary.GlossaryTermResourceTest;
|
||||||
import org.openmetadata.service.resources.services.DashboardServiceResourceTest;
|
import org.openmetadata.service.resources.services.DashboardServiceResourceTest;
|
||||||
@ -71,6 +80,7 @@ import org.openmetadata.service.resources.services.DatabaseServiceResourceTest;
|
|||||||
import org.openmetadata.service.resources.tags.ClassificationResourceTest;
|
import org.openmetadata.service.resources.tags.ClassificationResourceTest;
|
||||||
import org.openmetadata.service.resources.tags.TagResourceTest;
|
import org.openmetadata.service.resources.tags.TagResourceTest;
|
||||||
import org.openmetadata.service.util.FullyQualifiedName;
|
import org.openmetadata.service.util.FullyQualifiedName;
|
||||||
|
import org.openmetadata.service.util.ResultList;
|
||||||
import org.openmetadata.service.util.TestUtils;
|
import org.openmetadata.service.util.TestUtils;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -1150,6 +1160,151 @@ class ColumnResourceTest extends OpenMetadataApplicationTest {
|
|||||||
assertTrue(hasDerivedTag, "Derived tag should be present in persisted data");
|
assertTrue(hasDerivedTag, "Derived tag should be present in persisted data");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_tableColumnChangeEvents() throws IOException {
|
||||||
|
// Create a new table specifically for this test to avoid feed pollution
|
||||||
|
TableResourceTest isolatedTableTest = new TableResourceTest();
|
||||||
|
String testName = "tableColumnFeedTest_" + UUID.randomUUID();
|
||||||
|
|
||||||
|
DatabaseSchema schema =
|
||||||
|
new DatabaseSchemaResourceTest()
|
||||||
|
.createEntity(
|
||||||
|
new CreateDatabaseSchema()
|
||||||
|
.withName(testName)
|
||||||
|
.withDatabase(table.getDatabase().getFullyQualifiedName()),
|
||||||
|
ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
List<Column> columns =
|
||||||
|
Arrays.asList(
|
||||||
|
new Column().withName("id").withDataType(ColumnDataType.INT),
|
||||||
|
new Column()
|
||||||
|
.withName("description_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255),
|
||||||
|
new Column()
|
||||||
|
.withName("displayname_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255),
|
||||||
|
new Column()
|
||||||
|
.withName("tags_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255));
|
||||||
|
|
||||||
|
CreateTable createIsolatedTable =
|
||||||
|
new CreateTable()
|
||||||
|
.withName(testName)
|
||||||
|
.withDatabaseSchema(schema.getFullyQualifiedName())
|
||||||
|
.withColumns(columns);
|
||||||
|
|
||||||
|
Table isolatedTable = isolatedTableTest.createEntity(createIsolatedTable, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// 1. Test description change in feed
|
||||||
|
String descriptionColFQN = isolatedTable.getFullyQualifiedName() + ".description_col";
|
||||||
|
UpdateColumn updateDescription =
|
||||||
|
new UpdateColumn().withDescription("Test description for feed verification");
|
||||||
|
Column updatedDescriptionCol = updateColumnByFQN(descriptionColFQN, updateDescription);
|
||||||
|
assertEquals("Test description for feed verification", updatedDescriptionCol.getDescription());
|
||||||
|
|
||||||
|
// Verify description change appears in activity feed
|
||||||
|
verifyColumnChangeEventInFeed(
|
||||||
|
isolatedTable, "description_col", List.of("description"), Entity.TABLE);
|
||||||
|
|
||||||
|
// 2. Test tags/terms change in feed
|
||||||
|
String tagsColFQN = isolatedTable.getFullyQualifiedName() + ".tags_col";
|
||||||
|
|
||||||
|
// Create tag label for classification tag
|
||||||
|
TagLabel classificationTag =
|
||||||
|
new TagLabel()
|
||||||
|
.withTagFQN(personalDataTag.getFullyQualifiedName())
|
||||||
|
.withSource(TagLabel.TagSource.CLASSIFICATION);
|
||||||
|
|
||||||
|
// Create tag label for glossary term
|
||||||
|
TagLabel glossaryTerm =
|
||||||
|
new TagLabel()
|
||||||
|
.withTagFQN(businessTermsGlossaryTerm.getFullyQualifiedName())
|
||||||
|
.withSource(TagLabel.TagSource.GLOSSARY);
|
||||||
|
|
||||||
|
// Update column with tags
|
||||||
|
UpdateColumn updateTags = new UpdateColumn().withTags(List.of(classificationTag, glossaryTerm));
|
||||||
|
Column updatedTagsCol = updateColumnByFQN(tagsColFQN, updateTags);
|
||||||
|
|
||||||
|
// Verify tags were added
|
||||||
|
assertEquals(2, updatedTagsCol.getTags().size());
|
||||||
|
|
||||||
|
// Verify tags change appears in activity feed
|
||||||
|
verifyColumnChangeEventInFeed(isolatedTable, "tags_col", List.of("tags"), Entity.TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void test_dashboardDataModelColumnChangeEvents() throws IOException {
|
||||||
|
// Create a new dashboard data model specifically for this test to avoid feed pollution
|
||||||
|
DashboardDataModelResourceTest isolatedModelTest = new DashboardDataModelResourceTest();
|
||||||
|
String testName = "dataModelColumnFeedTest_" + UUID.randomUUID();
|
||||||
|
|
||||||
|
// Create test data model with simple columns
|
||||||
|
List<Column> columns =
|
||||||
|
Arrays.asList(
|
||||||
|
new Column().withName("metric_col").withDataType(ColumnDataType.NUMERIC),
|
||||||
|
new Column()
|
||||||
|
.withName("description_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255),
|
||||||
|
new Column()
|
||||||
|
.withName("displayname_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255),
|
||||||
|
new Column()
|
||||||
|
.withName("tags_col")
|
||||||
|
.withDataType(ColumnDataType.VARCHAR)
|
||||||
|
.withDataLength(255));
|
||||||
|
|
||||||
|
CreateDashboardDataModel createIsolatedModel =
|
||||||
|
new CreateDashboardDataModel()
|
||||||
|
.withName(testName)
|
||||||
|
.withService(dashboardDataModel.getService().getFullyQualifiedName())
|
||||||
|
.withDataModelType(DataModelType.MetabaseDataModel)
|
||||||
|
.withColumns(columns);
|
||||||
|
|
||||||
|
DashboardDataModel isolatedModel =
|
||||||
|
isolatedModelTest.createEntity(createIsolatedModel, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
// 1. Test description change in feed
|
||||||
|
String descriptionColFQN = isolatedModel.getFullyQualifiedName() + ".description_col";
|
||||||
|
UpdateColumn updateDescription =
|
||||||
|
new UpdateColumn().withDescription("Test data model description for feed verification");
|
||||||
|
Column updatedDescriptionCol =
|
||||||
|
updateColumnByFQN(descriptionColFQN, updateDescription, "dashboardDataModel");
|
||||||
|
assertEquals(
|
||||||
|
"Test data model description for feed verification",
|
||||||
|
updatedDescriptionCol.getDescription());
|
||||||
|
|
||||||
|
// 2. Test tags/terms change in feed
|
||||||
|
String tagsColFQN = isolatedModel.getFullyQualifiedName() + ".tags_col";
|
||||||
|
|
||||||
|
// Create tag label for classification tag
|
||||||
|
TagLabel classificationTag =
|
||||||
|
new TagLabel()
|
||||||
|
.withTagFQN(businessMetricsTag.getFullyQualifiedName())
|
||||||
|
.withSource(TagLabel.TagSource.CLASSIFICATION);
|
||||||
|
|
||||||
|
// Create tag label for glossary term
|
||||||
|
TagLabel glossaryTerm =
|
||||||
|
new TagLabel()
|
||||||
|
.withTagFQN(technicalTermsGlossaryTerm.getFullyQualifiedName())
|
||||||
|
.withSource(TagLabel.TagSource.GLOSSARY);
|
||||||
|
|
||||||
|
// Update column with tags
|
||||||
|
UpdateColumn updateTags = new UpdateColumn().withTags(List.of(classificationTag, glossaryTerm));
|
||||||
|
Column updatedTagsCol = updateColumnByFQN(tagsColFQN, updateTags, "dashboardDataModel");
|
||||||
|
|
||||||
|
// Verify tags were added
|
||||||
|
assertEquals(2, updatedTagsCol.getTags().size());
|
||||||
|
|
||||||
|
// Verify tags change appears in activity feed
|
||||||
|
verifyColumnChangeEventInFeed(
|
||||||
|
isolatedModel, "tags_col", List.of("tags"), Entity.DASHBOARD_DATA_MODEL);
|
||||||
|
}
|
||||||
|
|
||||||
private Column updateColumnByFQN(String columnFQN, UpdateColumn updateColumn, String entityType)
|
private Column updateColumnByFQN(String columnFQN, UpdateColumn updateColumn, String entityType)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
WebTarget target =
|
WebTarget target =
|
||||||
@ -1160,4 +1315,75 @@ class ColumnResourceTest extends OpenMetadataApplicationTest {
|
|||||||
private Column updateColumnByFQN(String columnFQN, UpdateColumn updateColumn) throws IOException {
|
private Column updateColumnByFQN(String columnFQN, UpdateColumn updateColumn) throws IOException {
|
||||||
return updateColumnByFQN(columnFQN, updateColumn, "table");
|
return updateColumnByFQN(columnFQN, updateColumn, "table");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void verifyColumnChangeEventInFeed(
|
||||||
|
EntityInterface parentEntity,
|
||||||
|
String columnName,
|
||||||
|
List<String> expectedFields,
|
||||||
|
String entityType)
|
||||||
|
throws IOException {
|
||||||
|
|
||||||
|
String entityLink =
|
||||||
|
String.format("<#E::%s::%s>", entityType, parentEntity.getFullyQualifiedName());
|
||||||
|
|
||||||
|
FeedResourceTest feedResourceTest = new FeedResourceTest();
|
||||||
|
|
||||||
|
AtomicReference<ResultList<Thread>> threadsRef = new AtomicReference<>();
|
||||||
|
|
||||||
|
await()
|
||||||
|
.pollInterval(2, TimeUnit.SECONDS)
|
||||||
|
.atMost(90, TimeUnit.SECONDS)
|
||||||
|
.until(
|
||||||
|
() -> {
|
||||||
|
// Poll for threads related to this entity
|
||||||
|
ResultList<Thread> fetchedThreads =
|
||||||
|
feedResourceTest.listThreads(entityLink, null, ADMIN_AUTH_HEADERS);
|
||||||
|
|
||||||
|
if (fetchedThreads == null || fetchedThreads.getData().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any thread mentions our column
|
||||||
|
boolean found =
|
||||||
|
fetchedThreads.getData().stream()
|
||||||
|
.anyMatch(
|
||||||
|
thread ->
|
||||||
|
thread.getMessage() != null
|
||||||
|
&& thread.getMessage().contains(columnName));
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
// Store the result only when we find a match
|
||||||
|
threadsRef.set(fetchedThreads);
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
ResultList<Thread> threads = threadsRef.get();
|
||||||
|
|
||||||
|
// Verify feed contains our column update
|
||||||
|
assertNotNull(threads);
|
||||||
|
assertFalse(threads.getData().isEmpty());
|
||||||
|
|
||||||
|
Optional<Thread> columnUpdateThread =
|
||||||
|
threads.getData().stream()
|
||||||
|
.filter(thread -> thread.getMessage().contains(columnName))
|
||||||
|
.findFirst();
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
columnUpdateThread.isPresent(),
|
||||||
|
"No activity feed entry found for column update: " + columnName);
|
||||||
|
|
||||||
|
Thread thread = columnUpdateThread.get();
|
||||||
|
|
||||||
|
// Verify the thread message mentions the expected fields
|
||||||
|
boolean foundFieldReferences =
|
||||||
|
expectedFields.stream()
|
||||||
|
.allMatch(field -> thread.getMessage().toLowerCase().contains(field.toLowerCase()));
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
foundFieldReferences,
|
||||||
|
"Activity feed doesn't contain references to all expected fields: "
|
||||||
|
+ String.join(", ", expectedFields));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user