|
23 | 23 | import org.elasticsearch.action.FailedNodeException; |
24 | 24 | import org.elasticsearch.action.TaskOperationFailure; |
25 | 25 | import org.elasticsearch.action.admin.cluster.node.tasks.list.ListTasksResponse; |
26 | | -import org.elasticsearch.common.bytes.BytesReference; |
27 | | -import org.elasticsearch.common.io.stream.NamedWriteableRegistry; |
28 | | -import org.elasticsearch.common.xcontent.XContentFactory; |
29 | | -import org.elasticsearch.common.xcontent.XContentHelper; |
| 26 | +import org.elasticsearch.common.Strings; |
30 | 27 | import org.elasticsearch.common.xcontent.XContentParser; |
31 | | -import org.elasticsearch.common.xcontent.XContentType; |
32 | 28 | import org.elasticsearch.test.AbstractXContentTestCase; |
33 | 29 |
|
34 | 30 | import java.io.IOException; |
| 31 | +import java.net.ConnectException; |
35 | 32 | import java.util.ArrayList; |
36 | 33 | import java.util.Collections; |
37 | 34 | import java.util.List; |
38 | | -import java.util.Objects; |
| 35 | +import java.util.function.Predicate; |
| 36 | +import java.util.function.Supplier; |
39 | 37 |
|
40 | 38 | import static java.util.Collections.emptyList; |
41 | 39 | import static java.util.Collections.singletonList; |
42 | 40 | import static org.hamcrest.Matchers.equalTo; |
43 | | -import static org.hamcrest.Matchers.notNullValue; |
| 41 | +import static org.hamcrest.Matchers.instanceOf; |
44 | 42 |
|
45 | 43 | public class ListTasksResponseTests extends AbstractXContentTestCase<ListTasksResponse> { |
46 | 44 |
|
47 | 45 | public void testEmptyToString() { |
48 | | - assertEquals("{\"tasks\":[]}", new ListTasksResponse().toString()); |
| 46 | + assertEquals("{\n" + |
| 47 | + " \"tasks\" : [ ]\n" + |
| 48 | + "}", new ListTasksResponse().toString()); |
49 | 49 | } |
50 | 50 |
|
51 | 51 | public void testNonEmptyToString() { |
52 | 52 | TaskInfo info = new TaskInfo( |
53 | 53 | new TaskId("node1", 1), "dummy-type", "dummy-action", "dummy-description", null, 0, 1, true, new TaskId("node1", 0), |
54 | 54 | Collections.singletonMap("foo", "bar")); |
55 | 55 | ListTasksResponse tasksResponse = new ListTasksResponse(singletonList(info), emptyList(), emptyList()); |
56 | | - assertEquals("{\"tasks\":[{\"node\":\"node1\",\"id\":1,\"type\":\"dummy-type\",\"action\":\"dummy-action\"," |
57 | | - + "\"description\":\"dummy-description\",\"start_time_in_millis\":0,\"running_time_in_nanos\":1,\"cancellable\":true," |
58 | | - + "\"parent_task_id\":\"node1:0\",\"headers\":{\"foo\":\"bar\"}}]}", tasksResponse.toString()); |
| 56 | + assertEquals("{\n" + |
| 57 | + " \"tasks\" : [\n" + |
| 58 | + " {\n" + |
| 59 | + " \"node\" : \"node1\",\n" + |
| 60 | + " \"id\" : 1,\n" + |
| 61 | + " \"type\" : \"dummy-type\",\n" + |
| 62 | + " \"action\" : \"dummy-action\",\n" + |
| 63 | + " \"description\" : \"dummy-description\",\n" + |
| 64 | + " \"start_time\" : \"1970-01-01T00:00:00.000Z\",\n" + |
| 65 | + " \"start_time_in_millis\" : 0,\n" + |
| 66 | + " \"running_time\" : \"1nanos\",\n" + |
| 67 | + " \"running_time_in_nanos\" : 1,\n" + |
| 68 | + " \"cancellable\" : true,\n" + |
| 69 | + " \"parent_task_id\" : \"node1:0\",\n" + |
| 70 | + " \"headers\" : {\n" + |
| 71 | + " \"foo\" : \"bar\"\n" + |
| 72 | + " }\n" + |
| 73 | + " }\n" + |
| 74 | + " ]\n" + |
| 75 | + "}", tasksResponse.toString()); |
59 | 76 | } |
60 | 77 |
|
61 | 78 | @Override |
62 | 79 | protected ListTasksResponse createTestInstance() { |
| 80 | + //failures are tested separately, so we can test xcontent equivalence at least when we have no failures |
| 81 | + return new ListTasksResponse(randomTasks(), Collections.emptyList(), Collections.emptyList()); |
| 82 | + } |
| 83 | + |
| 84 | + private static List<TaskInfo> randomTasks() { |
63 | 85 | List<TaskInfo> tasks = new ArrayList<>(); |
64 | 86 | for (int i = 0; i < randomInt(10); i++) { |
65 | 87 | tasks.add(TaskInfoTests.randomTaskInfo()); |
66 | 88 | } |
67 | | - List<TaskOperationFailure> taskFailures = new ArrayList<>(); |
68 | | - for (int i = 0; i < randomInt(5); i++) { |
69 | | - taskFailures.add(new TaskOperationFailure( |
70 | | - randomAlphaOfLength(5), randomNonNegativeLong(), new IllegalStateException("message"))); |
71 | | - } |
72 | | - return new ListTasksResponse(tasks, taskFailures, Collections.singletonList(new FailedNodeException("", "message", null))); |
| 89 | + return tasks; |
73 | 90 | } |
74 | 91 |
|
75 | 92 | @Override |
76 | | - protected ListTasksResponse doParseInstance(XContentParser parser) throws IOException { |
| 93 | + protected ListTasksResponse doParseInstance(XContentParser parser) { |
77 | 94 | return ListTasksResponse.fromXContent(parser); |
78 | 95 | } |
79 | 96 |
|
80 | 97 | @Override |
81 | 98 | protected boolean supportsUnknownFields() { |
82 | | - return false; |
| 99 | + return true; |
| 100 | + } |
| 101 | + |
| 102 | + @Override |
| 103 | + protected Predicate<String> getRandomFieldsExcludeFilter() { |
| 104 | + //status and headers hold arbitrary content, we can't inject random fields in them |
| 105 | + return field -> field.endsWith("status") || field.endsWith("headers"); |
83 | 106 | } |
84 | 107 |
|
85 | 108 | @Override |
86 | 109 | protected void assertEqualInstances(ListTasksResponse expectedInstance, ListTasksResponse newInstance) { |
87 | 110 | assertNotSame(expectedInstance, newInstance); |
88 | 111 | assertThat(newInstance.getTasks(), equalTo(expectedInstance.getTasks())); |
89 | | - assertThat(newInstance.getNodeFailures().size(), equalTo(1)); |
90 | | - for (ElasticsearchException failure : newInstance.getNodeFailures()) { |
91 | | - assertThat(failure, notNullValue()); |
92 | | - assertThat(failure.getMessage(), equalTo("Elasticsearch exception [type=failed_node_exception, reason=message]")); |
| 112 | + assertThat(newInstance.getNodeFailures().size(), equalTo(expectedInstance.getNodeFailures().size())); |
| 113 | + for (int i = 0; i < newInstance.getNodeFailures().size(); i++) { |
| 114 | + ElasticsearchException newException = newInstance.getNodeFailures().get(i); |
| 115 | + ElasticsearchException expectedException = expectedInstance.getNodeFailures().get(i); |
| 116 | + assertThat(newException.getMetadata("es.node_id").get(0), equalTo(((FailedNodeException)expectedException).nodeId())); |
| 117 | + assertThat(newException.getMessage(), equalTo("Elasticsearch exception [type=failed_node_exception, reason=error message]")); |
| 118 | + assertThat(newException.getCause(), instanceOf(ElasticsearchException.class)); |
| 119 | + ElasticsearchException cause = (ElasticsearchException) newException.getCause(); |
| 120 | + assertThat(cause.getMessage(), equalTo("Elasticsearch exception [type=connect_exception, reason=null]")); |
| 121 | + } |
| 122 | + assertThat(newInstance.getTaskFailures().size(), equalTo(expectedInstance.getTaskFailures().size())); |
| 123 | + for (int i = 0; i < newInstance.getTaskFailures().size(); i++) { |
| 124 | + TaskOperationFailure newFailure = newInstance.getTaskFailures().get(i); |
| 125 | + TaskOperationFailure expectedFailure = expectedInstance.getTaskFailures().get(i); |
| 126 | + assertThat(newFailure.getNodeId(), equalTo(expectedFailure.getNodeId())); |
| 127 | + assertThat(newFailure.getTaskId(), equalTo(expectedFailure.getTaskId())); |
| 128 | + assertThat(newFailure.getStatus(), equalTo(expectedFailure.getStatus())); |
| 129 | + assertThat(newFailure.getCause(), instanceOf(ElasticsearchException.class)); |
| 130 | + ElasticsearchException cause = (ElasticsearchException) newFailure.getCause(); |
| 131 | + assertThat(cause.getMessage(), equalTo("Elasticsearch exception [type=illegal_state_exception, reason=null]")); |
93 | 132 | } |
94 | 133 | } |
95 | 134 |
|
96 | | - @Override |
97 | | - protected boolean assertToXContentEquivalence() { |
98 | | - return false; |
| 135 | + /** |
| 136 | + * Test parsing {@link ListTasksResponse} with inner failures as they don't support asserting on xcontent equivalence, given that |
| 137 | + * exceptions are not parsed back as the same original class. We run the usual {@link AbstractXContentTestCase#testFromXContent()} |
| 138 | + * without failures, and this other test with failures where we disable asserting on xcontent equivalence at the end. |
| 139 | + */ |
| 140 | + public void testFromXContentWithFailures() throws IOException { |
| 141 | + Supplier<ListTasksResponse> instanceSupplier = ListTasksResponseTests::createTestInstanceWithFailures; |
| 142 | + //with random fields insertion in the inner exceptions, some random stuff may be parsed back as metadata, |
| 143 | + //but that does not bother our assertions, as we only want to test that we don't break. |
| 144 | + boolean supportsUnknownFields = true; |
| 145 | + //exceptions are not of the same type whenever parsed back |
| 146 | + boolean assertToXContentEquivalence = false; |
| 147 | + AbstractXContentTestCase.testFromXContent(NUMBER_OF_TEST_RUNS, instanceSupplier, supportsUnknownFields, Strings.EMPTY_ARRAY, |
| 148 | + getRandomFieldsExcludeFilter(), this::createParser, this::doParseInstance, |
| 149 | + this::assertEqualInstances, assertToXContentEquivalence); |
| 150 | + } |
| 151 | + |
| 152 | + private static ListTasksResponse createTestInstanceWithFailures() { |
| 153 | + int numNodeFailures = randomIntBetween(0, 3); |
| 154 | + List<FailedNodeException> nodeFailures = new ArrayList<>(numNodeFailures); |
| 155 | + for (int i = 0; i < numNodeFailures; i++) { |
| 156 | + nodeFailures.add(new FailedNodeException(randomAlphaOfLength(5), "error message", new ConnectException())); |
| 157 | + } |
| 158 | + int numTaskFailures = randomIntBetween(0, 3); |
| 159 | + List<TaskOperationFailure> taskFailures = new ArrayList<>(numTaskFailures); |
| 160 | + for (int i = 0; i < numTaskFailures; i++) { |
| 161 | + taskFailures.add(new TaskOperationFailure(randomAlphaOfLength(5), randomLong(), new IllegalStateException())); |
| 162 | + } |
| 163 | + return new ListTasksResponse(randomTasks(), taskFailures, nodeFailures); |
99 | 164 | } |
100 | 165 | } |
0 commit comments