Skip to content

Commit 823cbb4

Browse files
authored
[Test] Extending parsing checks for SearchResponse (#25148)
This change extends the tests and parsing of SearchResponse to make sure we can skip additional fields the parser doesn't know for forward compatibility reasons.
1 parent a03b6c2 commit 823cbb4

File tree

2 files changed

+56
-17
lines changed

2 files changed

+56
-17
lines changed

core/src/main/java/org/elasticsearch/action/search/SearchResponse.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@
4545

4646
import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure;
4747
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
48-
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownField;
49-
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownToken;
5048

5149

5250
/**
@@ -245,7 +243,7 @@ public static SearchResponse fromXContent(XContentParser parser) throws IOExcept
245243
} else if (NUM_REDUCE_PHASES.match(currentFieldName)) {
246244
numReducePhases = parser.intValue();
247245
} else {
248-
throwUnknownField(currentFieldName, parser.getTokenLocation());
246+
parser.skipChildren();
249247
}
250248
} else if (token == XContentParser.Token.START_OBJECT) {
251249
if (SearchHits.Fields.HITS.equals(currentFieldName)) {
@@ -268,22 +266,22 @@ public static SearchResponse fromXContent(XContentParser parser) throws IOExcept
268266
} else if (RestActions.TOTAL_FIELD.match(currentFieldName)) {
269267
totalShards = parser.intValue();
270268
} else {
271-
throwUnknownField(currentFieldName, parser.getTokenLocation());
269+
parser.skipChildren();
272270
}
273271
} else if (token == XContentParser.Token.START_ARRAY) {
274272
if (RestActions.FAILURES_FIELD.match(currentFieldName)) {
275273
while((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
276274
failures.add(ShardSearchFailure.fromXContent(parser));
277275
}
278276
} else {
279-
throwUnknownField(currentFieldName, parser.getTokenLocation());
277+
parser.skipChildren();
280278
}
281279
} else {
282-
throwUnknownToken(token, parser.getTokenLocation());
280+
parser.skipChildren();
283281
}
284282
}
285283
} else {
286-
throwUnknownField(currentFieldName, parser.getTokenLocation());
284+
parser.skipChildren();
287285
}
288286
}
289287
}

core/src/test/java/org/elasticsearch/action/search/SearchResponseTests.java

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import java.util.List;
5050

5151
import static java.util.Collections.singletonMap;
52+
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
5253
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
5354

5455
public class SearchResponseTests extends ESTestCase {
@@ -78,31 +79,71 @@ protected NamedXContentRegistry xContentRegistry() {
7879
}
7980

8081
private SearchResponse createTestItem(ShardSearchFailure... shardSearchFailures) {
81-
SearchHits hits = SearchHitsTests.createTestItem();
82+
return createTestItem(false, shardSearchFailures);
83+
}
84+
85+
/**
86+
* This SearchResponse doesn't include SearchHits, Aggregations, Suggestions, ShardSearchFailures, SearchProfileShardResults
87+
* to make it possible to only test properties of the SearchResponse itself
88+
*/
89+
private SearchResponse createMinimalTestItem() {
90+
return createTestItem(true);
91+
}
92+
93+
/**
94+
* if minimal is set, don't include search hits, aggregations, suggest etc... to make test simpler
95+
*/
96+
private SearchResponse createTestItem(boolean minimal, ShardSearchFailure... shardSearchFailures) {
8297
boolean timedOut = randomBoolean();
8398
Boolean terminatedEarly = randomBoolean() ? null : randomBoolean();
8499
int numReducePhases = randomIntBetween(1, 10);
85100
long tookInMillis = randomNonNegativeLong();
86101
int successfulShards = randomInt();
87102
int totalShards = randomInt();
88-
89-
InternalAggregations aggregations = aggregationsTests.createTestInstance();
90-
Suggest suggest = SuggestTests.createTestItem();
91-
SearchProfileShardResults profileShardResults = SearchProfileShardResultsTests.createTestItem();
92-
93-
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(hits, aggregations, suggest, profileShardResults,
103+
InternalSearchResponse internalSearchResponse;
104+
if (minimal == false) {
105+
SearchHits hits = SearchHitsTests.createTestItem();
106+
InternalAggregations aggregations = aggregationsTests.createTestInstance();
107+
Suggest suggest = SuggestTests.createTestItem();
108+
SearchProfileShardResults profileShardResults = SearchProfileShardResultsTests.createTestItem();
109+
internalSearchResponse = new InternalSearchResponse(hits, aggregations, suggest, profileShardResults,
94110
timedOut, terminatedEarly, numReducePhases);
111+
} else {
112+
internalSearchResponse = InternalSearchResponse.empty();
113+
}
95114
return new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, tookInMillis, shardSearchFailures);
96115
}
97116

117+
/**
118+
* the "_shard/total/failures" section makes it impossible to directly
119+
* compare xContent, so we omit it here
120+
*/
98121
public void testFromXContent() throws IOException {
99-
// the "_shard/total/failures" section makes if impossible to directly compare xContent, so we omit it here
100-
SearchResponse response = createTestItem();
122+
doFromXContentTestWithRandomFields(createTestItem(), false);
123+
}
124+
125+
/**
126+
* This test adds random fields and objects to the xContent rendered out to
127+
* ensure we can parse it back to be forward compatible with additions to
128+
* the xContent. We test this with a "minimal" SearchResponse, adding random
129+
* fields to SearchHits, Aggregations etc... is tested in their own tests
130+
*/
131+
public void testFromXContentWithRandomFields() throws IOException {
132+
doFromXContentTestWithRandomFields(createMinimalTestItem(), true);
133+
}
134+
135+
private void doFromXContentTestWithRandomFields(SearchResponse response, boolean addRandomFields) throws IOException {
101136
XContentType xcontentType = randomFrom(XContentType.values());
102137
boolean humanReadable = randomBoolean();
103138
final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true"));
104139
BytesReference originalBytes = toShuffledXContent(response, xcontentType, params, humanReadable);
105-
try (XContentParser parser = createParser(xcontentType.xContent(), originalBytes)) {
140+
BytesReference mutated;
141+
if (addRandomFields) {
142+
mutated = insertRandomFields(xcontentType, originalBytes, null, random());
143+
} else {
144+
mutated = originalBytes;
145+
}
146+
try (XContentParser parser = createParser(xcontentType.xContent(), mutated)) {
106147
SearchResponse parsed = SearchResponse.fromXContent(parser);
107148
assertToXContentEquivalent(originalBytes, XContentHelper.toXContent(parsed, xcontentType, params, humanReadable), xcontentType);
108149
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());

0 commit comments

Comments
 (0)