Class AnnotationsWebServiceTest
- All Implemented Interfaces:
org.springframework.beans.factory.Aware, org.springframework.context.ApplicationContextAware
- Author:
- poirigui
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic class -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidvoidvoid🛑 The control that keeps the cut honest.voidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoid...and a caller who asks for 0 gets 0.voidOn a single-cell dataset the cell types belong to the CELLS, so what they imply is not a fact about the experiment.voidA term-seeded read carries no dataset, so there is nothing to call single-cell and no cut.voidPositive control for the two above: a term-seeded read is NOT barred.void🛑 The regression guard for the whole point of this endpoint: a twin the CORPUS HAS NEVER USED must still be answered for.voidThe canonicalization table has to be reachable by a client that resolves terms before asking Gemma — cab seeded a two-row local redirect table because ours was not queryable, and two authorities on one question is the thing this endpoint exists to prevent.voidEvery row says which rule decided it, so a consumer can tell an ontology-intrinsic answer (R3 xref, R4 definition) from one that rests on our curators' spelling habits (R5 usage).voidFiltering by URI returns that one row, and an unmapped URI returns none rather than a guess.voidAn excluded namespace leavesdatabut is REPORTED, not deleted — a gene symbol answered with the disease it causes is the measured failure, and an over-firing rule has to be visible rather than silent.voidvoidvoidThe children walk carries the same marker, on the same two-walk mechanism.voidvoidexact_labelfilters the POSITIVE list;negativeEvidencereports the VERDICT.voidThe exact tier has the same problem the category promotion had: it is established on the candidate list and then thrown away bystrategy.rank(), which re-sorts everything on its own score.voidReported by CAB 2026-08-11: `H1` is a declared exact synonym of EFO_0003042 ("H1-hESC", 18 corpus uses), but the relevance tiers read the hit's LABEL only, so on "h1-hesc" it scored as a prefix match and came back at rank 7 -- behind two CHEBI histamine-receptor ligands with no corpus use at all.voidvoidvoidvoidvoidvoidA CURIE-valued literal (CLO:0000457) is canonicalised, not dropped.voidA term that was split rather than merged names candidates, not a replacement.voidThe EFO case that motivated the field (uib, 2026-08-16):EFO_0000408 obsolete_diseasenamesMONDO_0000001as its successor, and EFO's OWL carries a label for that class, so no second lookup is needed.voidvoidvoidvoidvoidSame axiom written as a literal rather than anrdf:resource— the OBO→OWL conversions disagree on this, and reading only the resource form empties the field for whole ontologies.voidvoidA live term is never probed for obsolescence axioms — it declares none, and probing would cost a successor lookup per call for a field that is always null.voidvoidvoid🛑 Literature citations are withheld fromdbXrefsunless asked for, and always counted.voidvoid🛑 The gate reads/relations/implies.void...and a caller who asks for them gets them back.voidvoidvoidThe confident negative must not be claimed when identity matching never ran — a descriptive query keeps its near-matches, so absence of a match there says nothing.voidvoidNo URI, nothing to match against the subClassOf-only walk — unknown, not "reached by a role".voidCHEBI's shape:vancomycinreachesglycopeptidebyrdfs:subClassOfandantibacterial drugonly byhas_role, which Gemma follows as an additional property.voidvoidvoidvoidvoidvoidThe bar defaults ON for a dataset-seeded read, where the fan-out was measured.voidAnd it stays off unless asked, so exposing it changed no existing caller's results.voidThe other half: on /relations the parameter does reach the query, or it is decorative.voidA query parameter that never reaches the query is decorative and nothing fails — the same shape the two tests above pin forincludeRefuted.voidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidvoidCounting one experiment once, however many annotation rows or levels carry the term, is now the database's job —countExperimentsByUrisdoes it with acount(distinct ...)over the union of URI columns, so there is no per-class map left for this layer to collapse.voidvoidvoidvoidvoidvoidvoidvoidA designation query that retrieves only near-matches must report WHICH terms it ruled out, not merely that nothing matched.Methods inherited from class BaseJerseyTest5
configure, configureClient, getTestContainerFactory, setApplicationContext, setUp, tearDownMethods inherited from class org.glassfish.jersey.test.JerseyTest
client, close, closeIfNotNull, configureDeployment, disable, enable, forceDisable, forceEnable, forceSet, getAsyncTimeoutMultiplier, getBaseUri, getClient, getLastLoggedRecord, getLoggedRecords, getPort, getSslContext, getSslParameters, isEnabled, set, set, setClient, target, target
-
Constructor Details
-
AnnotationsWebServiceTest
public AnnotationsWebServiceTest()
-
-
Method Details
-
setUpMocks
@BeforeEach public void setUpMocks() -
resetMocks
@AfterEach public void resetMocks() -
testSearchTaxonDatasets
- Throws:
SearchExceptionTimeoutException
-
testParents
- Throws:
TimeoutException
-
testParentsWhenTermIsNotFound
- Throws:
TimeoutException
-
testParentsWhenInferenceTimeout
- Throws:
TimeoutException
-
testChildren
- Throws:
TimeoutException
-
testAddDatasetAnnotation
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotation() -
testAddDatasetAnnotationDuplicateReturns409
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationDuplicateReturns409() -
testAddDatasetAnnotationRejectsBlankCategory
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationRejectsBlankCategory() -
testAddDatasetAnnotationRejectsBadEvidenceCode
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationRejectsBadEvidenceCode() -
testAddDatasetAnnotationCreatesStatementWhenPredicateOrObjectSet
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationCreatesStatementWhenPredicateOrObjectSet() -
testAddDatasetAnnotationCreatesCharacteristicWhenNoStatementFieldsSet
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationCreatesCharacteristicWhenNoStatementFieldsSet() -
testAddDatasetAnnotationRejectsSecondPredicateWithoutFirst
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testAddDatasetAnnotationRejectsSecondPredicateWithoutFirst() -
testRemoveDatasetAnnotation
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testRemoveDatasetAnnotation() -
testRemoveDatasetAnnotationNotFound
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testRemoveDatasetAnnotationNotFound() -
testReplaceDatasetAnnotationsIdempotentNoOp
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testReplaceDatasetAnnotationsIdempotentNoOp() -
testReplaceDatasetAnnotationsAppliesDiff
@Test @WithMockUser(authorities="GROUP_CURATOR") public void testReplaceDatasetAnnotationsAppliesDiff() -
testParentsPopulatesUsageCount
- Throws:
TimeoutException
-
testChildrenPopulatesUsageCount
- Throws:
TimeoutException
-
testParentsReportsZeroWhenNoExperimentsMatch
- Throws:
TimeoutException
-
testParentsSkipsCountLookupWhenAllTermsLackUris
- Throws:
TimeoutException
-
testParentsMarksWhetherTheTermWasReachedBySubClassOfAlone
@Test public void testParentsMarksWhetherTheTermWasReachedBySubClassOfAlone() throws TimeoutExceptionCHEBI's shape:vancomycinreachesglycopeptidebyrdfs:subClassOfandantibacterial drugonly byhas_role, which Gemma follows as an additional property. Both land in one list, andviaSubClassOfis the only thing that tells them apart — a client that wants a compound's roles without its upper-ontology structure cannot recover the split from labels.- Throws:
TimeoutException
-
testParentsLeavesViaSubClassOfNullOnAUrilessTerm
No URI, nothing to match against the subClassOf-only walk — unknown, not "reached by a role".- Throws:
TimeoutException
-
testChildrenMarksWhetherTheTermWasReachedBySubClassOfAlone
@Test public void testChildrenMarksWhetherTheTermWasReachedBySubClassOfAlone() throws TimeoutExceptionThe children walk carries the same marker, on the same two-walk mechanism.- Throws:
TimeoutException
-
testSearchAnnotationsPopulatesUsageCount
@Test public void testSearchAnnotationsPopulatesUsageCount() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSuppressedDesignationReportsWhatItRuledOut
@Test public void testSuppressedDesignationReportsWhatItRuledOut() throws SearchException, TimeoutExceptionA designation query that retrieves only near-matches must report WHICH terms it ruled out, not merely that nothing matched. An emptydataarray is indistinguishable from "the ontology wasn't loaded" or "this call never ran", and on its own it does not stop a downstream stage from proposing one of these very terms from its own index.- Throws:
SearchExceptionTimeoutException
-
testExactLabelDoesNotSuppressNegativeEvidence
@Test public void testExactLabelDoesNotSuppressNegativeEvidence() throws SearchException, TimeoutExceptionexact_labelfilters the POSITIVE list;negativeEvidencereports the VERDICT. Passing both must still yield the verdict — the agents sendexact_label=trueon every call, so an interaction that swallows the signal means they never receive it at all, while appearing to have asked for it.- Throws:
SearchExceptionTimeoutException
-
testNegativeEvidenceAbsentForDescriptiveQuery
@Test public void testNegativeEvidenceAbsentForDescriptiveQuery() throws SearchException, TimeoutExceptionThe confident negative must not be claimed when identity matching never ran — a descriptive query keeps its near-matches, so absence of a match there says nothing.- Throws:
SearchExceptionTimeoutException
-
testExactSynonymEarnsTheExactTier
Reported by CAB 2026-08-11: `H1` is a declared exact synonym of EFO_0003042 ("H1-hESC", 18 corpus uses), but the relevance tiers read the hit's LABEL only, so on "h1-hesc" it scored as a prefix match and came back at rank 7 -- behind two CHEBI histamine-receptor ligands with no corpus use at all. A caller cannot act on that: the row it wants is indistinguishable from the noise it has to filter. An exact synonym is an exact match, and now earns the exact tier.- Throws:
Exception
-
testLexicalCatalogueHitIsWorthOneTierLessThanAnOntologyHit
-
testLexicalCatalogueHitAlsoSinksBelowANonExactOntologyHit
-
testCategoryPromotionSurvivesTheRankingStrategy
-
testExactMatchTierSurvivesTheRankingStrategy
The exact tier has the same problem the category promotion had: it is established on the candidate list and then thrown away bystrategy.rank(), which re-sorts everything on its own score. Measured on frink 2026-08-16 before the fix,Myelopathyreturnedspinal cord injuryabovemyelopathy— the latter matched on its PREFERRED LABEL and lost anyway, because the former carries far more corpus usage and composite weights usage. An exact match losing to a lexical neighbour is not a ranking preference, it is the ranking being wrong.- Throws:
Exception
-
testCategoryExclusionRemovesAndReportsOutOfCategoryHits
An excluded namespace leavesdatabut is REPORTED, not deleted — a gene symbol answered with the disease it causes is the measured failure, and an over-firing rule has to be visible rather than silent.- Throws:
Exception
-
testSearchAnnotationsBatchResolvesEachLabelIndependently
@Test public void testSearchAnnotationsBatchResolvesEachLabelIndependently() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsBatchEmptyReturns400
@Test public void testSearchAnnotationsBatchEmptyReturns400() -
testSearchAnnotationsIncludeGenesFalseSkipsGeneFanout
@Test public void testSearchAnnotationsIncludeGenesFalseSkipsGeneFanout() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testGetAnnotationCategories
@Test public void testGetAnnotationCategories() -
testGetAnnotationCategoriesEmpty
@Test public void testGetAnnotationCategoriesEmpty() -
testGetAnnotationPredicates
@Test public void testGetAnnotationPredicates() -
testGetAnnotationPredicatesEmpty
@Test public void testGetAnnotationPredicatesEmpty() -
testGetAnnotationTerm
- Throws:
TimeoutException
-
testGetAnnotationTermIncludesSynonymsAndAlternativeIds
- Throws:
TimeoutException
-
testGetAnnotationTermWithholdsLiteratureCitationsUnlessAsked
@Test public void testGetAnnotationTermWithholdsLiteratureCitationsUnlessAsked() throws TimeoutException🛑 Literature citations are withheld fromdbXrefsunless asked for, and always counted.Measured on
CHEBI_45783 imatinib: 63 cross-references, of which 51 arepubmed:and every identifier that names a record —cas,drugbank,drugcentral,kegg.drug— appears exactly once. The citations push the clickable ones off any bounded view, so uib was capping the list client-side and every other consumer would have written the same rule.Counted rather than dropped silently: a caller has to be able to tell "cites nothing" from "cites fifty-one things you did not ask for".
- Throws:
TimeoutException
-
testGetAnnotationTermExposesReplacementForObsoleteTerm
The EFO case that motivated the field (uib, 2026-08-16):EFO_0000408 obsolete_diseasenamesMONDO_0000001as its successor, and EFO's OWL carries a label for that class, so no second lookup is needed.- Throws:
TimeoutException
-
testGetAnnotationTermResolvesLiteralReplacementAndItsLabel
@Test public void testGetAnnotationTermResolvesLiteralReplacementAndItsLabel() throws TimeoutExceptionSame axiom written as a literal rather than anrdf:resource— the OBO→OWL conversions disagree on this, and reading only the resource form empties the field for whole ontologies. With no in-model label, the successor's label comes from resolving it through the service.- Throws:
TimeoutException
-
testGetAnnotationTermExpandsCurieValuedReplacement
A CURIE-valued literal (CLO:0000457) is canonicalised, not dropped.- Throws:
TimeoutException
-
testGetAnnotationTermExposesConsiderCandidates
A term that was split rather than merged names candidates, not a replacement.- Throws:
TimeoutException
-
testGetAnnotationTermSkipsObsolescenceLookupForLiveTerm
A live term is never probed for obsolescence axioms — it declares none, and probing would cost a successor lookup per call for a field that is always null.- Throws:
TimeoutException
-
testGetAnnotationTermReportsZeroWhenNoExperimentsMatch
- Throws:
TimeoutException
-
testGetAnnotationTermSkipsCountLookupWhenTermHasNoUri
- Throws:
TimeoutException
-
testGetAnnotationTermNotFound
- Throws:
TimeoutException
-
testGetAnnotationTermWithBlankUriIs400
- Throws:
TimeoutException
-
testGetAnnotationTermWithMissingUriIs400
- Throws:
TimeoutException
-
testGetAnnotationTermWhenLookupTimesOut
- Throws:
TimeoutException
-
testSearchAnnotationsEnrichesTopNWithDefinitionAndParents
@Test public void testSearchAnnotationsEnrichesTopNWithDefinitionAndParents() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testGetAnnotationTermPopulatesParents
- Throws:
TimeoutException
-
testSearchAnnotationsDefaultLimitIs20
- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsRespectsExplicitLimit
@Test public void testSearchAnnotationsRespectsExplicitLimit() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsLimitOver100Returns400
@Test public void testSearchAnnotationsLimitOver100Returns400() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsLimitBelow1Returns400
@Test public void testSearchAnnotationsLimitBelow1Returns400() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesPreferredLabelMatch
@Test public void testSearchAnnotationsAttributesPreferredLabelMatch() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesLabelTokensForNonEqualPartialLabel
@Test public void testSearchAnnotationsAttributesLabelTokensForNonEqualPartialLabel() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesSynonymTokensWhenLabelLacksTokens
@Test public void testSearchAnnotationsAttributesSynonymTokensWhenLabelLacksTokens() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesLabelPrefixForTypeahead
@Test public void testSearchAnnotationsAttributesLabelPrefixForTypeahead() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsSurfacesGeneByOfficialName
@Test public void testSearchAnnotationsSurfacesGeneByOfficialName() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsSurfacesGeneByAlias
@Test public void testSearchAnnotationsSurfacesGeneByAlias() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsDropsGeneFanoutWhenURIAlreadyInOntologyResults
@Test public void testSearchAnnotationsDropsGeneFanoutWhenURIAlreadyInOntologyResults() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsDedupesGenesAcrossProbes
@Test public void testSearchAnnotationsDedupesGenesAcrossProbes() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsExactOntologyLabelOutranksGeneAliasCollision
@Test public void testSearchAnnotationsExactOntologyLabelOutranksGeneAliasCollision() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsPopulatesPriorCategoriesFromCorpusHistory
@Test public void testSearchAnnotationsPopulatesPriorCategoriesFromCorpusHistory() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsHyphenInsensitiveMec2FindsCloCellLine
@Test public void testSearchAnnotationsHyphenInsensitiveMec2FindsCloCellLine() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsHyphenInsensitiveMec2DashFindsEfoBareLabel
@Test public void testSearchAnnotationsHyphenInsensitiveMec2DashFindsEfoBareLabel() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsExactLabelFilterRespectsHyphenAndCellSuffixCanonicalForm
@Test public void testSearchAnnotationsExactLabelFilterRespectsHyphenAndCellSuffixCanonicalForm() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsCanonicaliseDoesNotRegressNonHyphenatedQueries
@Test public void testSearchAnnotationsCanonicaliseDoesNotRegressNonHyphenatedQueries() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsSymbolMatchStillPrependsAboveOntologyHits
@Test public void testSearchAnnotationsSymbolMatchStillPrependsAboveOntologyHits() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesSynonymMatch
@Test public void testSearchAnnotationsAttributesSynonymMatch() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsLeavesEnrichmentNullForPostTopN
@Test public void testSearchAnnotationsLeavesEnrichmentNullForPostTopN() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsAttributesAFreeTextRowFromItsOwnLabel
@Test public void testSearchAnnotationsAttributesAFreeTextRowFromItsOwnLabel() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsUnknownRankReturns400
@Test public void testSearchAnnotationsUnknownRankReturns400() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsReportsTheTallyItIsGiven
@Test public void testSearchAnnotationsReportsTheTallyItIsGiven() throws SearchException, TimeoutExceptionCounting one experiment once, however many annotation rows or levels carry the term, is now the database's job —countExperimentsByUrisdoes it with acount(distinct ...)over the union of URI columns, so there is no per-class map left for this layer to collapse. The dedup itself is pinned byCharacteristicDaoTest#testCountExperimentsByUrisCountsAnExperimentOnceAcrossColumns, which exercises it against a real schema; what remains testable here is that the endpoint asks for exactly the candidate URIs and reports the tally it is handed, unmodified.- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsCanonicaliesInputOrderByUri
@Test public void testSearchAnnotationsCanonicaliesInputOrderByUri() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsPrefixesFilterAppliesBeforeRanking
@Test public void testSearchAnnotationsPrefixesFilterAppliesBeforeRanking() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsUpstreamReturns400WhenUrlUnset
@Test public void testSearchAnnotationsUpstreamReturns400WhenUrlUnset() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsPrefixesFilterRespectsLimit
@Test public void testSearchAnnotationsPrefixesFilterRespectsLimit() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsPrefersCloOverEfoForBareCellLineQuery
@Test public void testSearchAnnotationsPrefersCloOverEfoForBareCellLineQuery() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsPrefersCloOverEfoForCellSuffixQuery
@Test public void testSearchAnnotationsPrefersCloOverEfoForCellSuffixQuery() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testSearchAnnotationsCellLineTiebreakerDoesNotAffectNonCellLineQueries
@Test public void testSearchAnnotationsCellLineTiebreakerDoesNotAffectNonCellLineQueries() throws SearchException, TimeoutException- Throws:
SearchExceptionTimeoutException
-
testAddDatasetAnnotationCarriesSupportingEvidence
@Test @WithMockUser public void testAddDatasetAnnotationCarriesSupportingEvidence() -
testAddDatasetAnnotationWithoutEvidenceLeavesSupportingEvidenceNull
@Test @WithMockUser public void testAddDatasetAnnotationWithoutEvidenceLeavesSupportingEvidenceNull() -
testAddDatasetAnnotationCarriesSupportingEvidenceOnAStatement
@Test @WithMockUser public void testAddDatasetAnnotationCarriesSupportingEvidenceOnAStatement() -
testReplaceDatasetAnnotationsCarriesSupportingEvidence
@Test @WithMockUser public void testReplaceDatasetAnnotationsCarriesSupportingEvidence() -
testImpliesRefusesRefutedEvenWhenTheParameterIsSent
@Test @WithMockUser public void testImpliesRefusesRefutedEvenWhenTheParameterIsSent()🛑 The gate reads/relations/implies. A refuted row arriving there would let the curation pipeline inhibit or license on a finding that says the opposite of what it appears to say — MGI reporting that a genotype does NOT model a disease, rendered as an assertion, because the predicate is stored assertively and the negation lives only instatus.includeRefutedis exposed on/relationsfor auditability. This pins the asymmetry: the parameter must not reach the implies query even when a caller sends it. SinceUnknownQueryParameterFilterlanded the request is refused outright rather than answered with the parameter dropped, so the query is never built at all. DeclaringincludeRefutedon this route — whether directly or by moving both handlers onto a shared builder — turns this 400 back into a 200 and fails here. -
testRelationsHonoursIncludeRefutedWhenAsked
@Test @WithMockUser public void testRelationsHonoursIncludeRefutedWhenAsked()The other half: on /relations the parameter does reach the query, or it is decorative. -
testRelationsPassesTheSubjectBreadthBarToTheQuery
@Test @WithMockUser public void testRelationsPassesTheSubjectBreadthBarToTheQuery()A query parameter that never reaches the query is decorative and nothing fails — the same shape the two tests above pin forincludeRefuted. -
testRelationsAppliesTheSubjectBreadthBarByDefaultForADataset
@Test @WithMockUser public void testRelationsAppliesTheSubjectBreadthBarByDefaultForADataset()The bar defaults ON for a dataset-seeded read, where the fan-out was measured. -
testAnExplicitZeroTurnsTheSubjectBreadthBarOff
@Test @WithMockUser public void testAnExplicitZeroTurnsTheSubjectBreadthBarOff()...and a caller who asks for 0 gets 0. Without this the default could not be turned off, which is the difference between a default and a policy. -
testASingleCellDatasetDropsCellTypeSubjects
@Test @WithMockUser public void testASingleCellDatasetDropsCellTypeSubjects()On a single-cell dataset the cell types belong to the CELLS, so what they imply is not a fact about the experiment. Measured on GSE295459 (eid 58319): the forward walk returned 14 rows, and all 8 organism parts among them were a soma location or part-of of a cell-type subject — an antibiotic study described by brain anatomy. Paul ruled the cut belongs here rather than at render time, where the terms would still reach audit and export. -
testABulkDatasetKeepsCellTypeSubjects
@Test @WithMockUser public void testABulkDatasetKeepsCellTypeSubjects()🛑 The control that keeps the cut honest. A constant cell type on a BULK experiment IS an experiment-level property, and what it implies is worth having — so the filter keys on isSingleCell and never on the category alone. -
testIncludeCellTypeSubjectsTurnsTheSingleCellCutOff
@Test @WithMockUser public void testIncludeCellTypeSubjectsTurnsTheSingleCellCutOff()...and a caller who asks for them gets them back. Without this the cut could not be turned off, and a suppression with no way to see what it removed is indistinguishable from an experiment that never carried the term — the next person asking why the cell type is missing would have nothing to read. -
testATermSeededReadIsNeverCellTypeFiltered
@Test @WithMockUser public void testATermSeededReadIsNeverCellTypeFiltered()A term-seeded read carries no dataset, so there is nothing to call single-cell and no cut. -
testATermSeededReadIsNotBarredByDefault
@Test @WithMockUser public void testATermSeededReadIsNotBarredByDefault()Positive control for the two above: a term-seeded read is NOT barred. A term card asking what is known about DMSO wants its role list; that is the answer there, not the noise. -
testRelationsExcludesRefutedByDefault
@Test @WithMockUser public void testRelationsExcludesRefutedByDefault()And it stays off unless asked, so exposing it changed no existing caller's results. -
testCanonicalUrisAreQueryable
@Test public void testCanonicalUrisAreQueryable()The canonicalization table has to be reachable by a client that resolves terms before asking Gemma — cab seeded a two-row local redirect table because ours was not queryable, and two authorities on one question is the thing this endpoint exists to prevent. -
testCanonicalUrisAnswersForATwinTheCorpusNeverUsed
@Test public void testCanonicalUrisAnswersForATwinTheCorpusNeverUsed()🛑 The regression guard for the whole point of this endpoint: a twin the CORPUS HAS NEVER USED must still be answered for.CLO_0001199 ('22RV1 cell') has zero annotations in Gemma; its twin CLO_0001200 ('22Rv1 cell') has 19. A table built from the terms the corpus uses cannot see the zero-usage member at all — and that is exactly the member cab's Tier-0 synonym table mints out of file order and asks us about. The first cut of this table was corpus-anchored, this row was missing, and following our own instruction to generate from it would have regressed the case that prompted it.
-
testCanonicalUrisCarryTheDecidingRule
@Test public void testCanonicalUrisCarryTheDecidingRule()Every row says which rule decided it, so a consumer can tell an ontology-intrinsic answer (R3 xref, R4 definition) from one that rests on our curators' spelling habits (R5 usage). -
testCanonicalUrisFilterByUri
@Test public void testCanonicalUrisFilterByUri()Filtering by URI returns that one row, and an unmapped URI returns none rather than a guess.
-