[OPIK-8181] [BE] refactor: centralise dataset item test assertions in DatasetItemAssertions - #8095
Merged
JetoPistola merged 3 commits intoSep 4, 2026
Conversation
… DatasetItemAssertions IGNORED_FIELDS_DATA_ITEM was declared twice - in DatasetsResourceTest and, independently, in ExperimentAggregatesIntegrationTest - and DatasetVersionResourceTest imported it across package boundaries from a sibling test class. Thirteen call sites then re-derived the same comparator chain by hand. Introduce DatasetItemAssertions under api/resources/utils/datasets/, following the TraceAssertions shape: it owns the ignore-field constant and exposes the assertion methods the dataset tests need. Five methods cover the distinct shapes in use (single item, list equality, ordered, unordered, subset), plus ignoredFieldsPlus for the one call site that ignores an extra field and asserts it separately. The two constant declarations had already drifted: the ExperimentAggregatesIntegrationTest copy was missing runSummariesByExperiment. Reconciled on the union, since both files compare DatasetItems from the same endpoints and the field is a later-added derived one the second copy simply never picked up. Test-only. No production code and no change to what the assertions assert. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
⏱️ pre-commit per-hook timing
⏭️ 43 skipped (no matching files changed)
|
Routing ExperimentAggregatesIntegrationTest through the shared helper newly ignored runSummariesByExperiment, which its local constant had not ignored. The two lists differed there for a reason: ExperimentAggregatesDAO's parity query omits assertions_array (the mapper guards it with size() > ASSERTIONS_INDEX and degrades to null) while DatasetItemVersionDAO includes it, so this is exactly the field the parity test exists to compare. Assert it explicitly in assertDatasetItemsWithExperimentItems, keeping the shared helper. No fixture creates suite_assertion scores today, so nothing was masked - the loss was future protection. Also scope the skill-doc one-declaration rule to helper-covered comparisons: a call site whose contract genuinely differs may ignore a different set, provided it derives from the shared constant via ignoredFieldsPlus. DatasetsResourceProjectScopedTest is the real case - its expected item keeps id == null while the API generates one. The section now also warns about the trap this branch hit, where a field one comparison ignores and another asserts gets folded into a shared list. Both raised by the automated reviewer on PR #8095. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Collaborator
|
No test needed here. Test-only refactor: all four Java files are under Advisory, from the QA test radar. Nothing here blocks this PR, and anything it proposes is a draft for review. |
JetoPistola
deleted the
danield/OPIK-8181-centralise-dataset-item-test-assertions
branch
September 4, 2026 09:50
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Details
IGNORED_FIELDS_DATA_ITEMwas declared twice — once inDatasetsResourceTestand again, independently, inExperimentAggregatesIntegrationTest— whileDatasetVersionResourceTestimported it across package boundaries from a sibling test class. Thirteen call sites then re-derived the sameusingRecursiveFieldByFieldElementComparatorIgnoringFields(...)chain by hand. This introduces aDatasetItemAssertionshelper underapi/resources/utils/datasets/that owns the constant and the assertion methods, following the existingTraceAssertionsshape, and migrates every call site onto it.ExperimentAggregatesIntegrationTestcopy was missingrunSummariesByExperiment, a later-added derived field it never picked up. Reconciled on the union (9 fields): both files compareDatasetItems returned by the same endpoints, so the omission was staleness rather than a legitimate difference. Verified by a full green run of that suite (145/145) — the wider ignore set does not mask any assertion there.isEqualTo,containsExactlyElementsOf,containsExactlyInAnyOrder,containsAll, single-item recursive), so one signature could not cover them without contorting callers. The constant stayspublicon the helper for the few bespoke chains — exactly whatTraceAssertionsdoes — which satisfies single-ownership without forcing every site through a method.ignoredFieldsPlus("data")replaces a mutableArrayListcopy at the one call site that ignores an extra field and then asserts it separately, reusing theStream.concatidiom already established inExperimentTestAssertions.(actual, expected)argument order — the dominant convention and AssertJ's own reading order. Two of the former local helpers took(expected, actual)..agents/skills/opik-backend/testing.mdnow leads with helper-over-constant reuse and names the six helpers that exist, so the next author reaches for the helper instead of copying a comparator chain.Test-only change: no production code. Assertion semantics are unchanged at every call site bar one, called out below.
Review follow-up — one place the union reconciliation did change coverage. Routing
ExperimentAggregatesIntegrationTestthrough the shared helper newly ignoredrunSummariesByExperiment, which its local constant had not ignored onmain. The two lists differed there for a reason:ExperimentAggregatesDAO's parity query omitsassertions_array(the mapper guards this withsize() > ASSERTIONS_INDEXand degrades tonull), whileDatasetItemVersionDAOincludes it — so this is precisely the field that parity test exists to compare.assertDatasetItemsWithExperimentItemsnow asserts it explicitly alongside the existing experiment-items loop, restoring the coverage while keeping the shared helper. No fixture exercises it today (none createsuite_assertionscores), so nothing was masked — the loss was future protection. Raised by the automated reviewer; the accompanying suggestion to addassertions_arrayto the production DAO query is deliberately out of scope for a test-only ticket.Change checklist
Issues
AI-WATERMARK
AI-WATERMARK: yes
Testing
Commands run (from
apps/opik-backend):mvn test -Dtest='ExperimentAggregatesIntegrationTest'— 145/145 pass, BUILD SUCCESS, re-run after adding the explicitrunSummariesByExperimentparity assertion described above. This is the suite where the change is semantically meaningful.mvn test -Dtest='DatasetsResourceTest,DatasetVersionResourceTest,ExperimentAggregatesIntegrationTest'— every migrated call site's class passes: all 21DatasetVersionResourceTestnested classes (9 migrations), and the dataset-item classes inDatasetsResourceTest(4 migrations) at 163/163 —CreateDatasetItems,GetDatasetItem,GetDatasetItemsByDatasetId,DeleteDatasetItems,FindDatasetItemsWithExperimentItems,FindDatasetItemsWithExperimentItemsSortingTest,FindDatasetItemsWithExperimentItemsAssertionResults.mvn compile -DskipTests— clean.mvn spotless:check— clean.Environment: local, Testcontainers (MySQL + ClickHouse + Redis + ZooKeeper), macOS.
Tests not passing locally, with reason: the dataset-level classes in
DatasetsResourceTest(FindDatasets,CreateDataset,GetDataset,ApiKey,SessionTokenCookie,ExperimentSummaryEnrichment) fail with HTTP 500 in this environment. These are pre-existing and unrelated — confirmed by runningDatasetsResourceTest$FindDatasetsagainst an unmodifiedorigin/maincheckout, which fails 37/74 identically (same tests, same line, same 500). None of those classes are touched by this PR, and a test-only assertion refactor cannot produce server-side 500s. Relying on CI for a clean environment run of those classes.Documentation
.agents/skills/opik-backend/testing.md— the partial-comparison guidance now states that an existing assertion helper method or class takes precedence over inlining a shared ignore-field constant, names the six helpers underapi/resources/utils/, documentsignoredFieldsPlusfor the extra-field case, and says to add a new helper rather than hoist a constant into a test class when more than one class needs the assertion. ThehasSizeexample in the same section was updated to call the helper instead of showing a raw comparator chain.Revised after review feedback: the one-declaration rule is now scoped to the comparisons a helper actually covers, and explicitly permits a call site whose contract genuinely differs to ignore a different set — provided it derives from the shared constant via
ignoredFieldsPlusrather than rebuilding the list.DatasetsResourceProjectScopedTestis the real case that motivated this: its expected item keepsid == nullwhile the API generates one, so its ignore set legitimately includesid. The section also now warns about the trap this PR itself hit — a field one comparison ignores and another asserts is a real difference, not drift, and folding it into the shared list quietly drops coverage.