# OpenMetadata Integration Tests
This module contains SDK-based integration tests that run against a real OpenMetadata server using Testcontainers. Tests execute in parallel and are isolated using `TestNamespace`.
## Quick Start
```bash
# Run all tests with MySQL + Elasticsearch (default)
mvn test -pl :openmetadata-integration-tests
# Run with PostgreSQL + OpenSearch
mvn test -pl :openmetadata-integration-tests -Ppostgres-opensearch
# Run a specific test
mvn test -pl :openmetadata-integration-tests -Dtest="TableResourceIT"
```
## Available Profiles
| Profile | Database | Search Engine |
|---------|----------|---------------|
| `mysql-elasticsearch` (default) | MySQL 8.3.0 | Elasticsearch 8.11.4 |
| `postgres-opensearch` | PostgreSQL 15 | OpenSearch 2.19.0 |
| `postgres-elasticsearch` | PostgreSQL 15 | Elasticsearch 8.11.4 |
| `mysql-opensearch` | MySQL 8.3.0 | OpenSearch 2.19.0 |
## Writing a New Integration Test
### 1. Create the Test Class
Extend `BaseEntityIT` for entity CRUD tests or create a standalone test class:
```java
package org.openmetadata.it.tests;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.openmetadata.it.util.TestNamespace;
import org.openmetadata.schema.api.data.CreateTable;
import org.openmetadata.schema.entity.data.Table;
public class MyFeatureIT extends BaseEntityIT
{
@Override
protected String getEntityType() {
return "table";
}
@Override
protected Table createEntity(CreateTable request) {
return SdkClients.adminClient().tables().create(request);
}
@Override
protected CreateTable createRequest(String name, TestNamespace ns) {
return new CreateTable()
.withName(name)
.withDatabaseSchema(SharedEntities.getSchema().getFullyQualifiedName())
.withColumns(List.of(new Column().withName("id").withDataType(ColumnDataType.INT)));
}
@Test
void myCustomTest(TestNamespace ns) throws Exception {
CreateTable request = createRequest(ns.prefix("myTable"), ns);
Table table = createEntity(request);
assertNotNull(table.getId());
assertEquals(request.getName(), table.getName());
}
}
```
### 2. Key Concepts
#### TestNamespace
Every test method receives a `TestNamespace` parameter that provides unique prefixes for entity names:
```java
@Test
void myTest(TestNamespace ns) {
String uniqueName = ns.prefix("myEntity"); // e.g., "abc123_myEntity"
}
```
This ensures tests don't conflict when running in parallel.
#### SdkClients
Get pre-configured SDK clients for different users:
```java
OpenMetadataClient adminClient = SdkClients.adminClient();
OpenMetadataClient user1Client = SdkClients.user1Client();
OpenMetadataClient botClient = SdkClients.botClient();
```
#### SharedEntities
Access pre-created entities for tests:
```java
DatabaseService service = SharedEntities.getService();
Database database = SharedEntities.getDatabase();
DatabaseSchema schema = SharedEntities.getSchema();
User adminUser = SharedEntities.getAdminUser();
```
### 3. BaseEntityIT Features
When extending `BaseEntityIT`, you get these tests automatically:
| Test | Description |
|------|-------------|
| `post_entityCreate_200` | Create entity successfully |
| `get_entity_200_OK` | Get entity by ID |
| `get_entityByName_200` | Get entity by FQN |
| `get_entityNotFound_404` | Get non-existent entity |
| `put_entityCreate_200` | Create via PUT |
| `patch_entityAttributes_200` | Patch entity attributes |
| `delete_entityAsAdmin_200` | Delete entity |
| `get_entityListWithPagination_200` | List with pagination |
| `test_sdkCRUDOperations` | Full CRUD via SDK |
| ... and 30+ more |
### 4. Controlling Test Behavior
Use flags to customize which inherited tests run:
```java
public class MyEntityIT extends BaseEntityIT {
{
supportsPatch = true; // Enable PATCH tests
supportsTags = true; // Enable tag tests
supportsOwner = true; // Enable owner tests
supportsSearchIndex = true; // Enable search tests
supportsDomains = true; // Enable domain tests
}
}
```
### 5. Best Practices
1. **Use `TestNamespace.prefix()`** for all entity names to ensure uniqueness
2. **Don't clean up entities** - TestNamespace isolation handles this
3. **Use specific imports** - No wildcard imports (`import static ....*`)
4. **Keep tests independent** - Don't rely on order of execution
5. **Use Awaitility for async operations** - Not `Thread.sleep()`
```java
Awaitility.await()
.atMost(Duration.ofSeconds(30))
.pollInterval(Duration.ofMillis(500))
.until(() -> someCondition());
```
6. **Avoid single-line comments** - Write self-documenting code
## Project Structure
```
openmetadata-integration-tests/
├── src/test/java/org/openmetadata/it/
│ ├── auth/ # JWT token generation
│ ├── env/ # Test infrastructure (TestSuiteBootstrap)
│ ├── factories/ # Entity factory classes
│ ├── tests/ # Integration test classes
│ └── util/ # Utilities (SdkClients, TestNamespace)
└── src/test/resources/
├── openmetadata-secure-test.yaml # Test config
└── *.der # JWT keys
```
## Test Infrastructure
Tests use `TestSuiteBootstrap` (a JUnit `LauncherSessionListener`) that:
1. Starts database container (MySQL or PostgreSQL)
2. Starts search container (Elasticsearch or OpenSearch)
3. Starts Fuseki SPARQL container (for RDF tests)
4. Starts the OpenMetadata application
5. Initializes `SharedEntities`
All containers are started **once** per test run and shared across all tests.
## Running in CI
GitHub workflows run these tests on every PR:
- `integration-tests-mysql-elasticsearch.yml` - MySQL + Elasticsearch
- `integration-tests-postgres-opensearch.yml` - PostgreSQL + OpenSearch
Tests require the "safe to test" label on PRs.