Skip to content

filterByTableNameTestCaseBelongsTo returns true for events it cannot evaluate, so conversations on a test case bypass the table filter #31330

Description

@manerow

Summary

filterByTableNameTestCaseBelongsTo returns true for every event whose entityType is not
testCase, so a conversation on a test case bypasses the filter and is delivered to an alert scoped
to a different table.

This is the last surviving instance of the "a matcher that cannot evaluate an event returns
true" pattern in AlertsRuleEvaluator. Sibling of #30555 (the five scoping matchers, fixed by
#30571) and of #29112 (the observability trigger matchers). Part of family 1 of #27889.

Root cause

AlertsRuleEvaluator.java:316-324:

public boolean filterByTableNameTestCaseBelongsTo(List<String> tableFqns) {
  if (changeEvent == null) {
    return false;
  }
  if (!changeEvent.getEntityType().equals(TEST_CASE)) {
    return true;                                    // <-- "deliver", not "does not match"
  }
  TestCase testCase = (TestCase) getEntity(changeEvent);
  String parentFqn = resolveParentTableFqn(testCase);
  return parentFqn != null && tableFqns.contains(parentFqn);
}

Returning true from a filter that cannot be evaluated means "deliver", not "does not match", which
is the same defect #30555 describes for the scoping matchers.

Reachability on main / 2.0

Narrow but real, and worth stating precisely because the Task System Redesign changed it.

The filter is declared on exactly one resource, observability testCase, in
EntityObservabilityFilterDescriptor.json, alongside filterByFqn, filterByDomain and
filterByOwner. The observability resource list is [table, topic, container, pipeline, ingestionPipeline, testCase, testSuite, dataContract], with no all, so the
resources == ["all"] early return in AlertUtil.shouldTriggerAlert cannot apply. Routing is then
config.getResources().contains(event.getEntityType()), which means the only non-testCase event
that can reach this matcher is a THREAD event through the branch added by #28122.

Reaching it therefore needs all of:

  1. An observability alert on testCase carrying filterByTableNameTestCaseBelongsTo.
  2. Zero triggers on that alert. The fix(alerts): observability status triggers no longer fire on thread events #29112 guard only rejects thread events when config.getActions()
    is non-empty, and the trigger section is a form list with no minimum, so a zero-trigger
    observability alert is savable and does receive thread events. fix(alerts): scope thread events by their parent entity instead of bypassing filters #30571 verified this same shape
    end to end for filterByFqn.
  3. A Conversation whose about is a test case. FeedRepository does not restrict the about entity
    type (Entity.supportsFeed exists at Entity.java:909 but is referenced nowhere in the service),
    so POST /v1/feed with about: <#E::testCase::{fqn}::description> is accepted. The UI does not
    offer this today, so in practice this is an API-created conversation.

The redesign shrank this, it did not cause it. Before #25894, DQ incidents were thread-tasks whose
about pointed at the test case, so entityRef.type == testCase and they travelled the THREAD path
straight into a testCase-resource alert. On main, TestCaseResolutionStatusRepository creates
Task entities through TaskRepository, so incident events carry entityType = task, which never
matches resources = ["testCase"]. Only plain Conversations remain on this path.

Why this is not part of #30555

Filed separately rather than folded into #30571 because the shape differs:

Suggested fix

For a THREAD event, resolve thread.entityRef, require type == testCase, load the test case and
reuse the existing resolveParentTableFqn(testCase). Return false for anything else, including a
thread about a non-test-case entity and an unresolvable parent, so the matcher never answers "deliver"
to a question it could not evaluate.

Resolution of the parent must tolerate a missing entity. An exception escaping a matcher aborts the
whole change-event batch, the failure mode fixed in #28304 and #29674.

Acceptance criteria

  • A zero-trigger observability alert on testCase with filterByTableNameTestCaseBelongsTo = [tableA]
    does not receive a conversation on a test case belonging to tableB.
  • The same alert does receive a conversation on a test case belonging to tableA.
  • The matcher returns false, never true, for a thread about a non-test-case entity, a thread
    with no entityRef, and a thread whose parent test case no longer resolves.
  • No matcher in AlertsRuleEvaluator returns true for an event it cannot evaluate.
  • Unit coverage for match, non-match and each inapplicable case.

Metadata

Metadata

Assignees

Type

No type

Projects

Status
Release Backlog 🚧

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions