Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add allowed_warnings to yaml tests (backport of #53139) #53173

Merged
merged 3 commits into from
Mar 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,23 @@ header. The warnings must match exactly. Using it looks like this:
id: 1
....

If the arguments to `do` include `allowed_warnings` then matching `Warning`
headers do not fail the request. Unlike the `warnings` argument, these aren't
expected so much as "allowed". This usually comes up in backwards compatibility
testing. Using it looks like this:

....
- do:
allowed_warnings:
- some warning
- this argument is also always a list, never a single string
- no matter how many warnings you expect
get:
index: test
type: test
id: 1
....

If the arguments to `do` include `node_selector` then the request is only
sent to nodes that match the `node_selector`. It looks like this:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ public final class Features {
"yaml",
"contains",
"transform_and_set",
"arbitrary_key"
));
"arbitrary_key",
"allowed_warnings"));

private static final String SPI_ON_CLASSPATH_SINCE_JDK_9 = "spi_on_classpath_jdk9";

private Features() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ private static Stream<String> validateExecutableSections(List<ExecutableSection>
"without a corresponding [\"skip\": \"features\": \"warnings\"] so runners that do not support the [warnings] " +
"section can skip the test at line [" + section.getLocation().lineNumber + "]");

errors = Stream.concat(errors, sections.stream().filter(section -> section instanceof DoSection)
.map(section -> (DoSection) section)
.filter(section -> false == section.getAllowedWarningHeaders().isEmpty())
.filter(section -> false == hasSkipFeature("allowed_warnings", testSection, setupSection, teardownSection))
.map(section -> "attempted to add a [do] with a [allowed_warnings] section " +
"without a corresponding [\"skip\": \"features\": \"allowed_warnings\"] so runners that do not " +
"support the [allowed_warnings] section can skip the test at line [" + section.getLocation().lineNumber + "]"));

errors = Stream.concat(errors, sections.stream().filter(section -> section instanceof DoSection)
.map(section -> (DoSection) section)
.filter(section -> NodeSelector.ANY != section.getApiCallSection().getNodeSelector())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,11 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

import static java.util.Collections.emptyList;
import static java.util.Collections.unmodifiableList;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toSet;
import static org.elasticsearch.common.collect.Tuple.tuple;
import static org.elasticsearch.common.logging.DeprecationLogger.WARNING_HEADER_PATTERN;
import static org.elasticsearch.test.hamcrest.RegexMatcher.matches;
Expand All @@ -77,6 +78,9 @@
* - Stuff is deprecated, yo
* - Don't use deprecated stuff
* - Please, stop. It hurts.
* allowed_warnings:
* - Maybe this warning shows up
* - But it isn't actually required for the test to pass.
* update:
* index: test_1
* type: test
Expand All @@ -94,6 +98,7 @@ public static DoSection parse(XContentParser parser) throws IOException {
NodeSelector nodeSelector = NodeSelector.ANY;
Map<String, String> headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
List<String> expectedWarnings = new ArrayList<>();
List<String> allowedWarnings = new ArrayList<>();

if (parser.nextToken() != XContentParser.Token.START_OBJECT) {
throw new IllegalArgumentException("expected [" + XContentParser.Token.START_OBJECT + "], " +
Expand All @@ -117,6 +122,14 @@ public static DoSection parse(XContentParser parser) throws IOException {
if (token != XContentParser.Token.END_ARRAY) {
throw new ParsingException(parser.getTokenLocation(), "[warnings] must be a string array but saw [" + token + "]");
}
} else if ("allowed_warnings".equals(currentFieldName)) {
while ((token = parser.nextToken()) == XContentParser.Token.VALUE_STRING) {
allowedWarnings.add(parser.text());
}
if (token != XContentParser.Token.END_ARRAY) {
throw new ParsingException(parser.getTokenLocation(),
"[allowed_warnings] must be a string array but saw [" + token + "]");
}
} else {
throw new ParsingException(parser.getTokenLocation(), "unknown array [" + currentFieldName + "]");
}
Expand Down Expand Up @@ -172,10 +185,16 @@ public static DoSection parse(XContentParser parser) throws IOException {
if (apiCallSection == null) {
throw new IllegalArgumentException("client call section is mandatory within a do section");
}
for (String w : expectedWarnings) {
if (allowedWarnings.contains(w)) {
throw new IllegalArgumentException("the warning [" + w + "] was both allowed and expected");
}
}
apiCallSection.addHeaders(headers);
apiCallSection.setNodeSelector(nodeSelector);
doSection.setApiCallSection(apiCallSection);
doSection.setExpectedWarningHeaders(unmodifiableList(expectedWarnings));
doSection.setAllowedWarningHeaders(unmodifiableList(allowedWarnings));
} finally {
parser.nextToken();
}
Expand All @@ -188,6 +207,7 @@ public static DoSection parse(XContentParser parser) throws IOException {
private String catchParam;
private ApiCallSection apiCallSection;
private List<String> expectedWarningHeaders = emptyList();
private List<String> allowedWarningHeaders = emptyList();

public DoSection(XContentLocation location) {
this.location = location;
Expand Down Expand Up @@ -225,6 +245,22 @@ void setExpectedWarningHeaders(List<String> expectedWarningHeaders) {
this.expectedWarningHeaders = expectedWarningHeaders;
}

/**
* Warning headers that we allow from this response. These warning
* headers don't cause the test to fail. Defaults to emptyList.
*/
List<String> getAllowedWarningHeaders() {
return allowedWarningHeaders;
}

/**
* Set the warning headers that we expect from this response. These
* warning headers don't cause the test to fail. Defaults to emptyList.
*/
void setAllowedWarningHeaders(List<String> allowedWarningHeaders) {
this.allowedWarningHeaders = allowedWarningHeaders;
}

@Override
public XContentLocation getLocation() {
return location;
Expand Down Expand Up @@ -284,15 +320,18 @@ void checkWarningHeaders(final List<String> warningHeaders, final Version master
final List<String> unexpected = new ArrayList<>();
final List<String> unmatched = new ArrayList<>();
final List<String> missing = new ArrayList<>();
Set<String> allowed = allowedWarningHeaders.stream()
.map(DeprecationLogger::escapeAndEncode)
.collect(toSet());
// LinkedHashSet so that missing expected warnings come back in a predictable order which is nice for testing
final Set<String> expected =
new LinkedHashSet<>(expectedWarningHeaders.stream().map(DeprecationLogger::escapeAndEncode).collect(Collectors.toList()));
final Set<String> expected = expectedWarningHeaders.stream()
.map(DeprecationLogger::escapeAndEncode)
.collect(toCollection(LinkedHashSet::new));
for (final String header : warningHeaders) {
final Matcher matcher = WARNING_HEADER_PATTERN.matcher(header);
final boolean matches = matcher.matches();
if (matches) {
final String message = matcher.group(1);
// noinspection StatementWithEmptyBody
if (masterVersion.before(Version.V_7_0_0)
&& message.equals("the default number of shards will change from [5] to [1] in 7.0.0; "
+ "if you wish to continue using the default of [5] shards, "
Expand All @@ -301,15 +340,19 @@ void checkWarningHeaders(final List<String> warningHeaders, final Version master
* This warning header will come back in the vast majority of our tests that create an index when running against an
* older master. Rather than rewrite our tests to assert this warning header, we assume that it is expected.
*/
} else // noinspection StatementWithEmptyBody
if (message.startsWith("[types removal]") || message.startsWith("[_data_frame/transforms/] is deprecated")) {
/*
* We skip warnings related to types deprecation and transform rename so that we can continue to run the many
* mixed-version tests that used typed APIs.
*/
} else if (expected.remove(message) == false) {
unexpected.add(header);
continue;
}
if (message.startsWith("[types removal]")) {
// We skip warnings related to types deprecation because they are *everywhere*.
continue;
}
if (allowed.contains(message)) {
continue;
}
if (expected.remove(message)) {
continue;
}
unexpected.add(header);
} else {
unmatched.add(header);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,19 @@ public void testAddingDoWithWarningWithoutSkipWarnings() {
"at line [" + lineNumber + "]"));
}

public void testAddingDoWithAllowedWarningWithoutSkipAllowedWarnings() {
int lineNumber = between(1, 10000);
DoSection doSection = new DoSection(new XContentLocation(lineNumber, 0));
doSection.setAllowedWarningHeaders(singletonList("foo"));
doSection.setApiCallSection(new ApiCallSection("test"));
ClientYamlTestSuite testSuite = createTestSuite(SkipSection.EMPTY, doSection);
Exception e = expectThrows(IllegalArgumentException.class, testSuite::validate);
assertThat(e.getMessage(), containsString("api/name:\nattempted to add a [do] with a [allowed_warnings] " +
"section without a corresponding [\"skip\": \"features\": \"allowed_warnings\"] so runners that do not " +
"support the [allowed_warnings] section can skip the test at line [" + lineNumber + "]"));
}


public void testAddingDoWithHeaderWithoutSkipHeaders() {
int lineNumber = between(1, 10000);
DoSection doSection = new DoSection(new XContentLocation(lineNumber, 0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ public void testWarningHeaders() {
"did not get expected warning headers [\n\tanother\n\tsome more\n]\n",
e.getMessage());
}

// "allowed" warnings are fine
{
final DoSection section = new DoSection(new XContentLocation(1, 1));
section.setAllowedWarningHeaders(singletonList("test"));
section.checkWarningHeaders(singletonList(testHeader), Version.CURRENT);
// and don't throw exceptions if we don't receive them
section.checkWarningHeaders(emptyList(), Version.CURRENT);
}
}

public void testIgnoreTypesWarnings() {
Expand Down Expand Up @@ -467,6 +476,54 @@ public void testParseDoSectionExpectedWarnings() throws Exception {
"just one entry this time")));
}

public void testParseDoSectionAllowedWarnings() throws Exception {
parser = createParser(YamlXContent.yamlXContent,
"indices.get_field_mapping:\n" +
" index: test_index\n" +
" type: test_type\n" +
"allowed_warnings:\n" +
" - some test warning they are typically pretty long\n" +
" - some other test warning sometimes they have [in] them"
);

DoSection doSection = DoSection.parse(parser);
assertThat(doSection.getCatch(), nullValue());
assertThat(doSection.getApiCallSection(), notNullValue());
assertThat(doSection.getApiCallSection().getApi(), equalTo("indices.get_field_mapping"));
assertThat(doSection.getApiCallSection().getParams().size(), equalTo(2));
assertThat(doSection.getApiCallSection().getParams().get("index"), equalTo("test_index"));
assertThat(doSection.getApiCallSection().getParams().get("type"), equalTo("test_type"));
assertThat(doSection.getApiCallSection().hasBody(), equalTo(false));
assertThat(doSection.getApiCallSection().getBodies().size(), equalTo(0));
assertThat(doSection.getAllowedWarningHeaders(), equalTo(Arrays.asList(
"some test warning they are typically pretty long",
"some other test warning sometimes they have [in] them")));

parser = createParser(YamlXContent.yamlXContent,
"indices.get_field_mapping:\n" +
" index: test_index\n" +
"allowed_warnings:\n" +
" - just one entry this time"
);

doSection = DoSection.parse(parser);
assertThat(doSection.getCatch(), nullValue());
assertThat(doSection.getApiCallSection(), notNullValue());
assertThat(doSection.getAllowedWarningHeaders(), equalTo(singletonList(
"just one entry this time")));

parser = createParser(YamlXContent.yamlXContent,
"indices.get_field_mapping:\n" +
" index: test_index\n" +
"warnings:\n" +
" - foo\n" +
"allowed_warnings:\n" +
" - foo"
);
Exception e = expectThrows(IllegalArgumentException.class, () -> DoSection.parse(parser));
assertThat(e.getMessage(), equalTo("the warning [foo] was both allowed and expected"));
}

public void testNodeSelectorByVersion() throws IOException {
parser = createParser(YamlXContent.yamlXContent,
"node_selector:\n" +
Expand Down
Loading