|
|
@@ -9,16 +9,17 @@
|
|
|
|
|
|
package org.elasticsearch.common;
|
|
|
|
|
|
+import org.elasticsearch.common.bytes.BytesArray;
|
|
|
import org.elasticsearch.common.bytes.BytesReference;
|
|
|
+import org.elasticsearch.common.bytes.CompositeBytesReference;
|
|
|
import org.elasticsearch.test.ESTestCase;
|
|
|
-import org.elasticsearch.xcontent.XContentFactory;
|
|
|
-import org.elasticsearch.xcontent.XContentParseException;
|
|
|
|
|
|
-import java.io.ByteArrayInputStream;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-import java.util.Arrays;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
|
|
|
import static org.elasticsearch.common.ReferenceDocs.getVersionComponent;
|
|
|
+import static org.hamcrest.Matchers.equalTo;
|
|
|
+import static org.hamcrest.Matchers.startsWith;
|
|
|
|
|
|
public class ReferenceDocsTests extends ESTestCase {
|
|
|
|
|
|
@@ -39,119 +40,140 @@ public class ReferenceDocsTests extends ESTestCase {
|
|
|
assertEquals("master", getVersionComponent("ABCDEF", true));
|
|
|
}
|
|
|
|
|
|
- public void testReadsValidLinkDefinitions() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- builder.field(link.name(), "TEST");
|
|
|
- }
|
|
|
- builder.endObject();
|
|
|
+ private static final String TEST_LINK_PLACEHOLDER = "TEST_LINK";
|
|
|
|
|
|
- var map = ReferenceDocs.readLinksBySymbol(BytesReference.bytes(builder).streamInput());
|
|
|
- assertEquals(ReferenceDocs.values().length, map.size());
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- assertEquals("TEST", map.get(link.name()));
|
|
|
- }
|
|
|
- }
|
|
|
+ private interface LinkSupplier {
|
|
|
+ String mutateLinkLine(int index, String lineWithPlaceholder);
|
|
|
}
|
|
|
|
|
|
- public void testRejectsInvalidJSON() throws Exception {
|
|
|
- try (var stream = new ByteArrayInputStream("{\"invalid\":".getBytes(StandardCharsets.UTF_8))) {
|
|
|
- expectThrows(XContentParseException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public void testRejectsBadStructure() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- builder.field(link.name(), "TEST");
|
|
|
- }
|
|
|
- builder.startObject("UNEXPECTED").endObject().endObject();
|
|
|
-
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalArgumentException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
+ private static InputStream getResourceStream(LinkSupplier linkSupplier) {
|
|
|
+ final var stringBuilder = new StringBuilder();
|
|
|
+ for (int i = 0; i < ReferenceDocs.values().length; i++) {
|
|
|
+ final var symbol = ReferenceDocs.values()[i];
|
|
|
+ final var lineWithPlaceholder = symbol.name() + " ".repeat(ReferenceDocs.SYMBOL_COLUMN_WIDTH - symbol.name().length())
|
|
|
+ + TEST_LINK_PLACEHOLDER;
|
|
|
+ final var updatedLine = linkSupplier.mutateLinkLine(i, lineWithPlaceholder);
|
|
|
+ if (updatedLine == null) {
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ stringBuilder.append(updatedLine).append('\n');
|
|
|
}
|
|
|
}
|
|
|
+ return new BytesArray(stringBuilder.toString()).streamInput();
|
|
|
}
|
|
|
|
|
|
- public void testRejectsExtraSymbol() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- builder.field(link.name(), "TEST");
|
|
|
- }
|
|
|
- builder.field("EXTRA", "TEST").endObject();
|
|
|
-
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
+ public void testSuccess() throws IOException {
|
|
|
+ final var linksMap = ReferenceDocs.readLinksBySymbol(getResourceStream((i, l) -> l));
|
|
|
+ assertEquals(ReferenceDocs.values().length, linksMap.size());
|
|
|
+ for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
+ assertEquals(TEST_LINK_PLACEHOLDER, linksMap.get(link.name()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void testRejectsMissingSymbol() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- var skipped = randomFrom(ReferenceDocs.values());
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- if (link != skipped) {
|
|
|
- builder.field(link.name(), "TEST");
|
|
|
- }
|
|
|
- }
|
|
|
- builder.endObject();
|
|
|
-
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
- }
|
|
|
+ public void testTruncated() {
|
|
|
+ final var targetLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(getResourceStream((i, l) -> i == targetLine ? null : l))
|
|
|
+ ).getMessage(),
|
|
|
+ equalTo("links resource truncated at line " + (targetLine + 1))
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- public void testRejectsIncorrectOrder() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- var shuffled = Arrays.copyOf(ReferenceDocs.values(), ReferenceDocs.values().length);
|
|
|
- var i = between(0, ReferenceDocs.values().length - 1);
|
|
|
- var j = randomValueOtherThan(i, () -> between(0, ReferenceDocs.values().length - 1));
|
|
|
- var tmp = shuffled[i];
|
|
|
- shuffled[i] = shuffled[j];
|
|
|
- shuffled[j] = tmp;
|
|
|
-
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : shuffled) {
|
|
|
- builder.field(link.name(), "TEST");
|
|
|
- }
|
|
|
- builder.endObject();
|
|
|
+ public void testMissingLink() {
|
|
|
+ final var targetLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(
|
|
|
+ getResourceStream((i, l) -> i == targetLine ? l.replace(TEST_LINK_PLACEHOLDER, "") : l)
|
|
|
+ )
|
|
|
+ ).getMessage(),
|
|
|
+ equalTo("no link found for [" + ReferenceDocs.values()[targetLine].name() + "] at line " + (targetLine + 1))
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
- }
|
|
|
+ public void testUnexpectedSymbol() {
|
|
|
+ final var targetSymbol = randomFrom(ReferenceDocs.values()).name();
|
|
|
+ final var replacement = "x".repeat(targetSymbol.length());
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(getResourceStream((i, l) -> l.replace(targetSymbol, replacement)))
|
|
|
+ ).getMessage(),
|
|
|
+ startsWith("unexpected symbol at line ")
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- public void testRejectsAutoGeneratedFragment() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- builder.field(link.name(), "test.html#_auto_generated_fragment");
|
|
|
- }
|
|
|
- builder.endObject();
|
|
|
+ public void testWhitespace() {
|
|
|
+ final var leadingWhitespaceLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ final var trailingWhitespaceLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(
|
|
|
+ getResourceStream(
|
|
|
+ (i, l) -> l.replace(
|
|
|
+ TEST_LINK_PLACEHOLDER,
|
|
|
+ (i == leadingWhitespaceLine ? " " : "") + TEST_LINK_PLACEHOLDER + (i == trailingWhitespaceLine ? " " : "")
|
|
|
+ )
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ).getMessage(),
|
|
|
+ startsWith("unexpected content at line " + (Math.min(leadingWhitespaceLine, trailingWhitespaceLine) + 1) + ": expected [")
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
+ public void testTrailingContent() throws IOException {
|
|
|
+ final byte[] validContent;
|
|
|
+ try (var stream = getResourceStream((i, l) -> l)) {
|
|
|
+ validContent = stream.readAllBytes();
|
|
|
}
|
|
|
+ final BytesReference contentWithTrailingData = CompositeBytesReference.of(new BytesArray(validContent), new BytesArray("x"));
|
|
|
+
|
|
|
+ assertThat(
|
|
|
+ expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(contentWithTrailingData.streamInput()))
|
|
|
+ .getMessage(),
|
|
|
+ equalTo("unexpected trailing content at line " + (ReferenceDocs.values().length + 1))
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
- public void testRejectsAutoGeneratedPageName() throws Exception {
|
|
|
- try (var builder = XContentFactory.jsonBuilder()) {
|
|
|
- builder.startObject();
|
|
|
- for (ReferenceDocs link : ReferenceDocs.values()) {
|
|
|
- builder.field(link.name(), "_auto_generated_page.html");
|
|
|
- }
|
|
|
- builder.endObject();
|
|
|
+ public void testRejectsAutoGeneratedFragment() {
|
|
|
+ final var targetLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(
|
|
|
+ getResourceStream(
|
|
|
+ (i, l) -> i == targetLine ? l.replace(TEST_LINK_PLACEHOLDER, "test.html#_auto_generated_fragment") : l
|
|
|
+ )
|
|
|
+ )
|
|
|
+ ).getMessage(),
|
|
|
+ equalTo(
|
|
|
+ "found auto-generated fragment ID in link [test.html#_auto_generated_fragment] for ["
|
|
|
+ + ReferenceDocs.values()[targetLine].name()
|
|
|
+ + "] at line "
|
|
|
+ + (targetLine + 1)
|
|
|
+ )
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
- try (var stream = BytesReference.bytes(builder).streamInput()) {
|
|
|
- expectThrows(IllegalStateException.class, () -> ReferenceDocs.readLinksBySymbol(stream));
|
|
|
- }
|
|
|
- }
|
|
|
+ public void testRejectsAutoGeneratedPageName() {
|
|
|
+ final var targetLine = between(0, ReferenceDocs.values().length - 1);
|
|
|
+ assertThat(
|
|
|
+ expectThrows(
|
|
|
+ IllegalStateException.class,
|
|
|
+ () -> ReferenceDocs.readLinksBySymbol(
|
|
|
+ getResourceStream((i, l) -> i == targetLine ? l.replace(TEST_LINK_PLACEHOLDER, "_auto_generated_page.html") : l)
|
|
|
+ )
|
|
|
+ ).getMessage(),
|
|
|
+ equalTo(
|
|
|
+ "found auto-generated fragment ID in link [_auto_generated_page.html] for ["
|
|
|
+ + ReferenceDocs.values()[targetLine].name()
|
|
|
+ + "] at line "
|
|
|
+ + (targetLine + 1)
|
|
|
+ )
|
|
|
+ );
|
|
|
}
|
|
|
}
|