blob: d2e7f77dfec2eb446f2f2545fd3d495ee0285b80 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jmeter.extractor.json.jmespath;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.testelement.AbstractScopedTestElement;
import org.apache.jmeter.threads.JMeterContext;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class TestJMESPathExtractor {
private static final String DEFAULT_VALUE = "NONE"; // $NON-NLS-1$
private static final String REFERENCE_NAME = "varname"; // $NON-NLS-1$
private static final String REFERENCE_NAME_MATCH_NUMBER = "varname_matchNr"; // $NON-NLS-1$
private static JMESPathExtractor setupProcessor(JMeterVariables vars, SampleResult sampleResult, String data, boolean isSourceVars, String matchNumbers) {
JMeterContext jmctx = JMeterContextService.getContext();
jmctx.setVariables(vars);
jmctx.setPreviousResult(sampleResult);
JMESPathExtractor processor = new JMESPathExtractor();
processor.setThreadContext(jmctx);
processor.setRefName(REFERENCE_NAME);
processor.setMatchNumber(matchNumbers);
processor.setDefaultValue(DEFAULT_VALUE);
if (isSourceVars) {
vars.put("contentvar", data);
processor.setScopeVariable("contentvar");
} else {
sampleResult.setResponseData(data, null);
processor.setScopeAll();
}
return processor;
}
@Test
void testNoMatchNumberSet() {
JMeterVariables vars = new JMeterVariables();
SampleResult sampleResult = new SampleResult();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "[1]", false, "");
processor.setJmesPathExpression("[*]");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is("1"));
}
private static Stream<Arguments> dataOneMatch() {
return Stream.of(
Arguments.of("[\"one\"]", "[*]", "one", "1"),
Arguments.of("{\"a\": {\"b\": {\"c\": {\"d\": \"value\"}}}}", "a.b.c.d", "value", "1"),
Arguments.of("{\r\n" + " \"people\": [\r\n" + " {\"first\": \"James\", \"last\": \"d\"},\r\n"
+ " {\"first\": \"Jacob\", \"last\": \"e\"},\r\n"
+ " {\"first\": \"Jayden\", \"last\": \"f\"},\r\n" + " {\"missing\": \"different\"}\r\n"
+ " ],\r\n" + " \"foo\": {\"bar\": \"baz\"}\r\n" + "}", "people[2]",
"{\"first\":\"Jayden\",\"last\":\"f\"}",
"1")
);
}
@ParameterizedTest(name = "TestFromVars: {index} Extract from {0} with path {1} should result in {2} for match {3}")
@MethodSource("dataOneMatch")
void testOneMatchFromVars(String data, String jmesPath, String expectedResult, String expectedMatchNumber) {
testOneMatchOnAllExtractedValues(true, data, jmesPath, expectedResult, expectedMatchNumber);
}
@ParameterizedTest(name = "TestFromSampleResult: {index} Extract from {0} with path {1} should result in {2} for match {3}")
@MethodSource("dataOneMatch")
void testOneFromSampleResult(String data, String jmesPath, String expectedResult, String expectedMatchNumber) {
testOneMatchOnAllExtractedValues(false, data, jmesPath, expectedResult, expectedMatchNumber);
}
private void testOneMatchOnAllExtractedValues(boolean fromVars, String data, String jmesPath, String expectedResult, String expectedMatchNumber) {
JMeterVariables vars = new JMeterVariables();
SampleResult sampleResult = new SampleResult();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, data, fromVars, "-1");
processor.setJmesPathExpression(jmesPath);
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME + "_1"), CoreMatchers.is(expectedResult));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(expectedMatchNumber));
processor.clearOldRefVars(vars, REFERENCE_NAME);
assertThat(vars.get(REFERENCE_NAME + "_1"), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(CoreMatchers.nullValue()));
}
private static Stream<Arguments> dataMultipleMatches() {
return Stream.of(
Arguments.of("[\"one\", \"two\"]", "[*]", new String[] {"one", "two"}, "2"),
Arguments.of("[\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]", "[0:3]", new String[] {"a", "b","c"}, "3"),
Arguments.of("{\r\n" + " \"people\": [\r\n" + " {\"first\": \"James\", \"last\": \"d\"},\r\n"
+ " {\"first\": \"Jacob\", \"last\": \"e\"},\r\n"
+ " {\"first\": \"Jayden\", \"last\": \"f\"},\r\n" + " {\"missing\": \"different\"}\r\n"
+ " ],\r\n" + " \"foo\": {\"bar\": \"baz\"}\r\n" + "}", "people[:2].first", new String[] {"James", "Jacob"}, "2")
);
}
@ParameterizedTest
@MethodSource("dataMultipleMatches")
void testMultipleMatchesOnAllExtractedValuesFromVars(String data, String jmesPath, String[] expectedResults, String expectedMatchNumber) {
testMultipleMatchesOnAllExtractedValues(true, data, jmesPath, expectedResults, expectedMatchNumber);
}
@ParameterizedTest
@MethodSource("dataMultipleMatches")
void testMultipleMatchesOnAllExtractedValuesFromSampleResult(String data, String jmesPath, String[] expectedResults, String expectedMatchNumber) {
testMultipleMatchesOnAllExtractedValues(false, data, jmesPath, expectedResults, expectedMatchNumber);
}
private void testMultipleMatchesOnAllExtractedValues(boolean fromVars, String data, String jmesPath, String[] expectedResults, String expectedMatchNumber) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, data, fromVars, "-1");
// test1
processor.setJmesPathExpression(jmesPath);
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(CoreMatchers.nullValue()));
for (int i = 0; i < expectedResults.length; i++) {
assertThat(vars.get(REFERENCE_NAME + "_"+(i+1)), CoreMatchers.is(expectedResults[i]));
}
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(expectedMatchNumber));
}
private static final String TEST_DATA = "{\r\n" + " \"people\": [\r\n" + " {\"first\": \"James\", \"last\": \"d\", \"age\":10},\r\n"
+ " {\"first\": \"Jacob\", \"last\": \"e\", \"age\":20},\r\n"
+ " {\"first\": \"Jayden\", \"last\": \"f\", \"age\":30},\r\n"
+ " {\"missing\": \"different\"}\r\n" + " ],\r\n" + " \"foo\": {\"bar\": \"baz\"}\r\n"
+ "}";
private static Stream<Arguments> dataMatchNumberMoreThanZero() {
return Stream.of(
Arguments.of(TEST_DATA, "people[:3].first", "1", "James", "3"),
Arguments.of(TEST_DATA, "people[:3].first", "2", "Jacob", "3"),
Arguments.of(TEST_DATA, "people[:3].first", "3", "Jayden", "3"),
Arguments.of(TEST_DATA, "people[:3].age", "3", "30", "3"),
Arguments.of(TEST_DATA, "people[:3].first", "4", DEFAULT_VALUE, "3")
);
}
@ParameterizedTest
@MethodSource("dataMatchNumberMoreThanZero")
void testFromVars(String data, String jmesPath,
String matchNumber, String expectedResult, String expectedMatchNumber) {
testMatchNumberMoreThanZeroOn1ExtractedValue(true, data, jmesPath, matchNumber, expectedResult, expectedMatchNumber);
}
@ParameterizedTest
@MethodSource("dataMatchNumberMoreThanZero")
void testFromSampleResult(String data, String jmesPath,
String matchNumber, String expectedResult, String expectedMatchNumber) {
testMatchNumberMoreThanZeroOn1ExtractedValue(false, data, jmesPath, matchNumber, expectedResult, expectedMatchNumber);
}
private void testMatchNumberMoreThanZeroOn1ExtractedValue(boolean fromVars, String data, String jmesPath,
String matchNumber, String expectedResult, String expectedMatchNumber) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, data, fromVars, "1");
processor.setMatchNumber(matchNumber);
processor.setJmesPathExpression(jmesPath);
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(expectedResult));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(expectedMatchNumber));
}
enum AccessMode {
ALL(AbstractScopedTestElement::setScopeAll),
PARENT(AbstractScopedTestElement::setScopeParent),
CHILDREN(AbstractScopedTestElement::setScopeChildren);
@SuppressWarnings("ImmutableEnumChecker")
private final Consumer<AbstractScopedTestElement> applier;
AccessMode(Consumer<AbstractScopedTestElement> applier) {
this.applier = applier;
}
void configure(AbstractScopedTestElement element) {
applier.accept(element);
}
}
private static Stream<Arguments> dataScopedSamples() {
return Stream.of(
Arguments.of(AccessMode.ALL, "a", "1", "23", "2"),
Arguments.of(AccessMode.ALL, "a", "2", "42", "2"),
Arguments.of(AccessMode.ALL, "b", "0", "parent_only", "1"),
Arguments.of(AccessMode.ALL, "c", "0", "child_only", "1"),
Arguments.of(AccessMode.PARENT, "a", "1", "23", "1"),
Arguments.of(AccessMode.PARENT, "b", "0", "parent_only", "1"),
Arguments.of(AccessMode.PARENT, "c", "0", "NONE", "0"),
Arguments.of(AccessMode.CHILDREN, "a", "1", "42", "1"),
Arguments.of(AccessMode.CHILDREN, "b", "0", "NONE", "0"),
Arguments.of(AccessMode.CHILDREN, "c", "0", "child_only", "1")
);
}
@ParameterizedTest(name = "{index}: Mode: {0} Path: {1} MatchNr: {2} Result: {3} Count: {4}")
@MethodSource("dataScopedSamples")
void testRandomElementAllMatches(AccessMode accessMode, String path, String matchNumber, String resultObject, String resultCount) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "{\"a\": 23, \"b\": \"parent_only\"}", false, matchNumber);
SampleResult subSample = new SampleResult();
subSample.setResponseData("{\"a\": 42, \"c\": \"child_only\"}", null);
sampleResult.addSubResult(subSample);
processor.setJmesPathExpression(path);
accessMode.configure(processor);
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(resultObject));
assertThat(vars.get(REFERENCE_NAME + "_1"), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(resultCount));
}
private static Stream<Arguments> dataSourceVarOrResponse() {
return Stream.of(Arguments.of(Boolean.TRUE), Arguments.of(Boolean.FALSE));
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testRandomElementOneMatch(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "{\"a\": {\"b\": {\"c\": {\"d\": \"value\"}}}}", fromVariables, "0");
processor.setJmesPathExpression("a.b.c.d");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is("value"));
assertThat(vars.get(REFERENCE_NAME + "_1"), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is("1"));
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testRandomElementMultipleMatches(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "[\"one\", \"two\"]", fromVariables, "0");
processor.setJmesPathExpression("[*]");
processor.process();
assertThat(vars.get(REFERENCE_NAME),
CoreMatchers.is(CoreMatchers.anyOf(CoreMatchers.is("one"), CoreMatchers.is("two"))));
assertThat(vars.get(REFERENCE_NAME + "_1"), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME + "_2"), CoreMatchers.is(CoreMatchers.nullValue()));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is("2"));
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testEmptySourceData(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "", fromVariables, "-1");
processor.setJmesPathExpression("[*]");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(DEFAULT_VALUE));
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is(CoreMatchers.nullValue()));
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testErrorInJMESPath(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "{\"a\": {\"b\": {\"c\": {\"d\": \"value\"}}}}", fromVariables, "-1");
processor.setJmesPathExpression("$.k");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(DEFAULT_VALUE));
assertThat(vars.get(REFERENCE_NAME+ "_1"), CoreMatchers.nullValue());
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.nullValue());
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testNoMatch(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "{\"a\": {\"b\": {\"c\": {\"d\": \"value\"}}}}", fromVariables, "-1");
processor.setJmesPathExpression("a.b.c.f");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(DEFAULT_VALUE));
assertThat(vars.get(REFERENCE_NAME+ "_1"), CoreMatchers.nullValue());
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.is("0"));
}
@ParameterizedTest
@MethodSource("dataSourceVarOrResponse")
void testNoInput(boolean fromVariables) {
SampleResult sampleResult = new SampleResult();
JMeterVariables vars = new JMeterVariables();
JMESPathExtractor processor = setupProcessor(vars, sampleResult, "", fromVariables, "0");
processor.setJmesPathExpression("a.b");
processor.process();
assertThat(vars.get(REFERENCE_NAME), CoreMatchers.is(DEFAULT_VALUE));
assertThat(vars.get(REFERENCE_NAME+ "_1"), CoreMatchers.nullValue());
assertThat(vars.get(REFERENCE_NAME_MATCH_NUMBER), CoreMatchers.nullValue());
}
}