Skip to content

Commit

Permalink
HSEARCH-3212 Test that bridges are not applied if all their contribut…
Browse files Browse the repository at this point in the history
…ed fields are filtered out by @IndexedEmbedded filters
  • Loading branch information
yrodiere committed Sep 24, 2018
1 parent 48dab0a commit 06a8a0b
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 10 deletions.
Expand Up @@ -6,22 +6,28 @@
*/
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;

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;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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<StartupStubBridge> 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 <E> void doTestEmbeddedRuntime(JavaBeanMapping mapping,
Function<Integer, E> newEntityFunction,
Consumer<StubDocumentNode.Builder> expectedDocumentContributor) {
Expand Down
Expand Up @@ -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.
* <p>
* This is useful if we know the bridge will be filtered out, or simply if we don't test runtime at all.
* <p>
* This bridge contributes fields to the index schema where possible.
* <p>
* Any runtime use of this bridge will simply increment a counter and throw an exception.
* <p>
* For our own convenience, all bridge types are implemented in the same class.
*/
Expand All @@ -29,6 +35,7 @@ public class StartupStubBridge
RoutingKeyBridge, IdentifierBridge<Object> {
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() {
Expand Down Expand Up @@ -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
Expand All @@ -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."
Expand Down

0 comments on commit 06a8a0b

Please sign in to comment.