[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.
This commit is contained in:
Christoph Büscher 2017-06-09 17:33:44 +02:00 committed by GitHub
parent a03b6c2fa5
commit 823cbb437b
2 changed files with 56 additions and 17 deletions

View File

@ -45,8 +45,6 @@ import java.util.Map;
import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure; import static org.elasticsearch.action.search.ShardSearchFailure.readShardSearchFailure;
import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownField;
import static org.elasticsearch.common.xcontent.XContentParserUtils.throwUnknownToken;
/** /**
@ -245,7 +243,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
} else if (NUM_REDUCE_PHASES.match(currentFieldName)) { } else if (NUM_REDUCE_PHASES.match(currentFieldName)) {
numReducePhases = parser.intValue(); numReducePhases = parser.intValue();
} else { } else {
throwUnknownField(currentFieldName, parser.getTokenLocation()); parser.skipChildren();
} }
} else if (token == XContentParser.Token.START_OBJECT) { } else if (token == XContentParser.Token.START_OBJECT) {
if (SearchHits.Fields.HITS.equals(currentFieldName)) { if (SearchHits.Fields.HITS.equals(currentFieldName)) {
@ -268,7 +266,7 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
} else if (RestActions.TOTAL_FIELD.match(currentFieldName)) { } else if (RestActions.TOTAL_FIELD.match(currentFieldName)) {
totalShards = parser.intValue(); totalShards = parser.intValue();
} else { } else {
throwUnknownField(currentFieldName, parser.getTokenLocation()); parser.skipChildren();
} }
} else if (token == XContentParser.Token.START_ARRAY) { } else if (token == XContentParser.Token.START_ARRAY) {
if (RestActions.FAILURES_FIELD.match(currentFieldName)) { if (RestActions.FAILURES_FIELD.match(currentFieldName)) {
@ -276,14 +274,14 @@ public class SearchResponse extends ActionResponse implements StatusToXContentOb
failures.add(ShardSearchFailure.fromXContent(parser)); failures.add(ShardSearchFailure.fromXContent(parser));
} }
} else { } else {
throwUnknownField(currentFieldName, parser.getTokenLocation()); parser.skipChildren();
} }
} else { } else {
throwUnknownToken(token, parser.getTokenLocation()); parser.skipChildren();
} }
} }
} else { } else {
throwUnknownField(currentFieldName, parser.getTokenLocation()); parser.skipChildren();
} }
} }
} }

View File

@ -49,6 +49,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static org.elasticsearch.test.XContentTestUtils.insertRandomFields;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent;
public class SearchResponseTests extends ESTestCase { public class SearchResponseTests extends ESTestCase {
@ -78,31 +79,71 @@ public class SearchResponseTests extends ESTestCase {
} }
private SearchResponse createTestItem(ShardSearchFailure... shardSearchFailures) { private SearchResponse createTestItem(ShardSearchFailure... shardSearchFailures) {
SearchHits hits = SearchHitsTests.createTestItem(); return createTestItem(false, shardSearchFailures);
}
/**
* This SearchResponse doesn't include SearchHits, Aggregations, Suggestions, ShardSearchFailures, SearchProfileShardResults
* to make it possible to only test properties of the SearchResponse itself
*/
private SearchResponse createMinimalTestItem() {
return createTestItem(true);
}
/**
* if minimal is set, don't include search hits, aggregations, suggest etc... to make test simpler
*/
private SearchResponse createTestItem(boolean minimal, ShardSearchFailure... shardSearchFailures) {
boolean timedOut = randomBoolean(); boolean timedOut = randomBoolean();
Boolean terminatedEarly = randomBoolean() ? null : randomBoolean(); Boolean terminatedEarly = randomBoolean() ? null : randomBoolean();
int numReducePhases = randomIntBetween(1, 10); int numReducePhases = randomIntBetween(1, 10);
long tookInMillis = randomNonNegativeLong(); long tookInMillis = randomNonNegativeLong();
int successfulShards = randomInt(); int successfulShards = randomInt();
int totalShards = randomInt(); int totalShards = randomInt();
InternalSearchResponse internalSearchResponse;
if (minimal == false) {
SearchHits hits = SearchHitsTests.createTestItem();
InternalAggregations aggregations = aggregationsTests.createTestInstance(); InternalAggregations aggregations = aggregationsTests.createTestInstance();
Suggest suggest = SuggestTests.createTestItem(); Suggest suggest = SuggestTests.createTestItem();
SearchProfileShardResults profileShardResults = SearchProfileShardResultsTests.createTestItem(); SearchProfileShardResults profileShardResults = SearchProfileShardResultsTests.createTestItem();
internalSearchResponse = new InternalSearchResponse(hits, aggregations, suggest, profileShardResults,
InternalSearchResponse internalSearchResponse = new InternalSearchResponse(hits, aggregations, suggest, profileShardResults,
timedOut, terminatedEarly, numReducePhases); timedOut, terminatedEarly, numReducePhases);
} else {
internalSearchResponse = InternalSearchResponse.empty();
}
return new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, tookInMillis, shardSearchFailures); return new SearchResponse(internalSearchResponse, null, totalShards, successfulShards, tookInMillis, shardSearchFailures);
} }
/**
* the "_shard/total/failures" section makes it impossible to directly
* compare xContent, so we omit it here
*/
public void testFromXContent() throws IOException { public void testFromXContent() throws IOException {
// the "_shard/total/failures" section makes if impossible to directly compare xContent, so we omit it here doFromXContentTestWithRandomFields(createTestItem(), false);
SearchResponse response = createTestItem(); }
/**
* This test adds random fields and objects to the xContent rendered out to
* ensure we can parse it back to be forward compatible with additions to
* the xContent. We test this with a "minimal" SearchResponse, adding random
* fields to SearchHits, Aggregations etc... is tested in their own tests
*/
public void testFromXContentWithRandomFields() throws IOException {
doFromXContentTestWithRandomFields(createMinimalTestItem(), true);
}
private void doFromXContentTestWithRandomFields(SearchResponse response, boolean addRandomFields) throws IOException {
XContentType xcontentType = randomFrom(XContentType.values()); XContentType xcontentType = randomFrom(XContentType.values());
boolean humanReadable = randomBoolean(); boolean humanReadable = randomBoolean();
final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); final ToXContent.Params params = new ToXContent.MapParams(singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true"));
BytesReference originalBytes = toShuffledXContent(response, xcontentType, params, humanReadable); BytesReference originalBytes = toShuffledXContent(response, xcontentType, params, humanReadable);
try (XContentParser parser = createParser(xcontentType.xContent(), originalBytes)) { BytesReference mutated;
if (addRandomFields) {
mutated = insertRandomFields(xcontentType, originalBytes, null, random());
} else {
mutated = originalBytes;
}
try (XContentParser parser = createParser(xcontentType.xContent(), mutated)) {
SearchResponse parsed = SearchResponse.fromXContent(parser); SearchResponse parsed = SearchResponse.fromXContent(parser);
assertToXContentEquivalent(originalBytes, XContentHelper.toXContent(parsed, xcontentType, params, humanReadable), xcontentType); assertToXContentEquivalent(originalBytes, XContentHelper.toXContent(parsed, xcontentType, params, humanReadable), xcontentType);
assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());