summaryrefslogtreecommitdiffstats
path: root/vespaclient-container-plugin/src/test/java
diff options
context:
space:
mode:
Diffstat (limited to 'vespaclient-container-plugin/src/test/java')
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/CollectingMetric.java38
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/DummyMetric.java31
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerCompressionTest.java69
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java45
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java152
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedReaderFactoryTestCase.java40
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MetaStream.java39
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockNetwork.java69
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockReply.java35
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/VersionsTestCase.java118
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/util/ByteLimitedInputStreamTestCase.java106
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockFeedReaderFactory.java32
-rw-r--r--vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockReader.java69
13 files changed, 843 insertions, 0 deletions
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/CollectingMetric.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/CollectingMetric.java
new file mode 100644
index 00000000000..1b9a5eb6381
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/CollectingMetric.java
@@ -0,0 +1,38 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.jdisc.Metric;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * @author ollivir
+ */
+public final class CollectingMetric implements Metric {
+ private final Context DUMMY_CONTEXT = new Context() {};
+ private final Map<String, AtomicLong> values = new ConcurrentHashMap<>();
+
+ public CollectingMetric() {}
+
+ @Override
+ public void set(String key, Number val, Context ctx) {
+ values.computeIfAbsent(key, ignored -> new AtomicLong(0)).set(val.longValue());
+ }
+
+ @Override
+ public void add(String key, Number val, Context ctx) {
+ values.computeIfAbsent(key, ignored -> new AtomicLong(0)).addAndGet(val.longValue());
+ }
+
+ public long get(String key) {
+ return Optional.ofNullable(values.get(key)).map(AtomicLong::get).orElse(0L);
+ }
+
+ @Override
+ public Context createContext(Map<String, ?> properties) {
+ return DUMMY_CONTEXT;
+ }
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/DummyMetric.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/DummyMetric.java
new file mode 100644
index 00000000000..1cdac87f3df
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/DummyMetric.java
@@ -0,0 +1,31 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.jdisc.Metric;
+
+import java.util.Map;
+
+/**
+ * @author Einar M R Rosenvinge
+ * @since 5.1.20
+ */
+class DummyMetric implements Metric {
+
+ @Override
+ public void set(String key, Number val, Context ctx) {
+ }
+
+ @Override
+ public void add(String key, Number val, Context ctx) {
+ }
+
+ @Override
+ public Context createContext(Map<String, ?> properties) {
+ return DummyContext.INSTANCE;
+ }
+
+ private static class DummyContext implements Context {
+ private static final DummyContext INSTANCE = new DummyContext();
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerCompressionTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerCompressionTest.java
new file mode 100644
index 00000000000..6f1b5eebcc4
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerCompressionTest.java
@@ -0,0 +1,69 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.container.jdisc.HttpRequest;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPOutputStream;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedHandlerCompressionTest {
+
+ public static byte[] compress(final String dataToBrCompressed) throws IOException {
+ final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(dataToBrCompressed.length());
+ final GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
+ gzipOutputStream.write(dataToBrCompressed.getBytes());
+ gzipOutputStream.close();
+ byte[] compressedBytes = byteArrayOutputStream.toByteArray();
+ byteArrayOutputStream.close();
+ return compressedBytes;
+ }
+
+ @Test
+ public void testUnzipStreamIfNeeded() throws Exception {
+ final String testData = "foo bar";
+ InputStream inputStream = new ByteArrayInputStream(compress(testData));
+ HttpRequest httpRequest = mock(HttpRequest.class);
+ when(httpRequest.getHeader("content-encoding")).thenReturn("gzip");
+ InputStream decompressedStream = FeedHandler.unzipStreamIfNeeded(inputStream, httpRequest);
+ final StringBuilder processedInput = new StringBuilder();
+ while (true) {
+ int readValue = decompressedStream.read();
+ if (readValue < 0) {
+ break;
+ }
+ processedInput.append((char)readValue);
+ }
+ assertEquals(processedInput.toString(), testData);
+ }
+
+ /**
+ * Test by setting encoding, but not compressing data.
+ * @throws Exception
+ */
+ @Test
+ public void testUnzipFails() throws Exception {
+ final String testData = "foo bar";
+ InputStream inputStream = new ByteArrayInputStream(testData.getBytes());
+ HttpRequest httpRequest = mock(HttpRequest.class);
+ when(httpRequest.getHeader("Content-Encoding")).thenReturn("gzip");
+ InputStream decompressedStream = FeedHandler.unzipStreamIfNeeded(inputStream, httpRequest);
+ final StringBuilder processedInput = new StringBuilder();
+ while (true) {
+ int readValue = decompressedStream.read();
+ if (readValue < 0) {
+ break;
+ }
+ processedInput.append((char)readValue);
+ }
+ assertEquals(processedInput.toString(), testData);
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java
new file mode 100644
index 00000000000..f3ea8fb5a80
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerTest.java
@@ -0,0 +1,45 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+import com.yahoo.container.handler.threadpool.ContainerThreadPool;
+import com.yahoo.container.jdisc.RequestHandlerTestDriver;
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.document.config.DocumentmanagerConfig;
+import com.yahoo.jdisc.handler.OverloadException;
+import com.yahoo.metrics.simple.MetricReceiver;
+import org.junit.Test;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+import static com.yahoo.vespa.http.server.FeedHandlerV3Test.createRequest;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * @author bjorncs
+ */
+public class FeedHandlerTest {
+
+ @Test
+ public void response_has_status_code_429_when_throttling() {
+ FeedHandler handler = new FeedHandler(
+ new RejectingContainerThreadpool(),
+ new CollectingMetric(),
+ new DocumentTypeManager(new DocumentmanagerConfig.Builder().enablecompression(true).build()),
+ null /* session cache */,
+ MetricReceiver.nullImplementation);
+ var responseHandler = new RequestHandlerTestDriver.MockResponseHandler();
+ try {
+ handler.handleRequest(createRequest(100).getJDiscRequest(), responseHandler);
+ fail();
+ } catch (OverloadException e) {}
+ assertEquals(429, responseHandler.getStatus());
+ }
+
+ private static class RejectingContainerThreadpool implements ContainerThreadPool {
+ private final Executor executor = ignored -> { throw new RejectedExecutionException(); };
+
+ @Override public Executor executor() { return executor; }
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java
new file mode 100644
index 00000000000..a5a8f4cb5bd
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedHandlerV3Test.java
@@ -0,0 +1,152 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.google.common.base.Splitter;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.HttpResponse;
+import com.yahoo.container.jdisc.messagebus.SessionCache;
+import com.yahoo.document.DataType;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.document.config.DocumentmanagerConfig;
+import com.yahoo.documentapi.messagebus.protocol.PutDocumentMessage;
+import com.yahoo.documentapi.metrics.DocumentApiMetrics;
+import com.yahoo.jdisc.ReferencedResource;
+import com.yahoo.messagebus.Result;
+import com.yahoo.messagebus.SourceSessionParams;
+import com.yahoo.messagebus.shared.SharedSourceSession;
+import com.yahoo.metrics.simple.MetricReceiver;
+import com.yahoo.text.Utf8;
+import com.yahoo.vespa.http.client.config.FeedParams;
+import com.yahoo.vespa.http.client.core.ErrorCode;
+import com.yahoo.vespa.http.client.core.Headers;
+import com.yahoo.vespa.http.client.core.OperationStatus;
+import org.junit.Test;
+import org.mockito.stubbing.Answer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FeedHandlerV3Test {
+ final CollectingMetric metric = new CollectingMetric();
+ private final Executor simpleThreadpool = Executors.newCachedThreadPool();
+
+ @Test
+ public void feedOneDocument() throws Exception {
+ final FeedHandlerV3 feedHandlerV3 = setupFeederHandler(simpleThreadpool);
+ HttpResponse httpResponse = feedHandlerV3.handle(createRequest(1));
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ httpResponse.render(outStream);
+ assertEquals(httpResponse.getContentType(), "text/plain");
+ assertEquals(Utf8.toString(outStream.toByteArray()), "1230 OK message trace\n");
+ }
+
+ @Test
+ public void feedOneBrokenDocument() throws Exception {
+ final FeedHandlerV3 feedHandlerV3 = setupFeederHandler(simpleThreadpool);
+ HttpResponse httpResponse = feedHandlerV3.handle(createBrokenRequest());
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ httpResponse.render(outStream);
+ assertEquals(httpResponse.getContentType(), "text/plain");
+ assertTrue(Utf8.toString(outStream.toByteArray()).startsWith("1230 ERROR "));
+ assertEquals(1L, metric.get(MetricNames.PARSE_ERROR));
+ }
+
+ @Test
+ public void feedManyDocument() throws Exception {
+ final FeedHandlerV3 feedHandlerV3 = setupFeederHandler(simpleThreadpool);
+ HttpResponse httpResponse = feedHandlerV3.handle(createRequest(100));
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+ httpResponse.render(outStream);
+ assertEquals(httpResponse.getContentType(), "text/plain");
+ String result = Utf8.toString(outStream.toByteArray());
+ assertEquals(101, Splitter.on("\n").splitToList(result).size());
+ }
+
+ private static DocumentTypeManager createDoctypeManager() {
+ DocumentTypeManager docTypeManager = new DocumentTypeManager();
+ DocumentType documentType = new DocumentType("testdocument");
+ documentType.addField("title", DataType.STRING);
+ documentType.addField("body", DataType.STRING);
+ docTypeManager.registerDocumentType(documentType);
+ return docTypeManager;
+ }
+
+ static HttpRequest createRequest(int numberOfDocs) {
+ StringBuilder wireData = new StringBuilder();
+ for (int x = 0; x < numberOfDocs; x++) {
+ String docData = "[{\"put\": \"id:testdocument:testdocument::c\", \"fields\": { \"title\": \"fooKey\", \"body\": \"value\"}}]";
+ String operationId = "123" + x;
+ wireData.append(operationId + " " + Integer.toHexString(docData.length()) + "\n" + docData);
+ }
+ return createRequestWithPayload(wireData.toString());
+ }
+
+ private static HttpRequest createBrokenRequest() {
+ String docData = "[{\"put oops I broke it]";
+ String wireData = "1230 " + Integer.toHexString(docData.length()) + "\n" + docData;
+ return createRequestWithPayload(wireData);
+ }
+
+ static HttpRequest createRequestWithPayload(String payload) {
+ InputStream inputStream = new ByteArrayInputStream(payload.getBytes());
+ HttpRequest request = HttpRequest.createTestRequest("http://dummyhostname:19020/reserved-for-internal-use/feedapi",
+ com.yahoo.jdisc.http.HttpRequest.Method.POST, inputStream);
+ request.getJDiscRequest().headers().add(Headers.VERSION, "3");
+ request.getJDiscRequest().headers().add(Headers.DATA_FORMAT, FeedParams.DataFormat.JSON_UTF8.name());
+ request.getJDiscRequest().headers().add(Headers.TIMEOUT, "1000000000");
+ request.getJDiscRequest().headers().add(Headers.CLIENT_ID, "client123");
+ request.getJDiscRequest().headers().add(Headers.PRIORITY, "LOWEST");
+ request.getJDiscRequest().headers().add(Headers.TRACE_LEVEL, "4");
+ request.getJDiscRequest().headers().add(Headers.DRAIN, "true");
+ return request;
+ }
+
+ private FeedHandlerV3 setupFeederHandler(Executor threadPool) {
+ DocumentTypeManager docMan = new DocumentTypeManager(new DocumentmanagerConfig.Builder().enablecompression(true).build());
+ FeedHandlerV3 feedHandlerV3 = new FeedHandlerV3(
+ threadPool,
+ metric,
+ docMan,
+ null /* session cache */,
+ new DocumentApiMetrics(MetricReceiver.nullImplementation, "test")) {
+ @Override
+ protected ReferencedResource<SharedSourceSession> retainSource(
+ SessionCache sessionCache, SourceSessionParams sessionParams) {
+ SharedSourceSession sharedSourceSession = mock(SharedSourceSession.class);
+
+ try {
+ when(sharedSourceSession.sendMessageBlocking(any())).thenAnswer((Answer<?>) invocation -> {
+ Object[] args = invocation.getArguments();
+ PutDocumentMessage putDocumentMessage = (PutDocumentMessage) args[0];
+ ReplyContext replyContext = (ReplyContext) putDocumentMessage.getContext();
+ replyContext.feedReplies.add(new OperationStatus("message", replyContext.docId, ErrorCode.OK, false, "trace"));
+ Result result = mock(Result.class);
+ when(result.isAccepted()).thenReturn(true);
+ return result;
+ });
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ Result result = mock(Result.class);
+ when(result.isAccepted()).thenReturn(true);
+ ReferencedResource<SharedSourceSession> refSharedSessopn =
+ new ReferencedResource<>(sharedSourceSession, () -> {});
+ return refSharedSessopn;
+ }
+ };
+ feedHandlerV3.injectDocumentManangerForTests(createDoctypeManager());
+ return feedHandlerV3;
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedReaderFactoryTestCase.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedReaderFactoryTestCase.java
new file mode 100644
index 00000000000..6b0bd1c9518
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/FeedReaderFactoryTestCase.java
@@ -0,0 +1,40 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.text.Utf8;
+import com.yahoo.vespa.http.client.config.FeedParams;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class FeedReaderFactoryTestCase {
+ DocumentTypeManager manager = new DocumentTypeManager();
+
+ private InputStream createStream(String s) {
+ return new ByteArrayInputStream(Utf8.toBytes(s));
+ }
+
+ @Test
+ public void testXmlExceptionWithDebug() {
+ try {
+ new FeedReaderFactory(true).createReader(createStream("Some malformed xml"), manager, FeedParams.DataFormat.XML_UTF8);
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("Could not create VespaXMLFeedReader. First characters are: 'Some malformed xml'", e.getMessage());
+ }
+ }
+ @Test
+ public void testXmlException() {
+ try {
+ new FeedReaderFactory(false).createReader(createStream("Some malformed xml"), manager, FeedParams.DataFormat.XML_UTF8);
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("Could not create VespaXMLFeedReader.", e.getMessage());
+ }
+ }
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MetaStream.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MetaStream.java
new file mode 100644
index 00000000000..4dce8cb4e7d
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MetaStream.java
@@ -0,0 +1,39 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.text.Utf8;
+
+import java.io.ByteArrayInputStream;
+
+/**
+ * Stream with extra data outside the actual input stream.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public final class MetaStream extends ByteArrayInputStream {
+
+ private byte[] operations;
+ int i;
+
+ public MetaStream(byte[] buf) {
+ super(createPayload(buf));
+ this.operations = buf;
+ i = 0;
+ }
+
+ private static final byte[] createPayload(byte[] buf) {
+ StringBuilder bu = new StringBuilder();
+ for (int i = 0; i < buf.length; i++) {
+ bu.append("id:banana:banana::doc1 0\n");
+ }
+ return Utf8.toBytes(bu.toString());
+ }
+
+ public byte getNextOperation() {
+ if (i >= operations.length) {
+ return 0;
+ }
+ return operations[i++];
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockNetwork.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockNetwork.java
new file mode 100644
index 00000000000..7d3c0bb74ca
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockNetwork.java
@@ -0,0 +1,69 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import java.util.List;
+
+import com.yahoo.jrt.slobrok.api.IMirror;
+import com.yahoo.messagebus.Message;
+import com.yahoo.messagebus.network.Network;
+import com.yahoo.messagebus.network.NetworkOwner;
+import com.yahoo.messagebus.routing.RoutingNode;
+
+/**
+ * NOP-network.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+class MockNetwork implements Network {
+
+ @Override
+ public boolean waitUntilReady(double seconds) {
+ return true;
+ }
+
+ @Override
+ public void attach(NetworkOwner owner) {
+ }
+
+ @Override
+ public void registerSession(String session) {
+ }
+
+ @Override
+ public void unregisterSession(String session) {
+
+ }
+
+ @Override
+ public boolean allocServiceAddress(RoutingNode recipient) {
+ return true;
+ }
+
+ @Override
+ public void freeServiceAddress(RoutingNode recipient) {
+
+ }
+
+ @Override
+ public void send(Message msg, List<RoutingNode> recipients) {
+ }
+
+ @Override
+ public void sync() {
+ }
+
+ @Override
+ public void shutdown() {
+ }
+
+ @Override
+ public String getConnectionSpec() {
+ return null;
+ }
+
+ @Override
+ public IMirror getMirror() {
+ return null;
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockReply.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockReply.java
new file mode 100644
index 00000000000..1cb00160bbd
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/MockReply.java
@@ -0,0 +1,35 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.messagebus.Reply;
+import com.yahoo.text.Utf8String;
+
+/**
+ * Minimal reply simulator.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+class MockReply extends Reply {
+
+ Object context;
+
+ public MockReply(Object context) {
+ this.context = context;
+ }
+
+ @Override
+ public Utf8String getProtocol() {
+ return null;
+ }
+
+ @Override
+ public int getType() {
+ return 0;
+ }
+
+ @Override
+ public Object getContext() {
+ return context;
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/VersionsTestCase.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/VersionsTestCase.java
new file mode 100644
index 00000000000..6858c4bede3
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/VersionsTestCase.java
@@ -0,0 +1,118 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server;
+
+import com.yahoo.collections.Tuple2;
+import com.yahoo.container.jdisc.HttpResponse;
+import org.junit.Test;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @since 5.7.0
+ */
+public class VersionsTestCase {
+
+ private static final List<String> EMPTY = Collections.emptyList();
+ private static final List<String> ONE_TWO = Arrays.asList("1", "2");
+ private static final List<String> ONE_THREE = Arrays.asList("1", "3");
+ private static final List<String> TWO_THREE = Arrays.asList("3", "2");
+ private static final List<String> ONE_NULL_THREE = Arrays.asList("1", null, "3");
+ private static final List<String> ONE_COMMA_THREE = Collections.singletonList("1, 3");
+ private static final List<String> ONE_EMPTY_THREE = Arrays.asList("1", "", "3");
+ private static final List<String> TOO_LARGE_NUMBER = Collections.singletonList("1000000000");
+ private static final List<String> THREE_TOO_LARGE_NUMBER = Arrays.asList("3", "1000000000");
+ private static final List<String> THREE_COMMA_TOO_LARGE_NUMBER = Arrays.asList("3,1000000000");
+ private static final List<String> GARBAGE = Collections.singletonList("garbage");
+
+ @Test
+ public void testEmpty() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(EMPTY);
+ assertTrue(v.first instanceof ErrorHttpResponse);
+ assertEquals(Integer.valueOf(-1), v.second);
+ }
+
+ @Test
+ public void testOneTwo() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(ONE_TWO);
+ assertTrue(v.first instanceof ErrorHttpResponse);
+ assertEquals(Integer.valueOf(-1), v.second);
+ }
+
+ @Test
+ public void testOneThree() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(ONE_THREE);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testTwoThree() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(TWO_THREE);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testOneNullThree() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(ONE_NULL_THREE);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testOneCommaThree() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(ONE_COMMA_THREE);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testOneEmptyThree() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(ONE_EMPTY_THREE);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testTooLarge() throws Exception {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(TOO_LARGE_NUMBER);
+ assertTrue(v.first instanceof ErrorHttpResponse);
+ ByteArrayOutputStream errorMsg = new ByteArrayOutputStream();
+ ErrorHttpResponse errorResponse = (ErrorHttpResponse) v.first;
+ errorResponse.render(errorMsg);
+ assertEquals(errorMsg.toString(),
+ "Could not parse X-Yahoo-Feed-Protocol-Versionheader of request (values: [1000000000]). " +
+ "Server supports protocol versions [3]");
+ assertEquals(Integer.valueOf(-1), v.second);
+ }
+
+ @Test
+ public void testThreeTooLarge() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(THREE_TOO_LARGE_NUMBER);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testTwoCommaTooLarge() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(THREE_COMMA_TOO_LARGE_NUMBER);
+ assertNull(v.first);
+ assertEquals(Integer.valueOf(3), v.second);
+ }
+
+ @Test
+ public void testGarbage() {
+ Tuple2<HttpResponse, Integer> v = FeedHandler.doCheckProtocolVersion(GARBAGE);
+ assertTrue(v.first instanceof ErrorHttpResponse);
+ assertEquals(Integer.valueOf(-1), v.second);
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/util/ByteLimitedInputStreamTestCase.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/util/ByteLimitedInputStreamTestCase.java
new file mode 100644
index 00000000000..ed571c6baff
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespa/http/server/util/ByteLimitedInputStreamTestCase.java
@@ -0,0 +1,106 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.http.server.util;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a>
+ * @since 5.1.23
+ */
+public class ByteLimitedInputStreamTestCase {
+
+ private static ByteLimitedInputStream create(byte[] source, int limit) {
+ if (limit > source.length) {
+ throw new IllegalArgumentException("Limit is greater than length of source buffer.");
+ }
+ InputStream wrappedStream = new ByteArrayInputStream(source);
+ return new ByteLimitedInputStream(wrappedStream, limit);
+ }
+
+ @Test
+ public void requireThatBasicsWork() throws IOException {
+ ByteLimitedInputStream stream = create("abcdefghijklmnopqr".getBytes(StandardCharsets.US_ASCII), 9);
+
+ assertEquals(9, stream.available());
+ assertEquals(97, stream.read());
+ assertEquals(8, stream.available());
+ assertEquals(98, stream.read());
+ assertEquals(7, stream.available());
+ assertEquals(99, stream.read());
+ assertEquals(6, stream.available());
+ assertEquals(100, stream.read());
+ assertEquals(5, stream.available());
+ assertEquals(101, stream.read());
+ assertEquals(4, stream.available());
+ assertEquals(102, stream.read());
+ assertEquals(3, stream.available());
+ assertEquals(103, stream.read());
+ assertEquals(2, stream.available());
+ assertEquals(104, stream.read());
+ assertEquals(1, stream.available());
+ assertEquals(105, stream.read());
+ assertEquals(0, stream.available());
+ assertEquals(-1, stream.read());
+ assertEquals(0, stream.available());
+ assertEquals(-1, stream.read());
+ assertEquals(0, stream.available());
+ assertEquals(-1, stream.read());
+ assertEquals(0, stream.available());
+ assertEquals(-1, stream.read());
+ assertEquals(0, stream.available());
+ assertEquals(-1, stream.read());
+ assertEquals(0, stream.available());
+ }
+
+ @Test
+ public void requireThatChunkedReadWorks() throws IOException {
+ ByteLimitedInputStream stream = create("abcdefghijklmnopqr".getBytes(StandardCharsets.US_ASCII), 9);
+
+ assertEquals(9, stream.available());
+ byte[] toBuf = new byte[4];
+ assertEquals(4, stream.read(toBuf));
+ assertEquals(97, toBuf[0]);
+ assertEquals(98, toBuf[1]);
+ assertEquals(99, toBuf[2]);
+ assertEquals(100, toBuf[3]);
+ assertEquals(5, stream.available());
+
+ assertEquals(4, stream.read(toBuf));
+ assertEquals(101, toBuf[0]);
+ assertEquals(102, toBuf[1]);
+ assertEquals(103, toBuf[2]);
+ assertEquals(104, toBuf[3]);
+ assertEquals(1, stream.available());
+
+ assertEquals(1, stream.read(toBuf));
+ assertEquals(105, toBuf[0]);
+ assertEquals(0, stream.available());
+
+ assertEquals(-1, stream.read(toBuf));
+ assertEquals(0, stream.available());
+ }
+
+ @Test
+ public void requireMarkWorks() throws IOException {
+ InputStream stream = create("abcdefghijklmnopqr".getBytes(StandardCharsets.US_ASCII), 9);
+ assertEquals(97, stream.read());
+ assertTrue(stream.markSupported());
+ stream.mark(5);
+ assertEquals(98, stream.read());
+ assertEquals(99, stream.read());
+ stream.reset();
+ assertEquals(98, stream.read());
+ assertEquals(99, stream.read());
+ assertEquals(100, stream.read());
+ assertEquals(101, stream.read());
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockFeedReaderFactory.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockFeedReaderFactory.java
new file mode 100644
index 00000000000..513892af213
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockFeedReaderFactory.java
@@ -0,0 +1,32 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespaxmlparser;
+
+import com.yahoo.document.DocumentTypeManager;
+import com.yahoo.vespa.http.client.config.FeedParams;
+import com.yahoo.vespa.http.server.FeedReaderFactory;
+
+import java.io.InputStream;
+
+/**
+ * For creating MockReader of innput stream.
+ * @author dybis
+ */
+public class MockFeedReaderFactory extends FeedReaderFactory {
+
+ public MockFeedReaderFactory() {
+ super(true);
+ }
+
+ @Override
+ public FeedReader createReader(
+ InputStream inputStream,
+ DocumentTypeManager docTypeManager,
+ FeedParams.DataFormat dataFormat) {
+ try {
+ return new MockReader(inputStream);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockReader.java b/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockReader.java
new file mode 100644
index 00000000000..c751849b84e
--- /dev/null
+++ b/vespaclient-container-plugin/src/test/java/com/yahoo/vespaxmlparser/MockReader.java
@@ -0,0 +1,69 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespaxmlparser;
+
+import com.yahoo.document.Document;
+import com.yahoo.document.DocumentId;
+import com.yahoo.document.DocumentType;
+import com.yahoo.document.DocumentUpdate;
+import com.yahoo.vespa.http.server.MetaStream;
+import com.yahoo.vespa.http.server.util.ByteLimitedInputStream;
+
+import java.io.InputStream;
+import java.lang.reflect.Field;
+
+/**
+ * Mock for ExternalFeedTestCase which had to override package private methods.
+ *
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ */
+public class MockReader implements FeedReader {
+
+ MetaStream stream;
+ boolean finished = false;
+
+ public MockReader(InputStream stream) throws Exception {
+ this.stream = getMetaStream(stream);
+ }
+
+ private static MetaStream getMetaStream(InputStream stream) {
+ if (stream instanceof MetaStream) {
+ return (MetaStream) stream;
+ }
+ if (!(stream instanceof ByteLimitedInputStream)) {
+ throw new IllegalStateException("Given unknown stream type.");
+ }
+ //Ooooooo this is so ugly
+ try {
+ ByteLimitedInputStream byteLimitedInputStream = (ByteLimitedInputStream) stream;
+ Field f = byteLimitedInputStream.getClass().getDeclaredField("wrappedStream"); //NoSuchFieldException
+ f.setAccessible(true);
+ return (MetaStream) f.get(byteLimitedInputStream);
+ } catch (Exception e) {
+ throw new IllegalStateException("Implementation of ByteLimitedInputStream has changed.", e);
+ }
+ }
+
+ @Override
+ public FeedOperation read() throws Exception {
+ if (finished) {
+ return FeedOperation.INVALID;
+ }
+
+ byte whatToDo = stream.getNextOperation();
+ DocumentId id = new DocumentId("id:banana:banana::doc1");
+ DocumentType docType = new DocumentType("banana");
+ switch (whatToDo) {
+ case 0:
+ return FeedOperation.INVALID;
+ case 1:
+ return new DocumentFeedOperation(new Document(docType, id));
+ case 2:
+ return new RemoveFeedOperation(id);
+ case 3:
+ return new DocumentUpdateFeedOperation(new DocumentUpdate(docType, id));
+ default:
+ throw new RuntimeException("boom");
+ }
+ }
+
+}