diff options
Diffstat (limited to 'application/src/test/java')
24 files changed, 1894 insertions, 0 deletions
diff --git a/application/src/test/java/com/yahoo/application/ApplicationFacade.java b/application/src/test/java/com/yahoo/application/ApplicationFacade.java new file mode 100644 index 00000000000..2c8ff05e2cc --- /dev/null +++ b/application/src/test/java/com/yahoo/application/ApplicationFacade.java @@ -0,0 +1,161 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application; + +import com.google.common.annotations.Beta; +import com.yahoo.application.container.handler.Request; +import com.yahoo.application.container.handler.Response; +import com.yahoo.component.Component; +import com.yahoo.component.ComponentId; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.container.Container; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; +import com.yahoo.document.DocumentOperation; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.service.ClientProvider; +import com.yahoo.jdisc.service.ServerProvider; +import com.yahoo.search.Query; +import com.yahoo.search.Result; + +/** + * Convenience access methods into some application content. + * These are not generally applicable and are therefore in test. + * This can also auto close its application. + * + * @author bratseth + */ +public class ApplicationFacade implements AutoCloseable { + + private static final String DEFAULT_CHAIN = "default"; + + private final Application application; + + public ApplicationFacade(Application application) { + this.application = application; + } + + /** Returns the application wrapped by this */ + public Application application() { return application; } + + /** + * Process a single document through the default chain. + * + * @param document the document to process + * @return the docproc return status + */ + @Beta + public DocumentProcessor.Progress process(final DocumentOperation document) { + return process(DEFAULT_CHAIN, document); + } + + /** + * Process a single document through the default chain. + * + * @param chain process using this chain + * @param op the document operation to process + * @return the docproc return status + */ + @Beta + public DocumentProcessor.Progress process(String chain, final DocumentOperation op) { + return application.getJDisc("default").documentProcessing().process(ComponentSpecification.fromString(chain), + Processing.of(op)); + } + + /** + * Pass a processing object through the + * + * @param processing the processing object to process + * @return the docproc return status + */ + @Beta + public DocumentProcessor.Progress process(final Processing processing) { + return process(DEFAULT_CHAIN, processing); + } + + /** + * Pass a processing object through the + * + * @param chain process using this chain + * @param processing the processing object to process + * @return the docproc return status + */ + @Beta + public DocumentProcessor.Progress process(String chain, final Processing processing) { + return application.getJDisc("default").documentProcessing().process(ComponentSpecification.fromString(chain), processing); + } + + /** + * Pass query object to the default search chain + * + * @param query the query + * @return the search result + */ + @Beta + public Result search(final Query query) { + return application.getJDisc("default").search().process(ComponentSpecification.fromString(DEFAULT_CHAIN), query); + } + + /** + * Pass query object to the default search chain + * + * @param chain the search chain to use + * @param query the query + * @return the search result + */ + @Beta + public Result search(String chain, final Query query) { + return application.getJDisc("default").search().process(ComponentSpecification.fromString(chain), query); + } + + /** + * Pass query object to the default search chain + * + * @param request the request to process + * @return the response object + */ + @Beta + public Response handleRequest(Request request) { + return application.getJDisc("default").handleRequest(request); + } + + /** + * @param componentId name of the component (usually YourClass.class.getName()) + * @return the component object, or null if not found + */ + @Beta + public Component getComponentById(String componentId) { + return Container.get().getComponentRegistry().getComponent(new ComponentId(componentId)); + } + + /** + * @param componentId name of the component (usually YourClass.class.getName()) + * @return the request handler object, or null if not found + */ + @Beta + public RequestHandler getRequestHandlerById(String componentId) { + return Container.get().getRequestHandlerRegistry().getComponent(new ComponentId(componentId)); + } + + /** + * @param componentId name of the component (usually YourClass.class.getName()) + * @return the client object, or null if not found + */ + @Beta + public ClientProvider getClientById(String componentId) { + return Container.get().getClientProviderRegistry().getComponent(new ComponentId(componentId)); + } + + /** + * @param componentId name of the component (usually YourClass.class.getName()) + * @return the client object, or null if not found + */ + @Beta + public ServerProvider getServerById(String componentId) { + return Container.get().getServerProviderRegistry().getComponent(new ComponentId(componentId)); + } + + @Override + public void close() throws Exception { + application.close(); + } +} diff --git a/application/src/test/java/com/yahoo/application/ApplicationTest.java b/application/src/test/java/com/yahoo/application/ApplicationTest.java new file mode 100644 index 00000000000..7f14a3e4e16 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/ApplicationTest.java @@ -0,0 +1,373 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application; + +import com.yahoo.application.container.MockClient; +import com.yahoo.application.container.MockServer; +import com.yahoo.application.container.docprocs.MockDispatchDocproc; +import com.yahoo.application.container.docprocs.MockDocproc; +import com.yahoo.application.container.handler.Request; +import com.yahoo.application.container.handler.Response; +import com.yahoo.application.container.handlers.MockHttpHandler; +import com.yahoo.application.container.renderers.MockRenderer; +import com.yahoo.application.container.searchers.MockSearcher; +import com.yahoo.component.Component; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; +import com.yahoo.document.Document; +import com.yahoo.document.DocumentPut; +import com.yahoo.document.DocumentRemove; +import com.yahoo.document.DocumentType; +import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.handler.SearchHandler; +import com.yahoo.vespa.defaults.Defaults; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.DefaultHttpClient; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ConnectException; +import java.net.ServerSocket; +import java.util.Map; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import static com.yahoo.application.container.JDiscTest.getListenPort; + +/** + * @author bratseth + */ +public class ApplicationTest { + + @Test + public void minimal_application_can_be_constructed() throws Exception { + try (Application application = Application.fromServicesXml("<jdisc version=\"1.0\"/>", Networking.disable)) { + } + } + + /** Tests that an application with search chains referencing a content cluster can be constructed. */ + @Test + public void container_and_referenced_content() throws Exception { + try (Application application = + Application.fromApplicationPackage(new File("src/test/app-packages/withcontent"), Networking.disable)) { + Result result = application.getJDisc("default").search().process(new ComponentSpecification("default"), + new Query("?query=substring:foobar&tracelevel=3")); + assertEquals("AND substring:fo substring:oo substring:ob substring:ba substring:ar", result.hits().get("hasQuery").getQuery().getModel().getQueryTree().toString()); + } + } + + private void printTrace(Result result) { + for (String message : result.getQuery().getContext(true).getTrace().traceNode().descendants(String.class)) + System.out.println(message); + } + + @Test + public void empty_container() throws Exception { + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container())))) { + try { + app.process(new DocumentRemove(null)); + fail("expected exception"); + } catch (Exception ignore) { + // no op + } + + try { + app.process(new Processing()); + fail("expected exception"); + } catch (Exception ignore) { + // no op + } + + try { + app.search(new Query("?foo")); + fail("expected exception"); + } catch (Exception ignore) { + // no op + } + } + } + + @Test + public void config() throws Exception { + try ( + ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .documentProcessor("docproc", "default", MockDocproc.class) + .config(new MockApplicationConfig(new MockApplicationConfig.Builder() + .mystruct(new MockApplicationConfig.Mystruct.Builder().id("structid").value("structvalue")) + .mystructlist(new MockApplicationConfig.Mystructlist.Builder().id("listid1").value("listvalue1")) + .mystructlist(new MockApplicationConfig.Mystructlist.Builder().id("listid2").value("listvalue2")) + .mylist("item1") + .mylist("item2") + .mymap("key1", "value1") + .mymap("key2", "value2") + .mymapstruct("key1", new MockApplicationConfig.Mymapstruct.Builder().id("mapid1").value("mapvalue1")) + .mymapstruct("key2", new MockApplicationConfig.Mymapstruct.Builder().id("mapid2").value("mapvalue2"))))))) + ) { + + MockDocproc docproc = (MockDocproc) app.getComponentById("docproc@default"); + assertNotNull(docproc); + + // struct + assertEquals(docproc.getConfig().mystruct().id(), "structid"); + assertEquals(docproc.getConfig().mystruct().value(), "structvalue"); + + // struct list + assertEquals(docproc.getConfig().mystructlist().size(), 2); + assertEquals(docproc.getConfig().mystructlist().get(0).id(), "listid1"); + assertEquals(docproc.getConfig().mystructlist().get(0).value(), "listvalue1"); + assertEquals(docproc.getConfig().mystructlist().get(1).id(), "listid2"); + assertEquals(docproc.getConfig().mystructlist().get(1).value(), "listvalue2"); + + // list + assertEquals(docproc.getConfig().mylist().size(), 2); + assertEquals(docproc.getConfig().mylist().get(0), "item1"); + assertEquals(docproc.getConfig().mylist().get(1), "item2"); + + // map + assertEquals(docproc.getConfig().mymap().size(), 2); + assertTrue(docproc.getConfig().mymap().containsKey("key1")); + assertEquals(docproc.getConfig().mymap().get("key1"), "value1"); + assertTrue(docproc.getConfig().mymap().containsKey("key2")); + assertEquals(docproc.getConfig().mymap().get("key2"), "value2"); + + // map struct + assertEquals(docproc.getConfig().mymapstruct().size(), 2); + assertTrue(docproc.getConfig().mymapstruct().containsKey("key1")); + assertEquals(docproc.getConfig().mymapstruct().get("key1").id(), "mapid1"); + assertEquals(docproc.getConfig().mymapstruct().get("key1").value(), "mapvalue1"); + assertTrue(docproc.getConfig().mymapstruct().containsKey("key2")); + assertEquals(docproc.getConfig().mymapstruct().get("key2").id(), "mapid2"); + assertEquals(docproc.getConfig().mymapstruct().get("key2").value(), "mapvalue2"); + } + } + + @Test + public void handler() throws Exception { + try ( + ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .handler("http://*/*", MockHttpHandler.class)))) + ) { + + RequestHandler handler = app.getRequestHandlerById(MockHttpHandler.class.getName()); + assertNotNull(handler); + + Request request = new Request("http://localhost:" + Defaults.getDefaults().vespaWebServicePort() + "/"); + Response response = app.handleRequest(request); + assertNotNull(response); + assertEquals(response.getStatus(), 200); + assertEquals(response.getBodyAsString(), "OK"); + + request = new Request("http://localhost"); + response = app.handleRequest(request); + assertNotNull(response); + assertEquals(response.getStatus(), 200); + assertEquals(response.getBodyAsString(), "OK"); + + request = new Request("http://localhost/query=foo"); + response = app.handleRequest(request); + assertNotNull(response); + assertEquals(response.getStatus(), 200); + assertEquals(response.getBodyAsString(), "OK"); + } + } + + @Test + public void renderer() throws Exception { + try ( + ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .renderer("mock", MockRenderer.class)))) + ) { + + Request request = new Request("http://localhost:" + Defaults.getDefaults().vespaWebServicePort() + "/search/?format=mock"); + Response response = app.handleRequest(request); + assertNotNull(response); + assertEquals(response.getStatus(), 200); + assertEquals(response.getBodyAsString(), "<mock hits=\"0\" />"); + } + } + + @Test + public void search_default() throws Exception { + try ( + ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .searcher(MockSearcher.class)))) + ) { + Result result = app.search(new Query("?query=foo")); + assertEquals(1, result.hits().size()); + } + } + + @Test + public void search() throws Exception { + try ( + ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .searcher("foo", MockSearcher.class)))) + ) { + Result result = app.search("foo", new Query("?query=foo")); + assertEquals(1, result.hits().size()); + } + } + + @Test + public void builder_with_networking() throws Exception { + try ( + Application app = Application.fromBuilder(new Application.Builder().networking(Networking.enable).container("default", new Application.Builder.Container().handler("http://*/*", MockHttpHandler.class))) + ) { + DefaultHttpClient client = new DefaultHttpClient(); + HttpResponse response = client.execute(new HttpGet("http://localhost:" + getListenPort() + "/query=foo")); + assertEquals(200, response.getStatusLine().getStatusCode()); + BufferedReader r = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); + String line; + StringBuilder sb = new StringBuilder(); + while ((line = r.readLine()) != null) { + sb.append(line).append("\n"); + } + assertEquals("OK\n", sb.toString()); + } + } + + @Test + public void document_type() throws Exception { + try ( + Application app = Application.fromBuilder(new Application.Builder() + .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd"))) + .container("default", new Application.Builder.Container() + .documentProcessor(MockDocproc.class) + .config(new MockApplicationConfig(new MockApplicationConfig.Builder().mystruct(new MockApplicationConfig.Mystruct.Builder().id("foo").value("bar")))))) + ) { + Map<String, DocumentType> typeMap = app.getJDisc("jdisc").documentProcessing().getDocumentTypes(); + assertNotNull(typeMap); + assertTrue(typeMap.containsKey("test")); + } + } + + @Test + public void get_search_handler() throws Exception { + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container().search(true))))) { + SearchHandler searchHandler = (SearchHandler) app.getRequestHandlerById("com.yahoo.search.handler.SearchHandler"); + assertNotNull(searchHandler); + } + } + + @Test + public void component() throws Exception { + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .component(MockSearcher.class))))) { + Component c = app.getComponentById(MockSearcher.class.getName()); + assertNotNull(c); + } + } + + @Test + public void component_with_config() throws Exception { + MockApplicationConfig config = new MockApplicationConfig(new MockApplicationConfig.Builder().mystruct(new MockApplicationConfig.Mystruct.Builder().id("foo").value("bar"))); + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .component("foo", MockDocproc.class, config))))) { + Component c = app.getComponentById("foo"); + assertNotNull(c); + } + } + + @Test + public void client() throws Exception { + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder() + .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd"))) + .container("default", new Application.Builder.Container() + .client("mbus://*/*", MockClient.class) + .documentProcessor(MockDispatchDocproc.class) + )) + )) { + + Map<String, DocumentType> typeMap = app.application().getJDisc("jdisc").documentProcessing().getDocumentTypes(); + assertNotNull(typeMap); + + DocumentType docType = typeMap.get("test"); + Document doc = new Document(docType, "id:foo:test::bar"); + doc.setFieldValue("title", "hello"); + + assertEquals(DocumentProcessor.Progress.DONE, app.process(new DocumentPut(doc))); + + MockClient client = (MockClient) app.getClientById(MockClient.class.getName()); + assertNotNull(client); + assertEquals(1, client.getCounter()); + + MockDispatchDocproc docproc = (MockDispatchDocproc) app.getComponentById(MockDispatchDocproc.class.getName() + "@default"); + assertNotNull(docproc); + assertEquals(1, docproc.getResponses().size()); + assertEquals(200, docproc.getResponses().get(0).getStatus()); + } + } + + @Test + public void server() throws Exception { + try (ApplicationFacade app = new ApplicationFacade(Application.fromBuilder(new Application.Builder().container("default", new Application.Builder.Container() + .server("foo", MockServer.class))) + )) { + MockServer server = (MockServer) app.getServerById("foo"); + assertNotNull(server); + assertTrue(server.isStarted()); + } + } + + @Test + public void query_profile() throws Exception { + try (Application app = Application.fromBuilder(new Application.Builder() + .queryProfile("default", "<query-profile id=\"default\">\n" + + "<field name=\"defaultage\">7d</field>\n" + + "</query-profile>") + .queryProfileType("type", "<query-profile-type id=\"type\">\n" + + "<field name=\"defaultage\" type=\"string\" />\n" + + "</query-profile-type>") + .rankExpression("re", "commonfirstphase(globalstaticrank)") + .documentType("test", IOUtils.toString(this.getClass().getResourceAsStream("/test.sd"))) + .container("default", new Application.Builder.Container() + .search(true) + ))) { + } + } + + @Test(expected = ConnectException.class) + public void http_interface_is_off_when_networking_is_disabled() throws Exception { + int httpPort = getFreePort(); + try (Application application = Application.fromServicesXml(servicesXmlWithServer(httpPort), Networking.disable)) { + HttpClient client = new DefaultHttpClient(); + int statusCode = client.execute(new HttpGet("http://localhost:" + httpPort)).getStatusLine().getStatusCode(); + fail("Networking.disable is specified, but the network interface is enabled! Got status code: " + statusCode); + } + } + + @Test + public void http_interface_is_on_when_networking_is_enabled() throws Exception { + int httpPort = getFreePort(); + try (Application application = Application.fromServicesXml(servicesXmlWithServer(httpPort), Networking.enable)) { + HttpClient client = new DefaultHttpClient(); + int statusCode = client.execute(new HttpGet("http://localhost:" + httpPort)).getStatusLine().getStatusCode(); + assertEquals(200, statusCode); + } + } + + private static int getFreePort() throws IOException { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } + } + + private static String servicesXmlWithServer(int port) { + return "<jdisc version='1.0'>" + + " <http> <server port='" + port +"' id='foo'/> </http>" + + "</jdisc>"; + } + +} diff --git a/application/src/test/java/com/yahoo/application/MockResultSearcher.java b/application/src/test/java/com/yahoo/application/MockResultSearcher.java new file mode 100644 index 00000000000..2dd3a34a6ec --- /dev/null +++ b/application/src/test/java/com/yahoo/application/MockResultSearcher.java @@ -0,0 +1,22 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application; + +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.Searcher; +import com.yahoo.search.result.Hit; +import com.yahoo.search.searchchain.Execution; + +/** + * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a> + */ +public class MockResultSearcher extends Searcher { + + @Override + public Result search(Query query, Execution execution) { + Result result = new Result(query); + result.hits().add(new Hit("hasQuery", query)); + return result; + } + +} diff --git a/application/src/test/java/com/yahoo/application/TestDocProc.java b/application/src/test/java/com/yahoo/application/TestDocProc.java new file mode 100644 index 00000000000..1631c7cd812 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/TestDocProc.java @@ -0,0 +1,23 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application; + +import com.yahoo.config.subscription.ConfigSubscriber; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; +import com.yahoo.document.config.DocumentmanagerConfig; + +/** + * @author bratseth + */ +public class TestDocProc extends DocumentProcessor { + + public TestDocProc(DocumentmanagerConfig config) { + new ConfigSubscriber().subscribe(DocumentmanagerConfig.class, "client"); + } + + @Override + public Progress process(Processing processing) { + return Progress.DONE; + } + +} diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java new file mode 100644 index 00000000000..d6c0e08a570 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerDocprocTest.java @@ -0,0 +1,153 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container; + +import com.yahoo.application.Application; +import com.yahoo.application.ApplicationBuilder; +import com.yahoo.application.Networking; +import com.yahoo.component.ComponentId; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.application.container.docprocs.Rot13DocumentProcessor; +import com.yahoo.container.Container; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.document.Document; +import com.yahoo.document.DocumentPut; +import com.yahoo.document.DocumentType; +import com.yahoo.processing.execution.chain.ChainRegistry; +import org.junit.Before; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.hamcrest.CoreMatchers.sameInstance; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class JDiscContainerDocprocTest { + + private static final String DOCUMENT = "document music {\n" + + " field title type string { }\n" + + "}\n"; + private static final String CHAIN_NAME = "myChain"; + + + private static String getXML(String chainName, String... processorIds) { + String xml = + "<container version=\"1.0\">\n" + + " <document-processing>\n" + + " <chain id=\"" + chainName + "\">\n"; + for (String processorId : processorIds) { + xml += " <documentprocessor id=\"" + processorId + "\"/>\n"; + } + xml += + " </chain>\n" + + " </document-processing>\n" + + "</container>\n"; + return xml; + } + + @Before + public void resetContainer() { + Container.resetInstance(); + } + + @Test + public void requireThatBasicDocumentProcessingWorks() throws Exception { + try (Application app = new ApplicationBuilder() + .servicesXml(getXML(CHAIN_NAME, Rot13DocumentProcessor.class.getCanonicalName())) + .documentType("music", DOCUMENT).build()) { + + JDisc container = app.getJDisc("container"); + DocumentProcessing docProc = container.documentProcessing(); + DocumentType type = docProc.getDocumentTypes().get("music"); + + ChainRegistry<DocumentProcessor> chains = docProc.getChains(); + assertTrue(chains.allComponentsById().containsKey(new ComponentId(CHAIN_NAME))); + + Document doc = new Document(type, "doc:this:is:a:great:album"); + doc.setFieldValue("title", "Great Album!"); + com.yahoo.docproc.Processing processing; + DocumentProcessor.Progress progress; + DocumentPut put = new DocumentPut(doc); + + processing = com.yahoo.docproc.Processing.of(put); + progress = docProc.process(ComponentSpecification.fromString(CHAIN_NAME), processing); + assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE)); + assertThat(doc.getFieldValue("title").toString(), equalTo("Terng Nyohz!")); + + processing = com.yahoo.docproc.Processing.of(put); + progress = docProc.process(ComponentSpecification.fromString(CHAIN_NAME), processing); + assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE)); + assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!")); + } + } + + @Test + public void requireThatLaterDocumentProcessingWorks() throws Exception { + try (Application app = new ApplicationBuilder() + .servicesXml(getXML(CHAIN_NAME, Rot13DocumentProcessor.class.getCanonicalName())) + .networking(Networking.disable) + .documentType("music", DOCUMENT).build()) { + JDisc container = app.getJDisc("container"); + DocumentProcessing docProc = container.documentProcessing(); + DocumentType type = docProc.getDocumentTypes().get("music"); + + ChainRegistry<DocumentProcessor> chains = docProc.getChains(); + assertTrue(chains.allComponentsById().containsKey(new ComponentId(CHAIN_NAME))); + + Document doc = new Document(type, "doc:this:is:a:great:album"); + doc.setFieldValue("title", "Great Album!"); + com.yahoo.docproc.Processing processing; + DocumentProcessor.Progress progress; + DocumentPut put = new DocumentPut(doc); + + processing = com.yahoo.docproc.Processing.of(put); + + progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing); + assertThat(progress, instanceOf(DocumentProcessor.LaterProgress.class)); + assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!")); + + progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing); + assertThat(progress, instanceOf(DocumentProcessor.LaterProgress.class)); + assertThat(doc.getFieldValue("title").toString(), equalTo("Great Album!")); + + progress = docProc.processOnce(ComponentSpecification.fromString(CHAIN_NAME), processing); + assertThat(progress, sameInstance(DocumentProcessor.Progress.DONE)); + assertThat(doc.getFieldValue("title").toString(), equalTo("Terng Nyohz!")); + } + } + + @Test(expected = IllegalArgumentException.class) + public void requireThatUnknownChainThrows() { + try (JDisc container = JDisc.fromServicesXml( + getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()), + Networking.disable)) { + container.documentProcessing().process(ComponentSpecification.fromString("unknown"), + new com.yahoo.docproc.Processing()); + } + + } + + + @Test(expected = UnsupportedOperationException.class) + public void requireThatProcessingFails() { + try (JDisc container = JDisc.fromServicesXml( + getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()), + Networking.disable)) { + container.processing(); + } + + } + + @Test(expected = UnsupportedOperationException.class) + public void requireThatSearchFails() { + try (JDisc container = JDisc.fromServicesXml( + getXML("foo", Rot13DocumentProcessor.class.getCanonicalName()), + Networking.disable)) { + container.search(); + } + + } +} diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java new file mode 100644 index 00000000000..a95068ce3a4 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerProcessingTest.java @@ -0,0 +1,125 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container; + +import com.yahoo.application.Networking; +import com.yahoo.application.container.processors.Rot13Processor; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.container.Container; +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.Matchers.containsString; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class JDiscContainerProcessingTest { + + private static String getXML(String chainName, String... processorIds) { + String xml = + "<container version=\"1.0\">\n" + + " <processing>\n" + + " <chain id=\"" + chainName + "\">\n"; + for (String processorId : processorIds) { + xml += " <processor id=\"" + processorId + "\"/>\n"; + } + xml += + " </chain>\n" + + " </processing>\n" + + "</container>\n"; + return xml; + } + + @Before + public void resetContainer() { + Container.resetInstance(); + } + + @Test + public void requireThatBasicProcessingWorks() { + try (JDisc container = getContainerWithRot13()) { + Processing processing = container.processing(); + + Request req = new Request(); + req.properties().set("title", "Good day!"); + Response response = processing.process(ComponentSpecification.fromString("foo"), req); + + assertThat(response.data().get(0).toString(), equalTo("Tbbq qnl!")); + } + } + + @Test + public void requireThatBasicProcessingDoesNotTruncateBigResponse() { + final int SIZE = 50*1000; + StringBuilder foo = new StringBuilder(); + for (int j = 0 ; j < SIZE ; j++) { + foo.append('b'); + } + + try (JDisc container = getContainerWithRot13()) { + final int NUM_TIMES = 10000; + for (int i = 0; i < NUM_TIMES; i++) { + + + com.yahoo.application.container.handler.Response response = + container.handleRequest( + new com.yahoo.application.container.handler.Request("http://foo/processing/?chain=foo&title=" + foo.toString())); + + assertThat(response.getBody().length, is(SIZE+26)); + } + } + } + + @Test + public void processing_and_rendering_works() throws Exception { + try (JDisc container = getContainerWithRot13()) { + Processing processing = container.processing(); + + Request req = new Request(); + req.properties().set("title", "Good day!"); + + byte[] rendered = processing.processAndRender(ComponentSpecification.fromString("foo"), + ComponentSpecification.fromString("default"), req); + String renderedAsString = new String(rendered, "utf-8"); + + assertThat(renderedAsString, containsString("Tbbq qnl!")); + } + } + + @Test(expected = IllegalArgumentException.class) + public void requireThatUnknownChainThrows() { + try (JDisc container = getContainerWithRot13()) { + container.processing().process(ComponentSpecification.fromString("unknown"), new Request()); + } + + } + + @Test(expected = UnsupportedOperationException.class) + public void requireThatDocprocFails() { + try (JDisc container = getContainerWithRot13()) { + container.documentProcessing(); + } + + } + + @Test(expected = UnsupportedOperationException.class) + public void requireThatSearchFails() { + try (JDisc container = getContainerWithRot13()) { + container.search(); + } + + } + + private JDisc getContainerWithRot13() { + return JDisc.fromServicesXml( + getXML("foo", Rot13Processor.class.getCanonicalName()), + Networking.disable); + } + +} diff --git a/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java b/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java new file mode 100644 index 00000000000..1ab91547f04 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/JDiscContainerRequestTest.java @@ -0,0 +1,87 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container; + +import com.yahoo.application.Networking; +import com.yahoo.application.container.handler.Request; +import com.yahoo.application.container.handler.Response; +import com.yahoo.application.container.handlers.DelayedThrowingInWriteRequestHandler; +import com.yahoo.application.container.handlers.DelayedWriteException; +import com.yahoo.application.container.handlers.EchoRequestHandler; +import com.yahoo.application.container.handlers.HeaderEchoRequestHandler; +import com.yahoo.application.container.handlers.ThrowingInWriteRequestHandler; +import com.yahoo.application.container.handlers.WriteException; +import com.yahoo.text.Utf8; +import org.junit.Test; + +import java.nio.charset.CharacterCodingException; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class JDiscContainerRequestTest { + + private static String getXML(String className, String binding) { + return "<container version=\"1.0\">\n" + + " <handler id=\"test-handler\" class=\"" + + className + + "\">\n" + + " <binding>" + + binding + + "</binding>\n" + + " </handler>\n" + + "</container>"; + } + + @Test + public void requireThatRequestBodyWorks() throws InterruptedException, CharacterCodingException { + final String DATA = "we have no bananas today"; + Request req = new Request("http://banana/echo", DATA.getBytes(Utf8.getCharset())); + + try (JDisc container = JDisc.fromServicesXml(getXML(EchoRequestHandler.class.getCanonicalName(), "http://*/echo"), Networking.disable)) { + Response response = container.handleRequest(req); + assertThat(response.getBodyAsString(), equalTo(DATA)); + req.toString(); + response.toString(); + } + } + + @Test + public void requireThatCustomRequestHeadersWork() throws InterruptedException { + Request req = new Request("http://banana/echo"); + req.getHeaders().add("X-Foo", "Bar"); + + try (JDisc container = JDisc.fromServicesXml(getXML(HeaderEchoRequestHandler.class.getCanonicalName(), "http://*/echo"), Networking.disable)) { + Response response = container.handleRequest(req); + assertThat(response.getHeaders().contains("X-Foo", "Bar"), is(true)); + req.toString(); + response.toString(); + } + } + + @Test(expected = WriteException.class) + public void requireThatRequestHandlerThatThrowsInWriteWorks() throws InterruptedException { + final String DATA = "we have no bananas today"; + Request req = new Request("http://banana/throwwrite", DATA.getBytes(Utf8.getCharset())); + + try (JDisc container = JDisc.fromServicesXml(getXML(ThrowingInWriteRequestHandler.class.getCanonicalName(), "http://*/throwwrite"), Networking.disable)) { + Response response = container.handleRequest(req); + req.toString(); + } + } + + + @Test(expected = DelayedWriteException.class) + public void requireThatRequestHandlerThatThrowsDelayedInWriteWorks() throws InterruptedException { + final String DATA = "we have no bananas today"; + Request req = new Request("http://banana/delayedthrowwrite", DATA.getBytes(Utf8.getCharset())); + + try (JDisc container = JDisc.fromServicesXml(getXML(DelayedThrowingInWriteRequestHandler.class.getCanonicalName(), "http://*/delayedthrowwrite"), Networking.disable)) { + Response response = container.handleRequest(req); + req.toString(); + } + } +} diff --git a/application/src/test/java/com/yahoo/application/container/MockClient.java b/application/src/test/java/com/yahoo/application/container/MockClient.java new file mode 100644 index 00000000000..1f885cfb737 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/MockClient.java @@ -0,0 +1,43 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container;
+
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.service.AbstractClientProvider;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockClient extends AbstractClientProvider {
+ private final AtomicInteger counter = new AtomicInteger();
+
+ @Override
+ public ContentChannel handleRequest(Request request, ResponseHandler handler) {
+ counter.incrementAndGet();
+ final ContentChannel responseContentChannel = handler.handleResponse(new Response(200));
+ responseContentChannel.close(NOOP_COMPLETION_HANDLER);
+ return null;
+ }
+
+ public int getCounter() {
+ return counter.get();
+ }
+
+ private static final CompletionHandler NOOP_COMPLETION_HANDLER = new CompletionHandler() {
+ @Override
+ public void completed() {
+ // Ignored
+ }
+
+ @Override
+ public void failed(Throwable t) {
+ // Ignored
+ }
+ };
+}
diff --git a/application/src/test/java/com/yahoo/application/container/MockServer.java b/application/src/test/java/com/yahoo/application/container/MockServer.java new file mode 100644 index 00000000000..bc79671c13a --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/MockServer.java @@ -0,0 +1,31 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container;
+
+import com.yahoo.jdisc.service.AbstractServerProvider;
+import com.yahoo.jdisc.service.CurrentContainer;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockServer extends AbstractServerProvider {
+ private boolean started = false;
+
+ public MockServer(CurrentContainer container) {
+ super(container);
+ }
+
+ @Override
+ public void start() {
+ started = true;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ public boolean isStarted() {
+ return started;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java b/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java new file mode 100644 index 00000000000..3c11cbdad8a --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/docprocs/MockDispatchDocproc.java @@ -0,0 +1,65 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.docprocs;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.yahoo.docproc.DocumentProcessor;
+import com.yahoo.docproc.Processing;
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentOperation;
+import com.yahoo.document.DocumentPut;
+import com.yahoo.documentapi.messagebus.protocol.DocumentMessage;
+import com.yahoo.documentapi.messagebus.protocol.PutDocumentMessage;
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.handler.RequestDispatch;
+import com.yahoo.jdisc.service.CurrentContainer;
+import com.yahoo.messagebus.jdisc.MbusRequest;
+import com.yahoo.messagebus.routing.Route;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+/**
+ * @author Christian Andersen
+ */
+public class MockDispatchDocproc extends DocumentProcessor {
+ private final Route route;
+ private final URI uri;
+ private final CurrentContainer currentContainer;
+ private final List<Response> responses = new ArrayList<>();
+
+ public MockDispatchDocproc(CurrentContainer currentContainer) {
+ this.route = Route.parse("default");
+ this.uri = URI.create("mbus://remotehost/source");
+ this.currentContainer = currentContainer;
+ }
+
+ @Override
+ public Progress process(Processing processing) {
+ for (DocumentOperation op : processing.getDocumentOperations()) {
+ PutDocumentMessage message = new PutDocumentMessage((DocumentPut)op);
+ ListenableFuture<Response> future = createRequest(message).dispatch();
+ try {
+ responses.add(future.get());
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return Progress.DONE;
+ }
+
+ private RequestDispatch createRequest(final DocumentMessage message) {
+ return new RequestDispatch() {
+ @Override
+ protected Request newRequest() {
+ return new MbusRequest(currentContainer, uri, message.setRoute(route)).setServerRequest(false);
+ }
+ };
+ }
+
+ public List<Response> getResponses() {
+ return responses;
+ }
+}
diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java b/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java new file mode 100644 index 00000000000..c07655cfe5c --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/docprocs/MockDocproc.java @@ -0,0 +1,34 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.docprocs; + +import com.google.inject.Inject; +import com.yahoo.application.MockApplicationConfig; +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; + +/** + * @author Christian Andersen + */ +public class MockDocproc extends DocumentProcessor { + private Processing lastProcessing; + private MockApplicationConfig config; + + @Inject + public MockDocproc(MockApplicationConfig config) { + this.config = config; + } + + @Override + public Progress process(Processing processing) { + this.lastProcessing = processing; + return Progress.DONE; + } + + public Processing getLastProcessing() { + return lastProcessing; + } + + public MockApplicationConfig getConfig() { + return config; + } +} diff --git a/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java b/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java new file mode 100644 index 00000000000..ec23b3671b9 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/docprocs/Rot13DocumentProcessor.java @@ -0,0 +1,54 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.docprocs; + +import com.yahoo.docproc.DocumentProcessor; +import com.yahoo.docproc.Processing; +import com.yahoo.document.Document; +import com.yahoo.document.DocumentOperation; +import com.yahoo.document.DocumentPut; +import com.yahoo.document.datatypes.StringFieldValue; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class Rot13DocumentProcessor extends DocumentProcessor { + private static final String FIELD_NAME = "title"; + + private AtomicInteger counter = new AtomicInteger(1); + + public static String rot13(String s) { + StringBuilder output = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c >= 'a' && c <= 'm' || c >= 'A' && c <= 'M') { + c += 13; + } else if (c >= 'n' && c <= 'z' || c >= 'N' && c <= 'Z') { + c -= 13; + } + output.append(c); + } + return output.toString(); + } + + @Override + public Progress process(Processing processing) { + int oldVal = counter.getAndIncrement(); + if ((oldVal % 3) != 0) { + return Progress.LATER; + } + + for (DocumentOperation op : processing.getDocumentOperations()) { + if (op instanceof DocumentPut) { + Document document = ((DocumentPut)op).getDocument(); + + StringFieldValue oldTitle = (StringFieldValue) document.getFieldValue(FIELD_NAME); + if (oldTitle != null) { + document.setFieldValue(FIELD_NAME, rot13(oldTitle.getString())); + } + } + } + return Progress.DONE; + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java new file mode 100644 index 00000000000..7f4a32c7df6 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handler/HeadersTestCase.java @@ -0,0 +1,366 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.handler; + +import org.junit.Test; + +import java.util.*; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; +import static junit.framework.Assert.fail; + +/** + * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a> + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class HeadersTestCase { + + @Test + public void requireThatSizeWorksAsExpected() { + Headers headers = new Headers(); + assertEquals(0, headers.size()); + headers.add("foo", "bar"); + assertEquals(1, headers.size()); + headers.add("foo", "baz"); + assertEquals(1, headers.size()); + headers.add("bar", "baz"); + assertEquals(2, headers.size()); + headers.remove("foo"); + assertEquals(1, headers.size()); + headers.remove("bar"); + assertEquals(0, headers.size()); + } + + @Test + public void requireThatIsEmptyWorksAsExpected() { + Headers headers = new Headers(); + assertTrue(headers.isEmpty()); + headers.add("foo", "bar"); + assertFalse(headers.isEmpty()); + headers.remove("foo"); + assertTrue(headers.isEmpty()); + } + + @Test + public void requireThatContainsKeyWorksAsExpected() { + Headers headers = new Headers(); + assertFalse(headers.containsKey("foo")); + assertFalse(headers.containsKey("FOO")); + headers.add("foo", "bar"); + assertTrue(headers.containsKey("foo")); + assertTrue(headers.containsKey("FOO")); + } + + @Test + public void requireThatContainsValueWorksAsExpected() { + Headers headers = new Headers(); + assertFalse(headers.containsValue(Arrays.asList("bar"))); + headers.add("foo", "bar"); + assertTrue(headers.containsValue(Arrays.asList("bar"))); + } + + @Test + public void requireThatContainsWorksAsExpected() { + Headers headers = new Headers(); + assertFalse(headers.contains("foo", "bar")); + assertFalse(headers.contains("FOO", "bar")); + assertFalse(headers.contains("foo", "BAR")); + assertFalse(headers.contains("FOO", "BAR")); + headers.add("foo", "bar"); + assertTrue(headers.contains("foo", "bar")); + assertTrue(headers.contains("FOO", "bar")); + assertFalse(headers.contains("foo", "BAR")); + assertFalse(headers.contains("FOO", "BAR")); + } + + @Test + public void requireThatContainsIgnoreCaseWorksAsExpected() { + Headers headers = new Headers(); + assertFalse(headers.containsIgnoreCase("foo", "bar")); + assertFalse(headers.containsIgnoreCase("FOO", "bar")); + assertFalse(headers.containsIgnoreCase("foo", "BAR")); + assertFalse(headers.containsIgnoreCase("FOO", "BAR")); + headers.add("foo", "bar"); + assertTrue(headers.containsIgnoreCase("foo", "bar")); + assertTrue(headers.containsIgnoreCase("FOO", "bar")); + assertTrue(headers.containsIgnoreCase("foo", "BAR")); + assertTrue(headers.containsIgnoreCase("FOO", "BAR")); + } + + @Test + public void requireThatAddStringWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.get("foo")); + headers.add("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.add("foo", "baz"); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + } + + @Test + public void requireThatAddListWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.get("foo")); + headers.add("foo", Arrays.asList("bar")); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.add("foo", Arrays.asList("baz", "cox")); + assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo")); + } + + @Test + public void requireThatAddAllWorksAsExpected() { + Headers headers = new Headers(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + + Map<String, List<String>> map = new HashMap<>(); + map.put("foo", Arrays.asList("baz", "cox")); + map.put("bar", Arrays.asList("cox")); + headers.addAll(map); + + assertEquals(Arrays.asList("bar", "baz", "cox"), headers.get("foo")); + assertEquals(Arrays.asList("baz", "cox"), headers.get("bar")); + } + + @Test + public void requireThatPutStringWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.get("foo")); + headers.put("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.put("foo", "baz"); + assertEquals(Arrays.asList("baz"), headers.get("foo")); + } + + @Test + public void requireThatPutListWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.get("foo")); + headers.put("foo", Arrays.asList("bar")); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + headers.put("foo", Arrays.asList("baz", "cox")); + assertEquals(Arrays.asList("baz", "cox"), headers.get("foo")); + } + + @Test + public void requireThatPutAllWorksAsExpected() { + Headers headers = new Headers(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + + Map<String, List<String>> map = new HashMap<>(); + map.put("foo", Arrays.asList("baz", "cox")); + map.put("bar", Arrays.asList("cox")); + headers.putAll(map); + + assertEquals(Arrays.asList("baz", "cox"), headers.get("foo")); + assertEquals(Arrays.asList("cox"), headers.get("bar")); + } + + @Test + public void requireThatRemoveWorksAsExpected() { + Headers headers = new Headers(); + headers.put("foo", Arrays.asList("bar", "baz")); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + assertEquals(Arrays.asList("bar", "baz"), headers.remove("foo")); + assertNull(headers.get("foo")); + assertNull(headers.remove("foo")); + } + + @Test + public void requireThatRemoveStringWorksAsExpected() { + Headers headers = new Headers(); + headers.put("foo", Arrays.asList("bar", "baz")); + assertEquals(Arrays.asList("bar", "baz"), headers.get("foo")); + assertTrue(headers.remove("foo", "bar")); + assertFalse(headers.remove("foo", "cox")); + assertEquals(Arrays.asList("baz"), headers.get("foo")); + assertTrue(headers.remove("foo", "baz")); + assertFalse(headers.remove("foo", "cox")); + assertNull(headers.get("foo")); + } + + @Test + public void requireThatClearWorksAsExpected() { + Headers headers = new Headers(); + headers.add("foo", "bar"); + headers.add("bar", "baz"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + assertEquals(Arrays.asList("baz"), headers.get("bar")); + headers.clear(); + assertNull(headers.get("foo")); + assertNull(headers.get("bar")); + } + + @Test + public void requireThatGetWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.get("foo")); + headers.add("foo", "bar"); + assertEquals(Arrays.asList("bar"), headers.get("foo")); + } + + @Test + public void requireThatGetFirstWorksAsExpected() { + Headers headers = new Headers(); + assertNull(headers.getFirst("foo")); + headers.add("foo", Arrays.asList("bar", "baz")); + assertEquals("bar", headers.getFirst("foo")); + } + + @Test + public void requireThatIsTrueWorksAsExpected() { + Headers headers = new Headers(); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true")); + assertTrue(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true", "true")); + assertTrue(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("true", "false")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false", "true")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false", "false")); + assertFalse(headers.isTrue("foo")); + headers.put("foo", Arrays.asList("false")); + assertFalse(headers.isTrue("foo")); + } + + @Test + public void requireThatKeySetWorksAsExpected() { + Headers headers = new Headers(); + assertEquals(Collections.<Set<String>>emptySet(), headers.keySet()); + headers.add("foo", "bar"); + assertEquals(new HashSet<>(Arrays.asList("foo")), headers.keySet()); + headers.add("bar", "baz"); + assertEquals(new HashSet<>(Arrays.asList("foo", "bar")), headers.keySet()); + } + + @Test + public void requireThatValuesWorksAsExpected() { + Headers headers = new Headers(); + assertTrue(headers.values().isEmpty()); + headers.add("foo", "bar"); + Collection<List<String>> values = headers.values(); + assertEquals(1, values.size()); + assertTrue(values.contains(Arrays.asList("bar"))); + + headers.add("bar", "baz"); + values = headers.values(); + assertEquals(2, values.size()); + assertTrue(values.contains(Arrays.asList("bar"))); + assertTrue(values.contains(Arrays.asList("baz"))); + } + + @Test + public void requireThatEntrySetWorksAsExpected() { + Headers headers = new Headers(); + assertEquals(Collections.emptySet(), headers.entrySet()); + headers.put("foo", Arrays.asList("bar", "baz")); + + Set<Map.Entry<String, List<String>>> entries = headers.entrySet(); + assertEquals(1, entries.size()); + Map.Entry<String, List<String>> entry = entries.iterator().next(); + assertNotNull(entry); + assertEquals("foo", entry.getKey()); + assertEquals(Arrays.asList("bar", "baz"), entry.getValue()); + } + + @Test + public void requireThatEntriesWorksAsExpected() { + Headers headers = new Headers(); + assertEquals(Collections.emptyList(), headers.entries()); + headers.put("foo", Arrays.asList("bar", "baz")); + + List<Map.Entry<String, String>> entries = headers.entries(); + assertEquals(2, entries.size()); + + Map.Entry<String, String> entry = entries.get(0); + assertNotNull(entry); + assertEquals("foo", entry.getKey()); + assertEquals("bar", entry.getValue()); + + assertNotNull(entry = entries.get(1)); + assertEquals("foo", entry.getKey()); + assertEquals("baz", entry.getValue()); + } + + @Test + public void requireThatEntryIsUnmodifiable() { + Headers headers = new Headers(); + headers.put("foo", "bar"); + Map.Entry<String, String> entry = headers.entries().get(0); + try { + entry.setValue("baz"); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatEntriesAreUnmodifiable() { + Headers headers = new Headers(); + headers.put("foo", "bar"); + List<Map.Entry<String, String>> entries = headers.entries(); + try { + entries.add(new MyEntry()); + fail(); + } catch (UnsupportedOperationException e) { + + } + try { + entries.remove(new MyEntry()); + fail(); + } catch (UnsupportedOperationException e) { + + } + } + + @Test + public void requireThatEqualsWorksAsExpected() { + Headers lhs = new Headers(); + Headers rhs = new Headers(); + assertTrue(lhs.equals(rhs)); + lhs.add("foo", "bar"); + assertFalse(lhs.equals(rhs)); + rhs.add("foo", "bar"); + assertTrue(lhs.equals(rhs)); + } + + @Test + public void requireThatHashCodeWorksAsExpected() { + Headers lhs = new Headers(); + Headers rhs = new Headers(); + assertTrue(lhs.hashCode() == rhs.hashCode()); + lhs.add("foo", "bar"); + assertTrue(lhs.hashCode() != rhs.hashCode()); + rhs.add("foo", "bar"); + assertTrue(lhs.hashCode() == rhs.hashCode()); + } + + private static class MyEntry implements Map.Entry<String, String> { + + @Override + public String getKey() { + return "key"; + } + + @Override + public String getValue() { + return "value"; + } + + @Override + public String setValue(String value) { + return "value"; + } + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java b/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java new file mode 100644 index 00000000000..53b62753523 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handler/ResponseTestCase.java @@ -0,0 +1,40 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.handler; + +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class ResponseTestCase { + + @Test + public void requireThatCharsetParsingWorks() { + assertThat(Response.charset("text/foobar").toString().toLowerCase(), equalTo("utf-8")); + assertThat(Response.charset("adsf").toString().toLowerCase(), equalTo("utf-8")); + assertThat(Response.charset("").toString().toLowerCase(), equalTo("utf-8")); + assertThat(Response.charset(null).toString().toLowerCase(), equalTo("utf-8")); + + assertThat(Response.charset("something; charset=US-ASCII").toString().toLowerCase(), equalTo("us-ascii")); + assertThat(Response.charset("something; charset=iso-8859-1").toString().toLowerCase(), equalTo("iso-8859-1")); + + assertThat(Response.charset("something; charset=").toString().toLowerCase(), equalTo("utf-8")); + assertThat(Response.charset("something; charset=bananarama").toString().toLowerCase(), equalTo("utf-8")); + } + + @Test + public void testDefaultResponseBody() { + Response res1 = new Response(); + Response res2 = new Response(new byte[0]); + + assertThat(res1.getBody(), notNullValue()); + assertThat(res1.getBody().length, is(0)); + assertThat(res2.getBody(), notNullValue()); + assertThat(res2.getBody().length, is(0)); + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java new file mode 100644 index 00000000000..54c850efd37 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/DelayedThrowingInWriteRequestHandler.java @@ -0,0 +1,59 @@ +// Copyright 2016 Yahoo Inc. 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.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** +* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> +*/ +public class DelayedThrowingInWriteRequestHandler extends AbstractRequestHandler { + private ExecutorService responseExecutor = Executors.newSingleThreadExecutor(); + + @Override + public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { + responseExecutor.execute(new DelayedThrowingInWriteTask(handler)); + return new DelayedThrowingInWriteContentChannel(); + } + + + private static class DelayedThrowingInWriteTask implements Runnable { + private final ResponseHandler handler; + + public DelayedThrowingInWriteTask(ResponseHandler handler) { + this.handler = handler; + } + + @Override + public void run() { + ContentChannel responseChannel = handler.handleResponse( + new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK)); + responseChannel.close(null); + } + } + + + private static class DelayedThrowingInWriteContentChannel implements ContentChannel { + private List<CompletionHandler> writeCompletionHandlers = new ArrayList<>(); + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + writeCompletionHandlers.add(handler); + } + + @Override + public void close(CompletionHandler handler) { + for (CompletionHandler writeCompletionHandler : writeCompletionHandlers) { + writeCompletionHandler.failed(new DelayedWriteException()); + } + handler.completed(); + } + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java b/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java new file mode 100644 index 00000000000..e856ac310cb --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/DelayedWriteException.java @@ -0,0 +1,8 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.handlers; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class DelayedWriteException extends RuntimeException { +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java new file mode 100644 index 00000000000..30fe379d864 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/EchoRequestHandler.java @@ -0,0 +1,17 @@ +// Copyright 2016 Yahoo Inc. 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.Response; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; + +/** +* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> +*/ +public class EchoRequestHandler extends AbstractRequestHandler { + @Override + public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { + return handler.handleResponse(new com.yahoo.jdisc.Response(Response.Status.OK)); + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java new file mode 100644 index 00000000000..4eea69ea162 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/HeaderEchoRequestHandler.java @@ -0,0 +1,59 @@ +// Copyright 2016 Yahoo Inc. 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.Request; +import com.yahoo.jdisc.handler.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; + +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** +* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> +*/ +public class HeaderEchoRequestHandler extends AbstractRequestHandler { + private ExecutorService responseExecutor = Executors.newSingleThreadExecutor(); + + @Override + public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { + responseExecutor.execute(new HeaderEchoTask(request, handler)); + return new HeaderEchoContentChannel(); + } + + + private static class HeaderEchoTask implements Runnable { + private final Request request; + private final ResponseHandler handler; + + public HeaderEchoTask(Request request, ResponseHandler handler) { + this.request = request; + this.handler = handler; + } + + @Override + public void run() { + com.yahoo.jdisc.Response response = new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK); + response.headers().addAll(request.headers()); + ContentChannel responseChannel = handler.handleResponse(response); + responseChannel.close(null); + } + } + + + private static class HeaderEchoContentChannel implements ContentChannel { + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + //we will not accept header body data + handler.completed(); + } + + @Override + public void close(CompletionHandler handler) { + //we will not accept header body data + handler.completed(); + } + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java new file mode 100644 index 00000000000..b3287015b28 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/MockHttpHandler.java @@ -0,0 +1,33 @@ +// Copyright 2016 Yahoo Inc. 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.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.concurrent.Executor; + +/** + * + * @author Christian Andersen + */ +public class MockHttpHandler extends ThreadedHttpRequestHandler { + public MockHttpHandler(Executor executor) { + super(executor); + } + + @Override + public HttpResponse handle(HttpRequest request) { + return new HttpResponse(200) { + @Override + public void render(OutputStream outputStream) throws IOException { + PrintStream out = new PrintStream(outputStream); + out.print("OK"); + out.flush(); + } + }; + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java b/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java new file mode 100644 index 00000000000..9cde4eaf9f8 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/ThrowingInWriteRequestHandler.java @@ -0,0 +1,53 @@ +// Copyright 2016 Yahoo Inc. 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.AbstractRequestHandler; +import com.yahoo.jdisc.handler.CompletionHandler; +import com.yahoo.jdisc.handler.ContentChannel; +import com.yahoo.jdisc.handler.ResponseHandler; + +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** +* @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> +*/ +public class ThrowingInWriteRequestHandler extends AbstractRequestHandler { + private ExecutorService responseExecutor = Executors.newSingleThreadExecutor(); + + @Override + public ContentChannel handleRequest(com.yahoo.jdisc.Request request, ResponseHandler handler) { + responseExecutor.execute(new ThrowingInWriteTask(handler)); + return new ThrowingInWriteContentChannel(); + } + + + private static class ThrowingInWriteTask implements Runnable { + private final ResponseHandler handler; + + public ThrowingInWriteTask(ResponseHandler handler) { + this.handler = handler; + } + + @Override + public void run() { + ContentChannel responseChannel = handler.handleResponse( + new com.yahoo.jdisc.Response(com.yahoo.jdisc.Response.Status.OK)); + responseChannel.close(null); + } + } + + + private static class ThrowingInWriteContentChannel implements ContentChannel { + @Override + public void write(ByteBuffer buf, CompletionHandler handler) { + throw new WriteException(); + } + + @Override + public void close(CompletionHandler handler) { + handler.completed(); + } + } +} diff --git a/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java b/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java new file mode 100644 index 00000000000..63a62340efe --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/handlers/WriteException.java @@ -0,0 +1,8 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.handlers; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class WriteException extends RuntimeException { +} diff --git a/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java b/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java new file mode 100644 index 00000000000..f0ec8c25a88 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/processors/Rot13Processor.java @@ -0,0 +1,26 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.processors; + +import com.yahoo.processing.Processor; +import com.yahoo.processing.Request; +import com.yahoo.processing.Response; +import com.yahoo.processing.execution.Execution; +import com.yahoo.processing.test.ProcessorLibrary; + +import static com.yahoo.application.container.docprocs.Rot13DocumentProcessor.rot13; + +/** + * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> + */ +public class Rot13Processor extends Processor { + @Override + public Response process(Request request, Execution execution) { + Object fooObj = request.properties().get("title"); + + Response response = new Response(request); + if (fooObj != null) { + response.data().add(new ProcessorLibrary.StringData(request, rot13(fooObj.toString()))); + } + return response; + } +} diff --git a/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java b/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java new file mode 100644 index 00000000000..6e9899c7b9f --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/renderers/MockRenderer.java @@ -0,0 +1,32 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.application.container.renderers; + +import com.yahoo.search.Result; +import com.yahoo.search.rendering.Renderer; + +import java.io.IOException; +import java.io.Writer; + +/** + * + * @author Christian Andersen + */ +public class MockRenderer extends Renderer { + public MockRenderer() { + } + + @Override + public String getEncoding() { + return "utf-8"; + } + + @Override + public String getMimeType() { + return "applications/xml"; + } + + @Override + protected void render(Writer writer, Result result) throws IOException { + writer.write("<mock hits=\"" + result.hits().size() + "\" />"); + } +} diff --git a/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java b/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java new file mode 100644 index 00000000000..ad65e597519 --- /dev/null +++ b/application/src/test/java/com/yahoo/application/container/searchers/MockSearcher.java @@ -0,0 +1,22 @@ +// Copyright 2016 Yahoo Inc. 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.Query;
+import com.yahoo.search.Result;
+import com.yahoo.search.Searcher;
+import com.yahoo.search.result.Hit;
+import com.yahoo.search.result.HitGroup;
+import com.yahoo.search.searchchain.Execution;
+
+/**
+ *
+ * @author Christian Andersen
+ */
+public class MockSearcher extends Searcher {
+ @Override
+ public Result search(Query query, Execution execution) {
+ HitGroup hits = new HitGroup();
+ hits.add(new Hit("foo", query));
+ return new Result(query, hits);
+ }
+}
|