Skip to content

Commit 49c61e6

Browse files
authored
Introduces the Data Prepper plugin test framework. This includes annotations and a base class for plugin testing along with a couple of initial tests. Resolves #5908 (#5909)
Also, adds these tests to the grok processor as an initial processor to start using it. Signed-off-by: David Venable <dlv@amazon.com>
1 parent b390e55 commit 49c61e6

12 files changed

Lines changed: 594 additions & 3 deletions

File tree

data-prepper-plugins/grok-processor/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies {
1313
implementation "io.krakens:java-grok:0.1.9"
1414
implementation 'io.micrometer:micrometer-core'
1515
testImplementation project(':data-prepper-test:test-common')
16+
testImplementation project(':data-prepper-test:plugin-test-framework')
1617
}
1718

1819
jacocoTestCoverageVerification {

data-prepper-plugins/grok-processor/src/test/java/org/opensearch/dataprepper/plugins/processor/grok/GrokProcessorIT.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@
2020
import org.opensearch.dataprepper.model.configuration.PluginSetting;
2121
import org.opensearch.dataprepper.model.event.Event;
2222
import org.opensearch.dataprepper.model.plugin.InvalidPluginConfigurationException;
23+
import org.opensearch.dataprepper.model.processor.Processor;
2324
import org.opensearch.dataprepper.model.record.Record;
25+
import org.opensearch.dataprepper.test.plugins.DataPrepperPluginTest;
26+
import org.opensearch.dataprepper.test.plugins.junit.BaseDataPrepperPluginStandardTestSuite;
2427

2528
import java.util.ArrayList;
2629
import java.util.Collections;
@@ -39,7 +42,8 @@
3942
import static org.junit.jupiter.api.Assertions.assertThrows;
4043
import static org.opensearch.dataprepper.plugins.processor.grok.GrokProcessorTests.buildRecordWithEvent;
4144

42-
public class GrokProcessorIT {
45+
@DataPrepperPluginTest(pluginName = "grok", pluginType = Processor.class)
46+
public class GrokProcessorIT extends BaseDataPrepperPluginStandardTestSuite {
4347
private PluginSetting pluginSetting;
4448
private PluginMetrics pluginMetrics;
4549
private GrokProcessorConfig grokProcessorConfig;
@@ -81,7 +85,9 @@ public void setup() {
8185

8286
@AfterEach
8387
public void tearDown() {
84-
grokProcessor.shutdown();
88+
if(grokProcessor != null) {
89+
grokProcessor.shutdown();
90+
}
8591
}
8692

8793
private PluginSetting completePluginSettingForGrokProcessor(final boolean breakOnMatch,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
dependencies {
11+
implementation testLibs.junit.core
12+
implementation testLibs.hamcrest
13+
implementation project(':data-prepper-api')
14+
implementation project(':data-prepper-plugin-framework')
15+
implementation(libs.spring.context) {
16+
exclude group: 'commons-logging', module: 'commons-logging'
17+
}
18+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
package org.opensearch.dataprepper.test.plugins;
11+
12+
import java.lang.annotation.ElementType;
13+
import java.lang.annotation.Retention;
14+
import java.lang.annotation.RetentionPolicy;
15+
import java.lang.annotation.Target;
16+
17+
/**
18+
* An annotation for configuring plugin tests for a given plugin.
19+
*
20+
* @since 2.13
21+
*/
22+
@Retention(RetentionPolicy.RUNTIME)
23+
@Target(ElementType.TYPE)
24+
public @interface DataPrepperPluginTest {
25+
/**
26+
* Provides the name of the plugin.
27+
*
28+
* @since 2.13
29+
* @return the plugin name
30+
*/
31+
String pluginName();
32+
33+
/**
34+
* Configures the type of the plugin. This should be the same type
35+
* that is used with Data Prepper, such as {@link org.opensearch.dataprepper.model.processor.Processor}.
36+
*
37+
* @since 2.13
38+
* @return The class type of the plugin.
39+
*/
40+
Class<?> pluginType();
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
package org.opensearch.dataprepper.test.plugins.junit;
11+
12+
import org.junit.jupiter.api.DynamicTest;
13+
import org.junit.jupiter.api.TestFactory;
14+
import org.junit.jupiter.api.extension.ExtendWith;
15+
import org.opensearch.dataprepper.plugin.PluginProvider;
16+
17+
import java.util.stream.Stream;
18+
19+
/**
20+
* Provides a common base class that plugin authors can use to create
21+
* Data Prepper plugin tests along with standard tests.
22+
*
23+
* @since 2.13
24+
*/
25+
@ExtendWith(DataPrepperPluginTestFrameworkExtension.class)
26+
public class BaseDataPrepperPluginStandardTestSuite {
27+
@TestFactory
28+
Stream<DynamicTest> standardDataPrepperPluginTests(
29+
final DataPrepperPluginTestContext dataPrepperPluginTestContext,
30+
final PluginProvider pluginProvider) {
31+
return DataPrepperPluginAutoTestSuite.generateTests(dataPrepperPluginTestContext, pluginProvider);
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
package org.opensearch.dataprepper.test.plugins.junit;
11+
12+
import org.junit.jupiter.api.DynamicTest;
13+
import org.opensearch.dataprepper.plugin.PluginProvider;
14+
15+
import java.util.Optional;
16+
import java.util.stream.Stream;
17+
18+
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.hamcrest.Matchers.equalTo;
20+
import static org.hamcrest.Matchers.matchesPattern;
21+
22+
class DataPrepperPluginAutoTestSuite {
23+
static Stream<DynamicTest> generateTests(
24+
final DataPrepperPluginTestContext dataPrepperPluginTestContext,
25+
final PluginProvider pluginProvider) {
26+
return Stream.of(
27+
DynamicTest.dynamicTest("Plugin can be found by the plugin framework", () ->
28+
{
29+
final Optional<Class<?>> optional = pluginProvider.findPluginClass(
30+
(Class) dataPrepperPluginTestContext.getPluginType(),
31+
dataPrepperPluginTestContext.getPluginName());
32+
33+
assertThat("Unable to locate the plugin class", optional.isPresent(), equalTo(true));
34+
}
35+
36+
),
37+
DynamicTest.dynamicTest("Plugin name conforms to standard naming convention", () ->
38+
assertThat(dataPrepperPluginTestContext.getPluginName(), matchesPattern("^[a-z0-9_]+$"))
39+
)
40+
);
41+
}
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
package org.opensearch.dataprepper.test.plugins.junit;
11+
12+
import java.util.Objects;
13+
14+
class DataPrepperPluginTestContext {
15+
private final String pluginName;
16+
private final Class<?> pluginType;
17+
18+
DataPrepperPluginTestContext(
19+
final String pluginName,
20+
final Class<?> pluginType) {
21+
this.pluginName = Objects.requireNonNull(pluginName);
22+
this.pluginType = Objects.requireNonNull(pluginType);
23+
}
24+
25+
public String getPluginName() {
26+
return pluginName;
27+
}
28+
29+
public Class<?> getPluginType() {
30+
return pluginType;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*
5+
* The OpenSearch Contributors require contributions made to
6+
* this file be licensed under the Apache-2.0 license or a
7+
* compatible open source license.
8+
*/
9+
10+
package org.opensearch.dataprepper.test.plugins.junit;
11+
12+
import org.junit.jupiter.api.extension.ExtensionContext;
13+
import org.junit.jupiter.api.extension.ParameterContext;
14+
import org.junit.jupiter.api.extension.ParameterResolutionException;
15+
import org.junit.jupiter.api.extension.ParameterResolver;
16+
import org.opensearch.dataprepper.plugin.ClasspathPluginProvider;
17+
import org.opensearch.dataprepper.plugin.PluginProvider;
18+
import org.opensearch.dataprepper.test.plugins.DataPrepperPluginTest;
19+
20+
import java.util.Set;
21+
22+
/**
23+
* A JUnit extension for using the Data Prepper plugin test framework.
24+
*/
25+
public class DataPrepperPluginTestFrameworkExtension implements ParameterResolver {
26+
private static final Set<Class<?>> SUPPORTED_CLASSES = Set.of(
27+
DataPrepperPluginTestContext.class,
28+
PluginProvider.class
29+
);
30+
31+
@Override
32+
public boolean supportsParameter(final ParameterContext parameterContext, final ExtensionContext extensionContext) throws ParameterResolutionException {
33+
return SUPPORTED_CLASSES.contains(parameterContext.getParameter().getType());
34+
}
35+
36+
@Override
37+
public Object resolveParameter(
38+
final ParameterContext parameterContext,
39+
final ExtensionContext extensionContext) throws ParameterResolutionException {
40+
final Class<?> type = parameterContext.getParameter().getType();
41+
42+
if (type.equals(DataPrepperPluginTestContext.class)) {
43+
final Class<?> testClass = extensionContext.getRequiredTestClass();
44+
final DataPrepperPluginTest annotation = testClass.getAnnotation(DataPrepperPluginTest.class);
45+
if (annotation == null) {
46+
throw new ParameterResolutionException("Missing @DataPrepperPluginTest annotation on class: " + testClass.getName());
47+
}
48+
return new DataPrepperPluginTestContext(annotation.pluginName(), annotation.pluginType());
49+
} else if (type.equals(PluginProvider.class)) {
50+
return new ClasspathPluginProvider();
51+
}
52+
53+
throw new ParameterResolutionException("Unsupported parameter type: " + type);
54+
}
55+
}

0 commit comments

Comments
 (0)