diff options
Diffstat (limited to 'application/src/test/scala/com')
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 + } +} |