From 06a8a0bc37e9f69f4e808998365395df4007441f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Rodi=C3=A8re?= Date: Tue, 18 Sep 2018 11:12:00 +0200 Subject: [PATCH] HSEARCH-3212 Test that bridges are not applied if all their contributed fields are filtered out by @IndexedEmbedded filters --- .../definition/IndexedEmbeddedBaseIT.java | 87 +++++++++++++++++++ .../pojo/test/util/StartupStubBridge.java | 30 ++++--- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/mapping/definition/IndexedEmbeddedBaseIT.java b/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/mapping/definition/IndexedEmbeddedBaseIT.java index 601f9c1e4ee..ed262422b11 100644 --- a/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/mapping/definition/IndexedEmbeddedBaseIT.java +++ b/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/mapping/definition/IndexedEmbeddedBaseIT.java @@ -6,6 +6,8 @@ */ package org.hibernate.search.integrationtest.mapper.pojo.mapping.definition; +import static org.junit.Assert.assertEquals; + import java.lang.invoke.MethodHandles; import java.util.function.Consumer; import java.util.function.Function; @@ -13,8 +15,10 @@ import org.hibernate.search.engine.backend.document.model.dsl.ObjectFieldStorage; import org.hibernate.search.integrationtest.mapper.pojo.smoke.AnnotationMappingSmokeIT; import org.hibernate.search.integrationtest.mapper.pojo.smoke.ProgrammaticMappingSmokeIT; +import org.hibernate.search.integrationtest.mapper.pojo.test.util.StartupStubBridge; import org.hibernate.search.integrationtest.mapper.pojo.test.util.rule.JavaBeanMappingSetupHelper; import org.hibernate.search.mapper.javabean.JavaBeanMapping; +import org.hibernate.search.mapper.pojo.bridge.mapping.BridgeBuilder; import org.hibernate.search.mapper.pojo.mapping.PojoSearchManager; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId; import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Field; @@ -22,6 +26,8 @@ import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; import org.hibernate.search.util.impl.integrationtest.common.rule.BackendMock; import org.hibernate.search.util.impl.integrationtest.common.stub.backend.document.StubDocumentNode; +import org.hibernate.search.util.impl.test.annotation.TestForIssue; +import org.hibernate.search.util.impl.test.rule.StaticCounters; import org.junit.Rule; import org.junit.Test; @@ -46,6 +52,9 @@ public class IndexedEmbeddedBaseIT { @Rule public JavaBeanMappingSetupHelper setupHelper = new JavaBeanMappingSetupHelper( MethodHandles.lookup() ); + @Rule + public StaticCounters counters = new StaticCounters(); + @Test public void noParameter() { class IndexedEmbeddedLevel2 { @@ -343,6 +352,84 @@ public IndexedEmbeddedLevel1 getLevel1() { ); } + /** + * Check that bridges whose contributed fields are all filtered out are never applied. + */ + @Test + @TestForIssue(jiraKey = "HSEARCH-3212") + public void includePaths_excludesBridges() { + class IndexedEmbeddedLevel1 { + String level1Property; + public String getLevel1Property() { + return level1Property; + } + } + class IndexedEntity { + Integer id; + IndexedEmbeddedLevel1 level1; + public IndexedEntity(int id, String level1Property) { + this.id = id; + this.level1 = new IndexedEmbeddedLevel1(); + this.level1.level1Property = level1Property; + } + public Integer getId() { + return id; + } + public IndexedEmbeddedLevel1 getLevel1() { + return level1; + } + } + + StartupStubBridge.CounterKeys filteredOutBridgeCounterKeys = StartupStubBridge.createKeys(); + + BridgeBuilder filteredOutBridgeBuilder = + c -> new StartupStubBridge( filteredOutBridgeCounterKeys ); + + backendMock.expectSchema( INDEX_NAME, b -> b + .objectField( "level1", b2 -> b2 + .field( "level1IncludedField", String.class ) + ) + ); + JavaBeanMapping mapping = setupHelper.withBackendMock( backendMock ) + .withConfiguration( b -> { + b.addEntityType( IndexedEntity.class ); + b.programmaticMapping().type( IndexedEntity.class ) + .indexed( INDEX_NAME ) + .property( "id" ) + .documentId() + .property( "level1" ) + .indexedEmbedded() + .includePaths( "level1IncludedField" ); + b.programmaticMapping().type( IndexedEmbeddedLevel1.class ) + .bridge( filteredOutBridgeBuilder ) + .property( "level1Property" ) + .bridge( filteredOutBridgeBuilder ) + .field( "level1IncludedField" ) + .field( "filteredOut" ) + .valueBridge( filteredOutBridgeBuilder ); + } ) + .setup(); + backendMock.verifyExpectationsMet(); + + /* + * All the bridges that were filtered out should have been instantiated, + * but then immediately closed. + */ + assertEquals( 3, counters.get( filteredOutBridgeCounterKeys.instance ) ); + assertEquals( 0, counters.get( filteredOutBridgeCounterKeys.instance ) - counters.get( filteredOutBridgeCounterKeys.close ) ); + + doTestEmbeddedRuntime( + mapping, + id -> new IndexedEntity( id, "level1Value" ), + document -> document.objectField( "level1", b2 -> b2 + .field( "level1IncludedField", "level1Value" ) + ) + ); + + // The bridges that were filtered out should not have been used. + assertEquals( 0, counters.get( filteredOutBridgeCounterKeys.runtimeUse ) ); + } + private void doTestEmbeddedRuntime(JavaBeanMapping mapping, Function newEntityFunction, Consumer expectedDocumentContributor) { diff --git a/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/test/util/StartupStubBridge.java b/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/test/util/StartupStubBridge.java index aef83479040..ec9fbaa4ff7 100644 --- a/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/test/util/StartupStubBridge.java +++ b/integrationtest/mapper/pojo/src/test/java/org/hibernate/search/integrationtest/mapper/pojo/test/util/StartupStubBridge.java @@ -20,7 +20,13 @@ import org.hibernate.search.util.impl.test.rule.StaticCounters; /** - * A stub bridge for use in startup tests. Any runtime use of this bridge will fail. + * A stub bridge for use in tests where the bridge is only used on startup. + *

+ * This is useful if we know the bridge will be filtered out, or simply if we don't test runtime at all. + *

+ * This bridge contributes fields to the index schema where possible. + *

+ * Any runtime use of this bridge will simply increment a counter and throw an exception. *

* For our own convenience, all bridge types are implemented in the same class. */ @@ -29,6 +35,7 @@ public class StartupStubBridge RoutingKeyBridge, IdentifierBridge { public static class CounterKeys { public final StaticCounters.Key instance = StaticCounters.createKey(); + public final StaticCounters.Key runtimeUse = StaticCounters.createKey(); public final StaticCounters.Key close = StaticCounters.createKey(); private CounterKeys() { @@ -64,13 +71,15 @@ public void close() { @Override public void bind(PropertyBridgeBindingContext context) { // Add at least one field so that the bridge is not removed - context.getIndexSchemaElement().field( "fieldFromPropertyBridge" ).asString().createAccessor(); + context.getIndexSchemaElement().field( "startupStubBridgeFieldFromPropertyBridge" ) + .asString().createAccessor(); } @Override public void bind(TypeBridgeBindingContext context) { // Add at least one field so that the bridge is not removed - context.getIndexSchemaElement().field( "fieldFromTypeBridge" ).asString().createAccessor(); + context.getIndexSchemaElement().field( "startupStubBridgeFieldFromTypeBridge" ) + .asString().createAccessor(); } @Override @@ -80,35 +89,36 @@ public void bind(RoutingKeyBridgeBindingContext context) { @Override public void write(DocumentElement target, PojoElement source) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } @Override public Object cast(Object value) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } @Override public String toIndexedValue(Object value) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } @Override public String toRoutingKey(String tenantIdentifier, Object entityIdentifier, PojoElement source) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } @Override public String toDocumentIdentifier(Object propertyValue) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } @Override public Object fromDocumentIdentifier(String documentIdentifier) { - throw shouldNotBeUsed(); + throw unexpectedRuntimeUse(); } - private AssertionFailure shouldNotBeUsed() { + private AssertionFailure unexpectedRuntimeUse() { + StaticCounters.get().increment( counterKeys.runtimeUse ); return new AssertionFailure( "Instances of " + getClass().getSimpleName() + " are not supposed to be used at runtime," + " they should only be used to test the startup process."