Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,8 @@ public static ReindexingConfiguration from(EventPublisherJob jobData) {
DEFAULT_FIELD_FETCH_THREADS,
DEFAULT_DOC_BUILD_THREADS,
DEFAULT_STATS_INTERVAL_MS,
Boolean.TRUE.equals(jobData.getRecreateIndex()),
// Always run in recreate since it's zero downtime
true,
Comment thread
harshach marked this conversation as resolved.
Outdated
Comment thread
harshach marked this conversation as resolved.
Outdated
Comment thread
mohityadav766 marked this conversation as resolved.
Outdated
Boolean.TRUE.equals(jobData.getAutoTune()),
Boolean.TRUE.equals(jobData.getUseDistributedIndexing()),
Boolean.TRUE.equals(jobData.getForce()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,8 @@ public ResultList<T> listFromSearchWithOffset(
searchListFilter, limit, offset, entityType, searchSortFilter, q, queryString);
total = results.getTotal();
for (Map<String, Object> json : results.getResults()) {
T entity = setFieldsInternal(JsonUtils.readOrConvertValue(json, entityClass), fields);
T entity =
setFieldsInternal(JsonUtils.readOrConvertValueLenient(json, entityClass), fields);
try {
Comment thread
mohityadav766 marked this conversation as resolved.
Comment thread
mohityadav766 marked this conversation as resolved.
setInheritedFields(entity);
} catch (RuntimeException e) {
Expand Down Expand Up @@ -526,7 +527,7 @@ public ResultList<T> listLatestFromSearch(
Map<String, Object> source = extractAndFilterSource(hit);
T entity =
setFieldsInternal(
JsonUtils.readOrConvertValue(source, entityClass), fields);
JsonUtils.readOrConvertValueLenient(source, entityClass), fields);
if (entity != null) {
try {
setInheritedFields(entity);
Expand Down Expand Up @@ -684,7 +685,7 @@ public T latestFromSearch(EntityUtil.Fields fields, SearchListFilter searchListF
SearchResultListMapper results =
searchRepository.listWithOffset(searchListFilter, 1, 0, entityType, searchSortFilter, q);
for (Map<String, Object> json : results.getResults()) {
T entity = setFieldsInternal(JsonUtils.readOrConvertValue(json, entityClass), fields);
T entity = setFieldsInternal(JsonUtils.readOrConvertValueLenient(json, entityClass), fields);
setInheritedFields(entity);
clearFieldsInternal(entity, fields);
return entity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public Set<String> getRequiredReindexFields() {
fields.add("testSuite");
fields.add("testSuites");
fields.add("testDefinition");
fields.add(Entity.TEST_CASE_RESULT);
fields.add("incidentId");
return java.util.Collections.unmodifiableSet(fields);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,11 @@ void reindexFieldsIncludeKnownOverrides() {
assertTrue(testCaseFields.contains("testSuite"));
assertTrue(testCaseFields.contains("testSuites"));
assertTrue(testCaseFields.contains("testDefinition"));
// Regression: testCaseResult/incidentId are stripped from storage JSON and
// only fetched by setFieldsInBulk when explicitly requested. Reindex without
// them produces docs missing testCaseStatus, blanking statuses in the UI.
assertTrue(testCaseFields.contains(Entity.TEST_CASE_RESULT));
assertTrue(testCaseFields.contains("incidentId"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
Expand Down Expand Up @@ -144,6 +145,28 @@ void testBuildSearchIndexDoc_endToEnd_hasCommonAndTagFields() {
assertNotNull(result.get("originEntityFQN"));
}

@Test
void testRequiredReindexFields_includesTestCaseResultAndIncidentId() {
// Regression test for the 1.12.7 reindex bug: testCaseResult and incidentId
// are stripped from the storage JSON and only loaded by
// TestCaseRepository.setFieldsInBulk when present in the requested field
// set. If they are not in getRequiredReindexFields(), the reindexer writes
// a doc with no testCaseStatus and the UI/search shows test cases with no
// status until a per-case write re-populates them.
TestCase tc = new TestCase().withId(UUID.randomUUID()).withName("tc");
Set<String> required = new TestCaseIndex(tc).getRequiredReindexFields();

assertTrue(
required.contains(Entity.TEST_CASE_RESULT),
"TestCaseIndex.getRequiredReindexFields() must include 'testCaseResult'");
assertTrue(
required.contains("incidentId"),
"TestCaseIndex.getRequiredReindexFields() must include 'incidentId'");
assertTrue(required.contains("testSuite"));
assertTrue(required.contains("testSuites"));
assertTrue(required.contains("testDefinition"));
}

@Test
void testBuildSearchIndexDocInternal_testDefinitionNotFound() {
UUID testDefId = UUID.randomUUID();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright 2026 Collate.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* Regression for the 1.12.7 selective-reindex bug
* (https://github.com/open-metadata/OpenMetadata/pull/27723):
* TestCaseIndex.getRequiredReindexFields() omitted `testCaseResult` and
* `incidentId`, both of which are stripped from the storage JSON. On reindex,
* TestCaseRepository.setFieldsInBulk skipped fetching them and the resulting
* ES doc had no `testCaseStatus` — wiping status from search/UI until a
* per-case write re-populated it.
*
* This test creates a test case, writes a result, forces an entity reindex
* with `recreate=true` (delete + re-add), and asserts the status survives.
*/

import test, { expect } from '@playwright/test';
import { TableClass } from '../../../support/entity/TableClass';
import { createNewPage } from '../../../utils/common';

test.use({ storageState: 'playwright/.auth/admin.json' });

const TEST_CASE_STATUS = 'Failed' as const;

test('Test case status survives a full entity reindex', async ({ browser }) => {
const { apiContext, afterAction } = await createNewPage(browser);

const table = new TableClass();

try {
await table.create(apiContext);

const testCase = await table.createTestCase(apiContext);
const testCaseFqn = testCase.fullyQualifiedName as string;
const testCaseId = testCase.id as string;

await table.addTestCaseResult(apiContext, testCaseFqn, {
result: 'Reindex regression check',
testCaseStatus: TEST_CASE_STATUS,
timestamp: Date.now(),
});

// Wait for the search doc to settle and assert the status is indexed.
await expect
.poll(
async () => {
const res = await apiContext.get(
`/api/v1/search/query?q=fullyQualifiedName:%22${encodeURIComponent(
testCaseFqn
)}%22&index=test_case_search_index`
);
if (res.status() !== 200) {
return undefined;
}
const body = await res.json();
const hits = body?.hits?.hits ?? [];
return hits[0]?._source?.testCaseResult?.testCaseStatus;
},
{
message:
'pre-reindex: test case search doc must include testCaseResult.testCaseStatus',
timeout: 30_000,
}
Comment thread
mohityadav766 marked this conversation as resolved.
)
.toBe(TEST_CASE_STATUS);

// Force a recreate-style reindex of the test case — this is the exact
// path that drops the status before the fix.
const reindexRes = await apiContext.post(
'/api/v1/search/reindexEntities?recreate=true',
{
data: [
{
id: testCaseId,
type: 'testCase',
fullyQualifiedName: testCaseFqn,
},
],
}
);

expect(reindexRes.status()).toBeLessThan(400);
Comment on lines +79 to +92

Comment thread
mohityadav766 marked this conversation as resolved.
// Assert the status is still there after reindex. Before the fix, the
// recreated doc had no testCaseResult and this poll would time out.
await expect
.poll(
async () => {
const res = await apiContext.get(
`/api/v1/search/query?q=fullyQualifiedName:%22${encodeURIComponent(
testCaseFqn
)}%22&index=test_case_search_index`
);
if (res.status() !== 200) {
return undefined;
}
const body = await res.json();
const hits = body?.hits?.hits ?? [];
return hits[0]?._source?.testCaseResult?.testCaseStatus;
},
{
message:
'post-reindex: test case search doc must still include testCaseResult.testCaseStatus',
Comment thread
mohityadav766 marked this conversation as resolved.
timeout: 30_000,
}
)
.toBe(TEST_CASE_STATUS);
} finally {
await table.delete(apiContext);
await afterAction();
}
Comment thread
gitar-bot[bot] marked this conversation as resolved.
});
Loading