Skip to content

test(viewtools): add raw() VTL coverage for aggregations (#36026)#36093

Open
fabrizzio-dotCMS wants to merge 1 commit into
mainfrom
issue-36026-raw-vtl-coverage
Open

test(viewtools): add raw() VTL coverage for aggregations (#36026)#36093
fabrizzio-dotCMS wants to merge 1 commit into
mainfrom
issue-36026-raw-vtl-coverage

Conversation

@fabrizzio-dotCMS

Copy link
Copy Markdown
Member

Proposed Changes

Follow-up test coverage for #36026. The fix for that issue restored a vendor-neutral aggregation tree so existing VTL templates walking $results.aggregations keep working after the ES→OS migration. ContentSearchToolTest already verified this through Velocity for $estool.search(...), and verified $estool.raw(...) only at the Java API level — there was no test driving raw() through the Velocity engine.

This PR adds rawVtl_rendersTreeAndFlatAggregations, which evaluates #set($results = $estool.raw($esQuery)) through VelocityUtil.eval and asserts both response surfaces render real data:

  • Tree walk$tree.content_types.bucketsgetKeyAsString() / getDocCount() → nested top_content top_hits$hit.id (mirrors the search() customer template).
  • Flat map$flat.content_types iterated directly as a List<AggregationBucket> (the raw-specific shape that differs from search()).

Record-accessor gotcha (documented in the test)

raw() returns a ContentSearchResponse record, whose accessors are aggregations() / aggregationTree() — there are no get* methods. Velocity's property syntax ($results.aggregations) only resolves getX()/isX()/public fields, so on a record it silently yields null. The template therefore calls them with explicit method syntax ($results.aggregationTree()), then stays property-style downstream because Aggregation's components are named getBuckets/getHits and AggregationBucket exposes bean getters. This contrasts with search(), which returns a ContentSearchResults class with real bean getters.

Testing

Run against a live integration environment (PostgreSQL + OpenSearch):

Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 21.97 s
 -- in com.dotcms.rendering.velocity.viewtools.ContentSearchToolTest

All 5 tests pass (4 pre-existing + the new one). Test-only change; no production code touched.

Closes #36026

🤖 Generated with Claude Code

ContentSearchToolTest only exercised $estool.search() through the
Velocity engine; raw() was covered at the Java API level only. This
adds rawVtl_rendersTreeAndFlatAggregations, driving $estool.raw($esQuery)
through VelocityUtil.eval and asserting both response surfaces render
real data:

- tree walk: $tree.content_types.buckets -> getDocCount() -> nested
  top_content top_hits -> $hit.id
- flat map: $flat.content_types iterated directly as List<AggregationBucket>

Documents the record-accessor gotcha that motivated the test: raw()
returns a ContentSearchResponse record whose accessors are aggregations()
/ aggregationTree() (no get* prefix), which Velocity's property syntax
cannot resolve, so the template must call them with explicit () — unlike
search()'s ContentSearchResults, which exposes bean getters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@claude

claude Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Claude finished @fabrizzio-dotCMS's task in 1m 33s —— View job


Code Review

  • Gather context and read changed file
  • Analyze for bugs, risks, and design issues

Clean test-only change. A few things worth noting:

1. Weak first assertion (line 332)

assertTrue("Aggregation data should be present in the response",
        output.contains("content_types"));

This passes trivially because aggregations: $!{flat} in the template renders the entire flat map to output, which will always contain the string "content_types" as a map key. The assertion doesn't actually verify traversal worked — it would pass even if the #foreach loops and #set($tree = ...) all silently failed. The five assertions below it are doing the real work. This line can be removed or replaced with a more specific check (e.g. output.contains("tree CT:")).

2. Javadoc/template mismatch

The rawVtl_rendersTreeAndFlatAggregations javadoc says:

the tree walk ($results.aggregationTree.content_types.buckets with nested top_hits)

But the actual template uses $results.aggregationTree() (explicit call). Minor, but it slightly misrepresents how the template works — the whole point of the PR is that property syntax fails on a record.

3. Silent failure path in VTL if sub-agg is missing

#set($topHits = $group.getAggregations().get("top_content"))
#foreach($hit in $topHits.getHits().getHits())

If "top_content" is absent (e.g. someone trims the query), $topHits is null, Velocity silently skips the #foreach, and the output.contains("tree hit id:") assertion fails with a message that doesn't indicate the sub-agg was missing. Not a bug in the current form (the @BeforeClass data guarantees the path), but a future maintainer who edits the query will get an opaque assertion failure rather than a clear NullPointerException. Adding a #if($topHits) guard with an explicit $response.sendError(500, ...) — or just an API-level pre-assertion — would make failures self-diagnosing. Again, low risk as-is.

Not flagged: The record accessor workaround is correctly implemented and well-documented. The data setup in @BeforeClass is sufficient. The Pattern.compile(...) assertions correctly verify numeric doc counts. The test structure is consistent with the sibling tests.


@fabrizzio-dotCMS fabrizzio-dotCMS added this pull request to the merge queue Jun 10, 2026
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Aggregation return-type change breaks existing VTL templates accessing $results.aggregations

2 participants