Skip to content

build(test): use testcontainers for postgresql tests #4831

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

Merged
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
9 changes: 0 additions & 9 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,6 @@ jobs:

Postgresql-Integration-Tests:
runs-on: ubuntu-latest

services:
postgres:
image: postgres:14.2
ports:
- 5432:5432
env:
POSTGRES_PASSWORD: password

steps:
- uses: actions/checkout@v4
- uses: eclipse-edc/.github/.github/actions/setup-build@main
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright (c) 2025 Cofinity-X
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Cofinity-X - initial API and implementation
*
*/

package org.eclipse.edc.sql.testfixtures;

import org.eclipse.edc.spi.system.configuration.Config;
import org.eclipse.edc.spi.system.configuration.ConfigFactory;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.testcontainers.containers.PostgreSQLContainer;

import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Map;

import static java.lang.String.format;
import static org.testcontainers.containers.PostgreSQLContainer.POSTGRESQL_PORT;

/**
* Extension to be used in end-to-end tests with Postgresql persistence
*/
public class PostgresqlEndToEndExtension implements BeforeAllCallback, AfterAllCallback {

private static final String DEFAULT_IMAGE = "postgres:17.3";

private final PostgreSQLContainer<?> postgres;

public PostgresqlEndToEndExtension() {
this(DEFAULT_IMAGE);
}

public PostgresqlEndToEndExtension(String dockerImageName) {
postgres = new PostgreSQLContainer<>(dockerImageName);
}

@Override
public void beforeAll(ExtensionContext context) {
postgres.start();
}

@Override
public void afterAll(ExtensionContext context) {
postgres.stop();
postgres.close();
}

/**
* Return config suitable for EDC runtime for default database.
*
* @return the config.
*/
public Config config() {
return configFor(postgres.getDatabaseName());
}

/**
* Return config suitable for EDC runtime giving the database name.
*
* @param databaseName the database name;
* @return the config.
*/
public Config configFor(String databaseName) {
var settings = Map.of(
"edc.datasource.default.url", "jdbc:postgresql://%s:%d/%s"
.formatted(postgres.getHost(), postgres.getMappedPort(POSTGRESQL_PORT), databaseName),
"edc.datasource.default.user", postgres.getUsername(),
"edc.datasource.default.password", postgres.getPassword(),
"edc.sql.schema.autocreate", "true"
);

return ConfigFactory.fromMap(settings);
}

public void createDatabase(String name) {
var jdbcUrl = postgres.getJdbcUrl() + postgres.getDatabaseName();
try (var connection = DriverManager.getConnection(jdbcUrl, postgres.getUsername(), postgres.getPassword())) {
connection.createStatement().execute(format("create database %s;", name));
} catch (SQLException e) {
// database could already exist
}
}

}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@

package org.eclipse.edc.sql.testfixtures;

import org.eclipse.edc.spi.system.configuration.Config;
import org.eclipse.edc.spi.system.configuration.ConfigFactory;
import org.eclipse.edc.sql.DriverManagerConnectionFactory;
import org.eclipse.edc.sql.QueryExecutor;
import org.eclipse.edc.sql.SqlQueryExecutor;
Expand All @@ -36,90 +34,58 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.UUID;

import static java.lang.String.format;
import static org.eclipse.edc.util.io.Ports.getFreePort;

/**
* Extension for running PG SQL store implementation. It automatically creates a test database and provided all the base data structure
* for a SQL store to run such as {@link DataSourceRegistry}, {@link TransactionContext} and data source name which is automatically generated
* Extension for running PostgreSQL store implementation tests. It starts a database container and provides all the base
* data structure for a SQL store to run such as {@link DataSourceRegistry}, {@link TransactionContext} and data source
* name which is automatically generated.
*/
public class PostgresqlStoreSetupExtension implements BeforeEachCallback, BeforeAllCallback, AfterAllCallback, ParameterResolver {

public static final String POSTGRES_IMAGE_NAME = "postgres:16.1";
private final PostgreSQLContainer<?> postgreSqlContainer = new PostgreSQLContainer<>(POSTGRES_IMAGE_NAME)
.withExposedPorts(5432)
.withUsername("postgres")
.withPassword("password")
.withDatabaseName("itest");
private final PostgresqlLocalInstance postgres;
private static final String DEFAULT_IMAGE = "postgres:17.3";

private final PostgreSQLContainer<?> postgres;
private final QueryExecutor queryExecutor = new SqlQueryExecutor();
private final TransactionContext transactionContext = new NoopTransactionContext();
private final DataSourceRegistry dataSourceRegistry = new DefaultDataSourceRegistry();
private final String datasourceName = UUID.randomUUID().toString();
private final String jdbcUrlPrefix;

public PostgresqlStoreSetupExtension() {
var exposedPort = getFreePort();
postgreSqlContainer.setPortBindings(List.of("%s:5432".formatted(exposedPort)));
jdbcUrlPrefix = format("jdbc:postgresql://%s:%s/", postgreSqlContainer.getHost(), exposedPort);
postgres = new PostgresqlLocalInstance(postgreSqlContainer.getUsername(), postgreSqlContainer.getPassword(), jdbcUrlPrefix);
}

public String getDatasourceName() {
return datasourceName;
this(DEFAULT_IMAGE);
}

public Connection getConnection() {
try {
return dataSourceRegistry.resolve(datasourceName).getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

public int runQuery(String query) {
return transactionContext.execute(() -> queryExecutor.execute(getConnection(), query));
}

public TransactionContext getTransactionContext() {
return transactionContext;
}

public DataSourceRegistry getDataSourceRegistry() {
return dataSourceRegistry;
public PostgresqlStoreSetupExtension(String dockerImageName) {
postgres = new PostgreSQLContainer<>(dockerImageName);
}

@Override
public void beforeAll(ExtensionContext context) {
postgreSqlContainer.start();
postgres.createDatabase(postgreSqlContainer.getDatabaseName());
postgres.start();
}

@Override
public void beforeEach(ExtensionContext context) {
var properties = new Properties();
properties.put("user", postgreSqlContainer.getUsername());
properties.put("password", postgreSqlContainer.getPassword());
properties.put("user", postgres.getUsername());
properties.put("password", postgres.getPassword());
var connectionFactory = new DriverManagerConnectionFactory();
var jdbcUrl = jdbcUrlPrefix + postgreSqlContainer.getDatabaseName();
var jdbcUrl = postgres.getJdbcUrl() + postgres.getDatabaseName();
var dataSource = new ConnectionFactoryDataSource(connectionFactory, jdbcUrl, properties);
dataSourceRegistry.register(datasourceName, dataSource);
}

@Override
public void afterAll(ExtensionContext context) {
postgreSqlContainer.stop();
postgreSqlContainer.close();
postgres.stop();
postgres.close();
}

@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
var type = parameterContext.getParameter().getParameterizedType();
return List.of(PostgresqlStoreSetupExtension.class, Connection.class, QueryExecutor.class, PostgresqlLocalInstance.class).contains(type);
return List.of(PostgresqlStoreSetupExtension.class, Connection.class, QueryExecutor.class).contains(type);
}

@Override
Expand All @@ -131,17 +97,32 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
return getConnection();
} else if (type.equals(QueryExecutor.class)) {
return queryExecutor;
} else if (type.equals(PostgresqlLocalInstance.class)) {
return postgres;
}
return null;
}

public Config getDatasourceConfig() {
return ConfigFactory.fromMap(getDatasourceConfiguration());
public String getDatasourceName() {
return datasourceName;
}

public Connection getConnection() {
try {
return dataSourceRegistry.resolve(datasourceName).getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

public int runQuery(String query) {
return transactionContext.execute(() -> queryExecutor.execute(getConnection(), query));
}

public Map<String, String> getDatasourceConfiguration() {
return postgres.createDefaultDatasourceConfiguration(postgreSqlContainer.getDatabaseName());
public TransactionContext getTransactionContext() {
return transactionContext;
}

public DataSourceRegistry getDataSourceRegistry() {
return dataSourceRegistry;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;

import java.io.IOException;

@ComponentTest
@ExtendWith(PostgresqlStoreSetupExtension.class)
public class SqlEndpointDataReferenceEntryIndexTest extends EndpointDataReferenceEntryIndexTestBase {
Expand All @@ -39,7 +37,7 @@ public class SqlEndpointDataReferenceEntryIndexTest extends EndpointDataReferenc
private SqlEndpointDataReferenceEntryIndex entryIndex;

@BeforeEach
void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) throws IOException {
void setUp(PostgresqlStoreSetupExtension extension, QueryExecutor queryExecutor) {

entryIndex = new SqlEndpointDataReferenceEntryIndex(extension.getDataSourceRegistry(), extension.getDatasourceName(),
extension.getTransactionContext(), new ObjectMapper(), statements, queryExecutor);
Expand Down
Loading