summaryrefslogtreecommitdiffstats
path: root/application/src/test/scala/com
diff options
context:
space:
mode:
Diffstat (limited to 'application/src/test/scala/com')
-rw-r--r--application/src/test/scala/com/yahoo/application/ApplicationBuilderTest.scala74
-rw-r--r--application/src/test/scala/com/yahoo/application/container/JDiscContainerSearchTest.scala69
-rw-r--r--application/src/test/scala/com/yahoo/application/container/JDiscTest.scala198
-rw-r--r--application/src/test/scala/com/yahoo/application/container/handlers/TestHandler.scala23
-rw-r--r--application/src/test/scala/com/yahoo/application/container/jersey/JerseyTest.scala172
-rw-r--r--application/src/test/scala/com/yahoo/application/container/jersey/resources/TestResource.scala12
-rw-r--r--application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage1/NestedTestResource1.scala12
-rw-r--r--application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage2/NestedTestResource2.scala12
-rw-r--r--application/src/test/scala/com/yahoo/application/container/searchers/AddHitSearcher.scala23
9 files changed, 595 insertions, 0 deletions
diff --git a/application/src/test/scala/com/yahoo/application/ApplicationBuilderTest.scala b/application/src/test/scala/com/yahoo/application/ApplicationBuilderTest.scala
new file mode 100644
index 00000000000..255810b959b
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/ApplicationBuilderTest.scala
@@ -0,0 +1,74 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application
+
+import container.JDiscTest._
+import java.nio.file.Files
+import org.junit.Assert.{assertTrue, assertThat, fail}
+import org.junit.Test
+import com.yahoo.io.IOUtils
+import org.hamcrest.CoreMatchers.containsString
+
+/**
+ * @author tonytv
+ */
+ class ApplicationBuilderTest {
+ @Test
+ def query_profile_types_can_be_added() {
+ withApplicationBuilder { builder =>
+ builder.queryProfileType("MyProfileType",
+ <query-profile-type id="MyProfileType">
+ <field name="age" type="integer" />
+ <field name="profession" type="string" />
+ <field name="user" type="query-profile:MyUserProfile" />
+ </query-profile-type>)
+
+ assertTrue(Files.exists(builder.getPath.resolve("search/query-profiles/types/MyProfileType.xml")))
+ }
+ }
+
+
+ @Test
+ def query_profile_can_be_added() {
+ withApplicationBuilder { builder =>
+ builder.queryProfile("MyProfile",
+ <query-profile id="MyProfile">
+ <field name="message">Hello world!</field>
+ </query-profile>)
+
+ assertTrue(Files.exists(builder.getPath.resolve("search/query-profiles/MyProfile.xml")))
+ }
+ }
+
+ @Test
+ def rank_expression_can_be_added() {
+ withApplicationBuilder { builder =>
+ builder.rankExpression("myExpression", "content")
+ assertTrue(Files.exists(builder.getPath.resolve("searchdefinitions/myExpression.expression")))
+ }
+ }
+
+ @Test
+ def builder_cannot_be_reused() {
+ val builder = new ApplicationBuilder
+ builder.servicesXml(<jdisc version="1.0" />)
+
+ using(builder.build()) { builder => }
+
+ try {
+ builder.servicesXml("")
+ fail("Expected exception.")
+ } catch {
+ case e: RuntimeException => assertThat(e.getMessage, containsString("build method"))
+ }
+
+ }
+
+ def withApplicationBuilder(f: ApplicationBuilder => Unit) {
+ val builder = new ApplicationBuilder()
+ try {
+ f(builder)
+ } finally {
+ IOUtils.recursiveDeleteDir(builder.getPath.toFile)
+ }
+ }
+}
diff --git a/application/src/test/scala/com/yahoo/application/container/JDiscContainerSearchTest.scala b/application/src/test/scala/com/yahoo/application/container/JDiscContainerSearchTest.scala
new file mode 100644
index 00000000000..9d7e8ea26ab
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/JDiscContainerSearchTest.scala
@@ -0,0 +1,69 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container
+
+import org.junit.Test
+import searchers.AddHitSearcher
+import com.yahoo.component.ComponentSpecification
+import com.yahoo.search.Query
+import org.junit.Assert._
+import org.hamcrest.CoreMatchers._
+import org.hamcrest.Matchers.containsString;
+import JDiscTest.{fromServicesXml, using}
+
+
+/**
+ *
+ * @author gjoranv
+ * @since 5.1.15
+ */
+class JDiscContainerSearchTest {
+
+ @Test
+ def processing_and_rendering_works() {
+ val searcherId = classOf[AddHitSearcher].getName
+
+ using(containerWithSearch(searcherId))
+ { container =>
+ val rendered = container.search.processAndRender(ComponentSpecification.fromString("mychain"),
+ ComponentSpecification.fromString("DefaultRenderer"), new Query(""))
+ val renderedAsString = new String(rendered, "utf-8")
+ assertThat(renderedAsString, containsString(searcherId))
+ }
+ }
+
+ @Test
+ def searching_works() {
+ val searcherId = classOf[AddHitSearcher].getName
+
+ using(containerWithSearch(searcherId))
+ { container =>
+ val searching = container.search
+ val result = searching.process(ComponentSpecification.fromString("mychain"), new Query(""))
+
+ val hitTitle = result.hits().get(0).getField("title").asInstanceOf[String]
+ assertThat(hitTitle, is(searcherId))
+ }
+ }
+
+ def containerWithSearch(searcherId: String) = {
+ fromServicesXml(
+ <container version="1.0">
+ <search>
+ <chain id="mychain">
+ <searcher id={searcherId}/>
+ </chain>
+ </search>
+ </container>)
+ }
+
+ @Test(expected = classOf[UnsupportedOperationException])
+ def retrieving_search_from_container_without_search_is_illegal() {
+ using(JDiscTest.fromServicesXml(
+ <container version="1.0" />
+ ))
+ { container =>
+ container.search // throws
+ }
+ }
+
+}
diff --git a/application/src/test/scala/com/yahoo/application/container/JDiscTest.scala b/application/src/test/scala/com/yahoo/application/container/JDiscTest.scala
new file mode 100644
index 00000000000..727a77ee119
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/JDiscTest.scala
@@ -0,0 +1,198 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container
+
+import com.yahoo.container.Container
+import com.yahoo.jdisc.http.server.jetty.JettyHttpServer
+
+import scala.language.implicitConversions
+import handler.Request
+import org.junit.{Ignore, Test}
+import org.junit.Assert.{assertThat, assertNotNull, fail}
+import org.hamcrest.CoreMatchers.{is, containsString, hasItem}
+import java.nio.file.FileSystems
+import com.yahoo.search.Query
+import com.yahoo.component.ComponentSpecification
+import handlers.TestHandler
+import xml.{Node, Elem}
+import JDiscTest._
+import com.yahoo.application.{Networking, ApplicationBuilder}
+
+import scala.collection.convert.wrapAsScala._
+
+
+/**
+ * @author tonytv
+ * @author gjoranv
+ */
+class JDiscTest {
+ @Test
+ def jdisc_can_be_used_as_top_level_element() {
+ using(fromServicesXml(
+ <jdisc version="1.0">
+ <search />
+ </jdisc>))
+ { container =>
+ assertNotNull(container.search())
+ }
+ }
+
+ @Test
+ def jdisc_id_can_be_set() {
+ using(fromServicesXml(
+ <jdisc version="1.0" id="my-service-id">
+ <search />
+ </jdisc>))
+ { container =>
+ assertNotNull(container.search())
+ }
+ }
+
+ @Test
+ def jdisc_can_be_embedded_in_services_tag() {
+ using(fromServicesXml(
+ <services>
+ <jdisc version="1.0" id="my-service-id">
+ <search />
+ </jdisc>
+ </services>))
+ { container =>
+ assertNotNull(container.search())
+ }
+ }
+
+ @Test
+ def multiple_jdisc_elements_gives_exception() {
+ try {
+ using(fromServicesXml(
+ <services>
+ <jdisc version="1.0" id="id1" />
+ <jdisc version="1.0" />
+ <container version="1.0"/>
+ </services>))
+ { container => fail("expected exception")}
+ } catch {
+ case e: Exception => assertThat(e.getMessage, containsString("container id='', jdisc id='id1', jdisc id=''"))
+ }
+ }
+
+ @Test
+ def handleRequest_yields_response_from_correct_request_handler() {
+ using(fromServicesXml(
+ <container version="1.0">
+ <handler id="test-handler" class={classOf[TestHandler].getName}>
+ <binding>http://*/TestHandler</binding>
+ </handler>
+ </container>))
+ { container =>
+ val response = container.handleRequest(new Request("http://foo/TestHandler"))
+ assertThat(response.getBodyAsString, is(TestHandler.RESPONSE))
+ }
+ }
+
+ @Test
+ def load_searcher_from_bundle() {
+ using(JDisc.fromPath(FileSystems.getDefault.getPath("src/test/app-packages/searcher-app"), Networking.disable))
+ { container =>
+ val result = container.search.process(ComponentSpecification.fromString("default"),new Query("?query=ignored"))
+ assertThat(result.hits().get(0).getField("title").toString, is("Heal the World!"))
+ }
+ }
+
+ @Test
+ def document_types_can_be_accessed() {
+ using(new ApplicationBuilder().documentType("example", exampleDocument).
+ servicesXml(containerWithDocumentProcessing).
+ build())
+ { application =>
+ val container = application.getJDisc("jdisc")
+ val processing = container.documentProcessing()
+ assertThat(processing.getDocumentTypes.keySet(), hasItem("example"))
+ }
+ }
+
+ @Test
+ def annotation_types_can_be_accessed() {
+ using(new ApplicationBuilder().documentType("example",
+ s"""
+ |search example {
+ | ${exampleDocument}
+ | annotation exampleAnnotation {}
+ |}
+ """.stripMargin).
+ servicesXml(containerWithDocumentProcessing).
+ build())
+ { application =>
+ val container = application.getJDisc("jdisc")
+ val processing = container.documentProcessing()
+ assertThat(processing.getAnnotationTypes.keySet(), hasItem("exampleAnnotation"))
+ }
+ }
+
+ @Ignore //Enable this when static state has been removed.
+ @Test
+ def multiple_containers_can_be_run_in_parallel() {
+ def sendRequest(jdisc: JDisc) {
+ val response = jdisc.handleRequest(new Request("http://foo/TestHandler"))
+ assertThat(response.getBodyAsString, is(TestHandler.RESPONSE))
+ }
+
+ using(jdiscWithHttp()) { jdisc1 =>
+ using(jdiscWithHttp()) { jdisc2 =>
+ sendRequest(jdisc1)
+ sendRequest(jdisc2)
+ }
+ }
+ }
+}
+
+object JDiscTest {
+
+ def fromServicesXml(elem: Elem, networking: Networking = Networking.disable) =
+ JDisc.fromServicesXml(elem.toString(), networking)
+
+ def using[T <: AutoCloseable, U](t: T)(f: T => U ) = {
+ try {
+ f(t)
+ } finally {
+ t.close()
+ }
+ }
+
+ implicit def xmlToString(xml: Node): String = xml.toString()
+
+ val containerWithDocumentProcessing =
+ <jdisc version="1.0">
+ <http />
+ <document-processing />
+ </jdisc>
+
+ val exampleDocument =
+ """
+ |document example {
+ |
+ | field title type string {
+ | indexing: summary | index # How this field should be indexed
+ | weight: 75 # Ranking importancy of this field, used by the built in nativeRank feature
+ | header
+ | }
+ |}
+ """.stripMargin
+
+
+ def jdiscWithHttp() = {
+ fromServicesXml(
+ <jdisc version="1.0">
+ <handler id={classOf[TestHandler].getName} />
+ <http>
+ <server id="main" port="9999" />
+ </http>
+ </jdisc>)
+ }
+
+ def getListenPort: Int =
+ Container.get.getServerProviderRegistry.allComponents().collectFirst {
+ case server: JettyHttpServer => server.getListenPort
+ } getOrElse {
+ throw new RuntimeException("No http server found")
+ }
+}
diff --git a/application/src/test/scala/com/yahoo/application/container/handlers/TestHandler.scala b/application/src/test/scala/com/yahoo/application/container/handlers/TestHandler.scala
new file mode 100644
index 00000000000..067ba3f31f1
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/handlers/TestHandler.scala
@@ -0,0 +1,23 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.handlers
+
+import com.yahoo.jdisc.handler.{ResponseDispatch, ResponseHandler, AbstractRequestHandler}
+import TestHandler._
+
+/**
+ * @author gjoranv
+ * @since 5.1.15
+ */
+
+class TestHandler extends AbstractRequestHandler {
+ def handleRequest(request:JDiscRequest, handler: ResponseHandler) = {
+ val writer = ResponseDispatch.newInstance(com.yahoo.jdisc.Response.Status.OK).connectFastWriter(handler)
+ writer.write(RESPONSE)
+ writer.close()
+ null
+ }
+}
+object TestHandler {
+ val RESPONSE = "Hello, World!"
+ type JDiscRequest = com.yahoo.jdisc.Request
+}
diff --git a/application/src/test/scala/com/yahoo/application/container/jersey/JerseyTest.scala b/application/src/test/scala/com/yahoo/application/container/jersey/JerseyTest.scala
new file mode 100644
index 00000000000..2aee68f254a
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/jersey/JerseyTest.scala
@@ -0,0 +1,172 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.jersey
+
+import java.nio.file.{Files, Path, Paths}
+import javax.ws.rs.core.UriBuilder
+
+import com.yahoo.application.Networking
+import com.yahoo.application.container.JDiscTest._
+import com.yahoo.container.test.jars.jersey.resources.TestResourceBase
+import com.yahoo.container.test.jars.jersey.{resources => jarResources}
+import com.yahoo.osgi.maven.ProjectBundleClassPaths
+import com.yahoo.osgi.maven.ProjectBundleClassPaths.BundleClasspathMapping
+import org.apache.http.HttpResponse
+import org.apache.http.client.methods.HttpGet
+import org.apache.http.impl.client.HttpClientBuilder
+import org.apache.http.util.EntityUtils
+import org.hamcrest.CoreMatchers.is
+import org.junit.Assert._
+import org.junit.Test
+
+import scala.collection.JavaConverters._
+import scala.io.Source
+
+/**
+ * @author tonytv
+ */
+class JerseyTest {
+ type TestResourceClass = Class[_ <: TestResourceBase]
+
+ val testJar = Paths.get("target/test-jars/jersey-resources.jar")
+ val testClassesDirectory = "target/test-classes"
+ val bundleSymbolicName = "myBundle"
+
+ val classPathResources = Set(
+ classOf[resources.TestResource],
+ classOf[resources.nestedpackage1.NestedTestResource1],
+ classOf[resources.nestedpackage2.NestedTestResource2])
+
+ val jarFileResources = Set(
+ classOf[jarResources.TestResource],
+ classOf[com.yahoo.container.test.jars.jersey.resources.nestedpackage1.NestedTestResource1],
+ classOf[com.yahoo.container.test.jars.jersey.resources.nestedpackage2.NestedTestResource2])
+
+ @Test
+ def jersey_resources_on_classpath_can_be_invoked_from_application(): Unit = {
+ saveMainBundleClassPathMappings(testClassesDirectory)
+
+ with_jersey_resources() { httpGetter =>
+ assertResourcesResponds(classPathResources, httpGetter)
+ }
+ }
+
+ @Test
+ def jersey_resources_in_provided_dependencies_can_be_invoked_from_application(): Unit = {
+ val providedDependency = new BundleClasspathMapping(bundleSymbolicName, List(testClassesDirectory).asJava)
+
+ save(new ProjectBundleClassPaths(
+ new BundleClasspathMapping("main", List().asJava),
+ List(providedDependency).asJava))
+
+ with_jersey_resources() { httpGetter =>
+ assertResourcesResponds(classPathResources, httpGetter)
+ }
+ }
+
+ @Test
+ def jersey_resource_on_classpath_can_be_filtered_using_packages(): Unit = {
+ saveMainBundleClassPathMappings(testClassesDirectory)
+
+ with_jersey_resources(
+ packagesToScan = List(
+ "com.yahoo.application.container.jersey.resources",
+ "com.yahoo.application.container.jersey.resources.nestedpackage1"))
+ { httpGetter =>
+ val nestedResource2 = classOf[resources.nestedpackage2.NestedTestResource2]
+
+ assertDoesNotRespond(nestedResource2, httpGetter)
+ assertResourcesResponds(classPathResources - nestedResource2, httpGetter)
+ }
+ }
+
+ @Test
+ def jersey_resource_in_jar_can_be_invoked_from_application(): Unit = {
+ saveMainBundleJarClassPathMappings(testJar)
+
+ with_jersey_resources() { httpGetter =>
+ assertResourcesResponds(jarFileResources, httpGetter)
+ }
+ }
+
+ @Test
+ def jersey_resource_in_jar_can_be_filtered_using_packages(): Unit = {
+ saveMainBundleJarClassPathMappings(testJar)
+
+ with_jersey_resources(
+ packagesToScan = List(
+ "com.yahoo.container.test.jars.jersey.resources",
+ "com.yahoo.container.test.jars.jersey.resources.nestedpackage1"))
+ { httpGetter =>
+ val nestedResource2 = classOf[com.yahoo.container.test.jars.jersey.resources.nestedpackage2.NestedTestResource2]
+
+ assertDoesNotRespond(nestedResource2, httpGetter)
+ assertResourcesResponds(jarFileResources - nestedResource2, httpGetter)
+ }
+ }
+
+ def with_jersey_resources(packagesToScan: List[String] = List())( f: HttpGetter => Unit): Unit = {
+ val packageElements = packagesToScan.map { p => <package>{p}</package>}
+
+ using(fromServicesXml(
+ <services>
+ <jdisc version="1.0" id="default" jetty="true">
+ <rest-api path="rest-api" jersey2="true">
+ <components bundle={bundleSymbolicName}>
+ { packageElements }
+ </components>
+ </rest-api>
+ <http>
+ <server id="mainServer" port="0" />
+ </http>
+ </jdisc>
+ </services>,
+ Networking.enable)) { jdisc =>
+
+
+ def httpGetter(path: HttpPath) = {
+ val client = HttpClientBuilder.create().build()
+ client.execute(new HttpGet(s"http://localhost:$getListenPort/rest-api/${path.stripPrefix("/")}"))
+ }
+
+ f(httpGetter)
+ }
+ }
+
+ def assertResourcesResponds(resourceClasses: Traversable[TestResourceClass], httpGetter: HttpGetter): Unit = {
+ for (resource <- resourceClasses) {
+ val response = httpGetter(path(resource))
+ assertThat(s"Failed sending response to $resource", response.getStatusLine.getStatusCode, is(200))
+
+ val content = Source.fromInputStream(response.getEntity.getContent).mkString
+ assertThat(content, is(TestResourceBase.content(resource)))
+ }
+ }
+
+ def assertDoesNotRespond(resourceClass: TestResourceClass, httpGetter: HttpGetter): Unit = {
+ val response = httpGetter(path(resourceClass))
+ assertThat(response.getStatusLine.getStatusCode, is(404))
+ EntityUtils.consume(response.getEntity)
+ }
+
+ def saveMainBundleJarClassPathMappings(jarFile: Path): Unit = {
+ assertTrue(s"Couldn't find file $jarFile, please remember to run mvn process-test-resources first.", Files.isRegularFile(jarFile))
+ saveMainBundleClassPathMappings(jarFile.toAbsolutePath.toString)
+ }
+
+ def saveMainBundleClassPathMappings(classPathElement: String): Unit = {
+ val mainBundleClassPathMappings = new BundleClasspathMapping(bundleSymbolicName, List(classPathElement).asJava)
+ save(new ProjectBundleClassPaths(mainBundleClassPathMappings, List().asJava))
+ }
+
+ def save(projectBundleClassPaths: ProjectBundleClassPaths): Unit = {
+ val path = Paths.get(testClassesDirectory).resolve(ProjectBundleClassPaths.CLASSPATH_MAPPINGS_FILENAME)
+ ProjectBundleClassPaths.save(path, projectBundleClassPaths)
+ }
+
+ def path(resourceClass: TestResourceClass) = {
+ UriBuilder.fromResource(resourceClass).build().toString
+ }
+
+ type HttpPath = String
+ type HttpGetter = HttpPath => HttpResponse
+}
diff --git a/application/src/test/scala/com/yahoo/application/container/jersey/resources/TestResource.scala b/application/src/test/scala/com/yahoo/application/container/jersey/resources/TestResource.scala
new file mode 100644
index 00000000000..5dc16102d8e
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/jersey/resources/TestResource.scala
@@ -0,0 +1,12 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.jersey.resources
+
+import javax.ws.rs.Path
+import com.yahoo.container.test.jars.jersey.resources.TestResourceBase
+
+
+/**
+ * @author tonytv
+ */
+@Path("/test-resource")
+class TestResource extends TestResourceBase
diff --git a/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage1/NestedTestResource1.scala b/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage1/NestedTestResource1.scala
new file mode 100644
index 00000000000..c50d7cd6f57
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage1/NestedTestResource1.scala
@@ -0,0 +1,12 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.jersey.resources.nestedpackage1
+
+import javax.ws.rs.Path
+
+import com.yahoo.container.test.jars.jersey.resources.TestResourceBase
+
+/**
+ * @author tonytv
+ */
+@Path("/nested-test-resource1")
+class NestedTestResource1 extends TestResourceBase
diff --git a/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage2/NestedTestResource2.scala b/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage2/NestedTestResource2.scala
new file mode 100644
index 00000000000..50f0054b6ec
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/jersey/resources/nestedpackage2/NestedTestResource2.scala
@@ -0,0 +1,12 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.jersey.resources.nestedpackage2
+
+import javax.ws.rs.Path
+
+import com.yahoo.container.test.jars.jersey.resources.TestResourceBase
+
+/**
+ * @author tonytv
+ */
+@Path("/nested-test-resource2")
+class NestedTestResource2 extends TestResourceBase
diff --git a/application/src/test/scala/com/yahoo/application/container/searchers/AddHitSearcher.scala b/application/src/test/scala/com/yahoo/application/container/searchers/AddHitSearcher.scala
new file mode 100644
index 00000000000..e3a6cd031bc
--- /dev/null
+++ b/application/src/test/scala/com/yahoo/application/container/searchers/AddHitSearcher.scala
@@ -0,0 +1,23 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.application.container.searchers
+
+import com.yahoo.search.{Searcher, Result, Query}
+import com.yahoo.search.searchchain.Execution
+import com.yahoo.search.result.Hit
+
+
+class AddHitSearcher extends Searcher {
+
+ override def search(query: Query, execution: Execution) : Result = {
+ val result = execution.search(query)
+ result.hits().add(dummyHit)
+
+ result
+ }
+
+ private def dummyHit = {
+ val hit = new Hit("dummy")
+ hit.setField("title", getId.getName)
+ hit
+ }
+}