Skip to content

Fixes 4065: TestCase reindex preserves testCaseStatus + lenient time-series reads#28061

Open
mohityadav766 wants to merge 17 commits into
mainfrom
fix-deleted-field
Open

Fixes 4065: TestCase reindex preserves testCaseStatus + lenient time-series reads#28061
mohityadav766 wants to merge 17 commits into
mainfrom
fix-deleted-field

Conversation

@mohityadav766
Copy link
Copy Markdown
Member

@mohityadav766 mohityadav766 commented May 12, 2026

Describe your changes:

Fixes https://github.com/open-metadata/openmetadata-collate/issues/4065 and https://github.com/open-metadata/openmetadata-collate/issues/4066

On a selective entity reindex (recreate=true), TestCaseIndex.getRequiredReindexFields() only declared the columns it explicitly read, but both testCaseResult and incidentId are stripped from the stored JSON and only re-attached via setFields. TestCaseRepository.setFieldsInBulk therefore skipped fetching them, the reindexed ES doc had no testCaseStatus, and the UI / Data Quality search showed status as blank until the next per-case write re-populated it.

I added both fields to the required-reindex set so the bulk fetch includes them. While auditing the read path I also switched EntityTimeSeriesRepository's three JSON-to-entity conversions to JsonUtils.readOrConvertValueLenient(...), so stale top-level fields persisted on time-series rows (e.g. a stray deleted stamped by softDeleteOrRestoredChildren) no longer fail the strict Jackson read.

Type of change:

  • Bug fix

High-level design:

N/A — small change.

Tests:

Use cases covered

  • A test case with a Failed result keeps its testCaseStatus after a full entity reindex (recreate=true).
  • Time-series reads tolerate extra/unknown top-level fields on the stored JSON instead of returning a 500.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated:
    • openmetadata-service/src/test/java/org/openmetadata/service/search/indexes/TestCaseIndexTest.java — asserts testCaseResult and incidentId are in getRequiredReindexFields().
    • openmetadata-service/src/test/java/org/openmetadata/service/search/SearchIndexFactoryTest.java — covers the factory wiring for the updated index.

Backend integration tests

  • Not applicable (no new API endpoints — behavioral fix on existing reindex + read path, covered by the Playwright regression below).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • I added Playwright E2E tests under openmetadata-ui/.../ui/playwright/ for UI changes.
  • Files added/updated:
    • openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseStatusAfterReindex.spec.ts — creates a test case, writes a `Failed` result, forces an entity reindex with `recreate=true`, and asserts the status survives.

Manual testing performed

  1. Started local stack against localhost:8585 and ingested `ingestion/pipelines/sample_data.yaml` (Workflow Success 99.96%).
  2. Seeded test case results via `POST /api/v1/dataQuality/testCases/testCaseResults/{fqn}` and triggered the entity reindex with `recreate=true`.
  3. Verified `testCaseStatus` is preserved on the reindexed ES doc and visible in the Data Quality search/list (previously wiped to blank until the next per-case write).
  4. Ran `yarn playwright test playwright/e2e/Features/DataQuality/TestCaseStatusAfterReindex.spec.ts` → 4/4 passing in 1m 12s.

UI screen recording / screenshots:

Not applicable.

Checklist:

  • I have read the CONTRIBUTING document.

  • My PR title is `Fixes : `

  • My PR is linked to a GitHub issue via `Fixes #` above.

  • I have commented on my code, particularly in hard-to-understand areas.

  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed.

  • For UI changes: I attached a screen recording and/or screenshots above.

  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

  • I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.


Summary by Gitar

  • Search indexing:
    • Included requestSchema and responseSchema in getRequiredReindexFields for APIEndpointIndex to support batch field tagging.
    • Added required fields to TopicIndex and WorksheetIndex to ensure proper schema metadata preservation during reindex operations.
  • Testing:
    • Updated DistributedIndexingStrategyTest to expect success for entities with zero recorded stats to prevent false job failures.
    • Adjusted finalizeAllEntityReindexSkipsPromotedEntitiesAndPromotesMissingEntityStats to correctly reflect the updated indexing contract for empty stats.

This will update automatically on new commits.

Copilot AI review requested due to automatic review settings May 12, 2026 11:02
@mohityadav766 mohityadav766 requested a review from a team as a code owner May 12, 2026 11:02
@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels May 12, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a selective reindexing regression where testCaseStatus could be dropped from test_case_search_index documents after a recreate=true entity reindex, and it makes time-series entity hydration from search results more tolerant of unknown/stale JSON fields.

Changes:

  • Ensure TestCaseIndex.getRequiredReindexFields() requests testCaseResult and incidentId so bulk field hydration preserves testCaseStatus during recreate-style reindexing.
  • Switch EntityTimeSeriesRepository search-result hydration to use lenient JSON conversion to ignore unknown top-level fields.
  • Add unit and Playwright regression tests covering the reindex/status scenario and index factory wiring.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseStatusAfterReindex.spec.ts Playwright regression validating testCaseStatus survives recreate=true entity reindex.
openmetadata-service/src/test/java/org/openmetadata/service/search/SearchIndexFactoryTest.java Asserts factory-exposed reindex field set includes testCaseResult/incidentId for TestCase.
openmetadata-service/src/test/java/org/openmetadata/service/search/indexes/TestCaseIndexTest.java Unit test for TestCaseIndex.getRequiredReindexFields() including testCaseResult + incidentId.
openmetadata-service/src/main/java/org/openmetadata/service/search/indexes/TestCaseIndex.java Adds testCaseResult and incidentId to required reindex fields.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/EntityTimeSeriesRepository.java Uses lenient JSON conversion when hydrating time-series entities from search results.
openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/ReindexingConfiguration.java Changes how recreateIndex is derived in config creation.

pmbrull
pmbrull previously approved these changes May 12, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.44% (64643/103515) 43.08% (35124/81517) 45.92% (10347/22528)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 12, 2026

🟡 Playwright Results — all passed (17 flaky)

✅ 4070 passed · ❌ 0 failed · 🟡 17 flaky · ⏭️ 86 skipped

Shard Passed Failed Flaky Skipped
✅ Shard 1 299 0 0 4
🟡 Shard 2 759 0 8 8
🟡 Shard 3 779 0 5 7
🟡 Shard 4 788 0 2 18
✅ Shard 5 709 0 0 41
🟡 Shard 6 736 0 2 8
🟡 17 flaky test(s) (passed on retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/DataQuality/DataQuality.spec.ts › TestCase filters (shard 2, 1 retry)
  • Features/Glossary/GlossaryWorkflow.spec.ts › should start term as Draft when glossary has reviewers (shard 2, 2 retries)
  • Features/KnowledgeCenter.spec.ts › User Mentions in article and redirect should work of Knowledge Center page (shard 2, 1 retry)
  • Features/KnowledgeCenter.spec.ts › Article mentions in description should working for Knowledge Center (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 2, 1 retry)
  • Features/NestedColumnsExpandCollapse.spec.ts › should not duplicate rows when expanding and collapsing nested columns with same names (shard 3, 1 retry)
  • Features/RTL.spec.ts › Verify Following widget functionality (shard 3, 1 retry)
  • Features/Tasks/DomainFiltering.spec.ts › selecting All Domains removes domain filter from feed API call (shard 3, 1 retry)
  • Flow/AddRoleAndAssignToUser.spec.ts › Verify assigned role to new user (shard 3, 1 retry)
  • Flow/ObservabilityAlerts.spec.ts › Ingestion Pipeline alert (shard 3, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Set (shard 4, 1 retry)
  • Pages/Domains.spec.ts › Create DataProducts and add remove assets (shard 4, 1 retry)
  • Pages/Glossary.spec.ts › Glossary Term Update in Glossary Page should persist tree (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

…ultTimestamp

TestSuiteIndex.buildSearchIndexDocInternal computes the top-level
lastResultTimestamp field from testSuite.getTestCaseResultSummary().
TestSuiteRepository registers a fetcher for that under the field name
"summary"; the reindex path only invokes fetchers whose field is in
getRequiredReindexFields(). Without "summary" declared, the fetcher does
not run, testCaseResultSummary stays null on the entity, and the Index
takes its else branch (TestSuiteIndex.java:41) writing
lastResultTimestamp=0L on every reindexed suite.

That field is exactly what the DQ /data-quality/test-suites list page
sorts by (TestSuites.component.tsx:175), so a full reindex collapses
every basic suite to the 1970 epoch and "most recently run first" stops
working.

SearchIndexFactoryTest pins the declaration. The Playwright spec
creates a basic suite, writes a test case result, reindexes the suite
with recreate=true, and asserts the resulting ES doc carries the live
millisecond timestamp -- not the 0L fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mohityadav766 mohityadav766 dismissed stale reviews from ShaileshParmar11 and pmbrull via 082f1e9 May 12, 2026 15:20
Copilot AI review requested due to automatic review settings May 12, 2026 15:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Comment on lines +131 to +132
// Always run in recreate since it's zero downtime
true,
Copilot AI review requested due to automatic review settings May 13, 2026 05:22
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

doc.put("tags", parseTags.getTags());
List<TagLabel> entityTags = Entity.getEntityTags(getEntityTypeName(), ei);
ParseTags parseTags = new ParseTags(entityTags);
doc.put("tags", entityTags);
Comment on lines +79 to +92
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 +62 to +75
const reindexRes = await apiContext.post(
'/api/v1/search/reindexEntities?recreate=true',
{
data: [
{
fullyQualifiedName: suite.fullyQualifiedName,
id: suite.id,
type: 'testSuite',
},
],
}
);

expect(reindexRes.status()).toBeLessThan(400);
…84c5c2

084c5c2 ("Fix Stats false status in case of empty stats") flipped
DistributedReindexFinalizer.computeEntitySuccess: an entity with no
stats recorded -- typically a parallel-pipeline driven entity like
vectorEmbedding (RecreateWithEmbeddings) or an entity with zero source
rows -- is now promoted as success instead of failure. The companion
unit test in DistributedReindexFinalizerTest was added in that commit
but the older DistributedIndexingStrategyTest case was missed and kept
asserting the pre-fix Boolean.FALSE outcome for `user`, breaking the
PR build.

Flip the user-outcome assertion to TRUE, document why, and rename the
test method to reflect the new contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 13, 2026

Code Review 👍 Approved with suggestions 2 resolved / 3 findings

Adds required fields to TestSuiteIndex and updates time-series reads to use lenient conversion to fix reindex data loss. Consider defining a named constant for the threshold in isSmartReindexing() to replace the magic number 20.

💡 Quality: Magic number 20 in isSmartReindexing() should be a named constant

📄 openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/ReindexingConfiguration.java:188

The isSmartReindexing() method uses the literal 20 as a threshold for determining smart reindexing. Per project conventions, all other thresholds in this file are declared as private static final constants (e.g., DEFAULT_BATCH_SIZE, DEFAULT_CONSUMER_THREADS). This magic number should follow the same pattern for readability and maintainability.

Extract 20 into a named constant alongside the other defaults
private static final int SMART_REINDEX_MAX_ENTITIES = 20;

/** Check if this is a subset (smart) reindexing */
public boolean isSmartReindexing() {
  return entities != null
      && !entities.contains(SearchIndexEntityTypes.ALL)
      && entities.size() < SMART_REINDEX_MAX_ENTITIES;
}
✅ 2 resolved
Quality: Playwright test missing cleanup of test case result

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/DataQuality/TestCaseStatusAfterReindex.spec.ts:118-121
The Playwright test creates a test case result via table.addTestCaseResult(...) but only calls table.delete(apiContext) in the finally block. If the table deletion doesn't cascade to test case results (depending on API behavior), stale time-series data could accumulate. This is minor since the table itself is cleaned up, but worth noting for test isolation.

Bug: Hardcoding recreateIndex=true ignores user's explicit preference

📄 openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/ReindexingConfiguration.java:131-132
The change at line 132 replaces Boolean.TRUE.equals(jobData.getRecreateIndex()) with a hardcoded true, meaning every reindex operation now forces index recreation regardless of what the caller requested. While the comment says "zero downtime", this is a separate behavioral change from the bug being fixed (missing testCaseStatus fields). If a user explicitly passes recreate=false (e.g., for a lightweight in-place reindex or partial update), their preference is now silently ignored. This could also mask issues where the staging/promotion path has bugs, since there's no way to fall back to the non-recreate path.

If the intent is to always use recreation going forward, this should be documented as an intentional behavioral change in the PR description. If it was added to ensure the test passes (since the Playwright test uses recreate=true), it shouldn't be needed given the core fix is the field additions in TestCaseIndex.

🤖 Prompt for agents
Code Review: Adds required fields to TestSuiteIndex and updates time-series reads to use lenient conversion to fix reindex data loss. Consider defining a named constant for the threshold in isSmartReindexing() to replace the magic number 20.

1. 💡 Quality: Magic number 20 in isSmartReindexing() should be a named constant
   Files: openmetadata-service/src/main/java/org/openmetadata/service/apps/bundles/searchIndex/ReindexingConfiguration.java:188

   The `isSmartReindexing()` method uses the literal `20` as a threshold for determining smart reindexing. Per project conventions, all other thresholds in this file are declared as `private static final` constants (e.g., `DEFAULT_BATCH_SIZE`, `DEFAULT_CONSUMER_THREADS`). This magic number should follow the same pattern for readability and maintainability.

   Fix (Extract 20 into a named constant alongside the other defaults):
   private static final int SMART_REINDEX_MAX_ENTITIES = 20;
   
   /** Check if this is a subset (smart) reindexing */
   public boolean isSmartReindexing() {
     return entities != null
         && !entities.contains(SearchIndexEntityTypes.ALL)
         && entities.size() < SMART_REINDEX_MAX_ENTITIES;
   }

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud
Copy link
Copy Markdown

@sonarqubecloud
Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants