forked from linkedin/datahub-gma
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start adding ES integration test framework. (linkedin#25)
This adds the initial module, as well as some basic annotations and interfaces.
- Loading branch information
1 parent
4d3644d
commit a45c074
Showing
11 changed files
with
299 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
# Elasticsearch Integration Testing | ||
|
||
This module includes a framework to write integration tests against Elasticsearch using GMA, Junit 5, and assertj. | ||
|
||
Status: **In Development**. | ||
|
||
## Creating a new integration test | ||
|
||
1. Add this module as a dependency, as well as an [implementation](#implementations). | ||
2. Next, create a new Junit 5 test class and add the `@ElasticsearchIntegrationTest` annotation. Adding this annotation | ||
should start and stop an Elasticsearch instance for you during your test. | ||
3. Add fields to your test of type `SearchIndex`, and annotate them with `@SearchIndexType`. Static `SearchIndex` fields | ||
will reuse the index across the entire class; instance fields will create a new index per test. | ||
4. Set the settings / mappings of your index, either with the `@SearchIndexSettings` and `@SearchIndexMappings` | ||
annotations, or via methods on `SearchIndex`. | ||
5. Begin testing by data via `SearchIndex#getWriteDao` and asserting various queries. | ||
|
||
```java | ||
import com.linkedin.metadata.testing.ElasticsearchIntegrationTest; | ||
import com.linkedin.metadata.testing.SearchIndex; | ||
import com.linkedin.metadata.testing.annotations.SearchIndexMappings; | ||
import com.linkedin.metadata.testing.annotations.SearchIndexSettings; | ||
import com.linkedin.metadata.testing.annotations.SearchIndexType; | ||
import org.junit.jupiter.api.Test; | ||
|
||
@ElasticsearchIntegrationTest // 2 | ||
public class ExampleTest { | ||
@SearchIndexType(MySearchDocument.class) // 3 | ||
@SearchIndexSettings("/settings.json") // 4 | ||
@SearchIndexMappings("/mappings.json") // 4 | ||
SearchIndex<MySearchDocument> index; // 3 | ||
|
||
@Test | ||
public void example() { | ||
// 5 | ||
// given | ||
final MySearchDocument mySearchDocument = new MySearchDocument(); | ||
index.getWriteDao().upsertDocument(mySearchDocument, "myId"); | ||
index.getRequestContainer().flushAndSettle(); | ||
|
||
// TODO finish example once we've decided on how asserts look. | ||
} | ||
} | ||
``` | ||
|
||
## Implementations | ||
|
||
This module does not ship with code to actually start and stop Elasticsearch. It looks for any class in the | ||
`com.linkedin.metadata.testing` package annotated with `ElasticsearchContainerFactory.@Implementation`, and implements | ||
`ElasticsearchContainerFactory`, to use as the implementation to start / stop Elasticsearch. | ||
|
||
GMA ships with a default implementation in the `elasticsearch-dao-integ-testing-docker` module, which uses the | ||
[Testcontainers](http://testcontainers.org) to start / stop Elasticsearch using docker. You are also free to write your | ||
own implementation. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
apply plugin: 'java' | ||
|
||
dependencies { | ||
compile project(':dao-impl:elasticsearch-dao') | ||
|
||
compile externalDependency.assertJ | ||
compile externalDependency.junitJupiterApi | ||
compile externalDependency.junitJupiterParams | ||
|
||
testRuntimeOnly externalDependency.junitJupiterEngine | ||
testCompile project(':testing:test-models') | ||
} |
30 changes: 30 additions & 0 deletions
30
...ao-integ-testing/src/main/java/com/linkedin/metadata/testing/ElasticsearchConnection.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package com.linkedin.metadata.testing; | ||
|
||
import javax.annotation.Nonnull; | ||
import org.elasticsearch.client.RestHighLevelClient; | ||
import org.elasticsearch.client.transport.TransportClient; | ||
|
||
|
||
/** | ||
* POJO to hold Elasticsearch client objects. | ||
*/ | ||
public final class ElasticsearchConnection { | ||
private final RestHighLevelClient _restHighLevelClient; | ||
private final TransportClient _transportClient; | ||
|
||
public ElasticsearchConnection(@Nonnull RestHighLevelClient restHighLevelClient, | ||
@Nonnull TransportClient transportClient) { | ||
_restHighLevelClient = restHighLevelClient; | ||
_transportClient = transportClient; | ||
} | ||
|
||
@Nonnull | ||
public RestHighLevelClient getRestHighLevelClient() { | ||
return _restHighLevelClient; | ||
} | ||
|
||
@Nonnull | ||
public TransportClient getTransportClient() { | ||
return _transportClient; | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
...eg-testing/src/main/java/com/linkedin/metadata/testing/ElasticsearchContainerFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package com.linkedin.metadata.testing; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import javax.annotation.Nonnull; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
|
||
|
||
/** | ||
* Factory which can start and stop an Elasticsearch instance. | ||
*/ | ||
public interface ElasticsearchContainerFactory extends ExtensionContext.Store.CloseableResource { | ||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@interface Implementation { | ||
} | ||
|
||
/** | ||
* Starts an Elasticsearch instance for testing and returns clients connected to it. | ||
*/ | ||
@Nonnull | ||
ElasticsearchConnection start() throws Exception; | ||
} |
63 changes: 63 additions & 0 deletions
63
...teg-testing/src/main/java/com/linkedin/metadata/testing/ElasticsearchIntegrationTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package com.linkedin.metadata.testing; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Inherited; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
|
||
|
||
/** | ||
* Junit 5 annotation to indicate that this test requires an instance of Elasticsearch to run against. | ||
* | ||
* <p>Test classes that have this annotation should also contain one or more public {@link SearchIndex} fields. These | ||
* can be static or instance variables to control the test life cycle of the index. The extension will populate these | ||
* fields for you. | ||
* | ||
* <p>The {@link ElasticsearchContainerFactory} implementation, which starts and stops the Elasticsearch instance, is | ||
* loaded via reflection. A class marked with {@link | ||
* com.linkedin.metadata.testing.ElasticsearchContainerFactory.Implementation} within the {@code | ||
* com.linkedin.metadata.testing} namespace will be used. See the {@code elasticsearch-dao-integ-testing-docker} module | ||
* for a good default implementation that uses the <a href="https://www.testcontainers.org/">Testcontainers</a> | ||
* framework. | ||
* | ||
* <pre> | ||
* {@code | ||
* @ElasticsearchIntegrationTest | ||
* public class ExampleTest { | ||
* // Index which is created before any test are run, and is cleaned up after all tests are done. | ||
* @SearchIndexType(MySearchDocument.class) | ||
* public static SearchIndex<MySearchDocument> perClassIndex; | ||
* | ||
* // Index which is created before each test method and cleaned up after each test method. | ||
* @SearchIndexType(MySearchDocument.class) | ||
* public SearchIndex<MySearchDocument> perMethodIndex; | ||
* | ||
* @BeforeEach | ||
* public void setUpIndex() { | ||
* perMethodIndex.setSettingsAndMappings(/* load json file * /); | ||
* } | ||
* | ||
* @Test | ||
* public void example() { | ||
* // given | ||
* final BarSearchDocument searchDocument = new BarSearchDocument().setUrn(new BarUrn(42)); | ||
* | ||
* // when | ||
* _searchIndex.getWriteDao().upsertDocument(searchDocument, "mydoc"); | ||
* _searchIndex.getRequestContainer().flushAndSettle(); | ||
* | ||
* // then | ||
* assertThat(_searchIndex.getRequestContainer()).wroteOnlyDocuments("mydoc"); | ||
* } | ||
* } | ||
* } | ||
* </pre> | ||
*/ | ||
@Target(ElementType.TYPE) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
@ExtendWith(ElasticsearchIntegrationTestExtension.class) | ||
@Inherited | ||
public @interface ElasticsearchIntegrationTest { | ||
} |
37 changes: 37 additions & 0 deletions
37
...ng/src/main/java/com/linkedin/metadata/testing/ElasticsearchIntegrationTestExtension.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package com.linkedin.metadata.testing; | ||
|
||
import org.junit.jupiter.api.extension.AfterAllCallback; | ||
import org.junit.jupiter.api.extension.AfterEachCallback; | ||
import org.junit.jupiter.api.extension.BeforeAllCallback; | ||
import org.junit.jupiter.api.extension.BeforeEachCallback; | ||
import org.junit.jupiter.api.extension.ExtensionContext; | ||
|
||
|
||
/** | ||
* JUnit 5 extension to start an Elasticsearch instance and create indexes for testing with GMA. | ||
* | ||
* <p>See {@link ElasticsearchIntegrationTest}. | ||
*/ | ||
final class ElasticsearchIntegrationTestExtension | ||
implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback { | ||
|
||
@Override | ||
public void afterAll(ExtensionContext context) throws Exception { | ||
// TODO | ||
} | ||
|
||
@Override | ||
public void afterEach(ExtensionContext context) throws Exception { | ||
// TODO | ||
} | ||
|
||
@Override | ||
public void beforeAll(ExtensionContext context) throws Exception { | ||
// TODO | ||
} | ||
|
||
@Override | ||
public void beforeEach(ExtensionContext context) throws Exception { | ||
// TODO | ||
} | ||
} |
22 changes: 22 additions & 0 deletions
22
...-testing/src/main/java/com/linkedin/metadata/testing/annotations/SearchIndexMappings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.linkedin.metadata.testing.annotations; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
|
||
/** | ||
* Indicates the mappings this index should be created with. | ||
* | ||
* <p>Optional parameter for {@link com.linkedin.metadata.testing.SearchIndex}es in tests. Can be set directly on the | ||
* index after creation with {@link com.linkedin.metadata.testing.SearchIndex#setMappings(String)}. | ||
*/ | ||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface SearchIndexMappings { | ||
/** | ||
* The JSON resource file to load Elasticsearch mappings from. | ||
*/ | ||
String value(); | ||
} |
22 changes: 22 additions & 0 deletions
22
...-testing/src/main/java/com/linkedin/metadata/testing/annotations/SearchIndexSettings.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package com.linkedin.metadata.testing.annotations; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
|
||
/** | ||
* Indicates the settings this index should be created with. | ||
* | ||
* <p>Optional parameter for {@link com.linkedin.metadata.testing.SearchIndex}es in tests. Can be set directly on the | ||
* index after creation with {@link com.linkedin.metadata.testing.SearchIndex#setSettings(String)} (String)}. | ||
*/ | ||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface SearchIndexSettings { | ||
/** | ||
* The JSON resource file to load Elasticsearch settings from. | ||
*/ | ||
String value(); | ||
} |
26 changes: 26 additions & 0 deletions
26
...nteg-testing/src/main/java/com/linkedin/metadata/testing/annotations/SearchIndexType.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package com.linkedin.metadata.testing.annotations; | ||
|
||
import com.linkedin.data.template.RecordTemplate; | ||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
import javax.annotation.Nonnull; | ||
|
||
|
||
/** | ||
* Annotates the given {@link com.linkedin.metadata.testing.SearchIndex} field with the document type. | ||
* | ||
* <p>Required annotation for {@link com.linkedin.metadata.testing.SearchIndex} instances in tests.</p> | ||
*/ | ||
@Target(ElementType.FIELD) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface SearchIndexType { | ||
/** | ||
* The search document class for this index. | ||
* | ||
* <p>Used to create an instance of the {@link com.linkedin.metadata.testing.SearchIndex} during testing. | ||
*/ | ||
@Nonnull | ||
Class<? extends RecordTemplate> value(); | ||
} |