Skip to content

Commit

Permalink
Merge pull request #20 from GoodforGod/dev
Browse files Browse the repository at this point in the history
[0.10.0]
  • Loading branch information
GoodforGod authored Apr 14, 2024
2 parents 8a97676 + 7f2b713 commit 8582703
Show file tree
Hide file tree
Showing 166 changed files with 3,404 additions and 3,018 deletions.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,48 @@ Makes testing & asserts with Testcontainers even easier.
- [Cassandra](cassandra)
- [Redis](redis)
- [MockServer](mockserver)

## Usage

Here is an example of [Kafka Extension](kafka) where KafkaContainer is started in `PER_RUN` mode with topic reset per method:

```java

@TestcontainersKafka(mode = ContainerMode.PER_RUN,
topics = @Topics(value = "my-topic-name", reset = Topics.Mode.PER_METHOD))
class ExampleTests {

@ContainerKafkaConnection
private KafkaConnection kafkaConnection;

@Test
void test() {
var consumer = kafkaConnection.subscribe("my-topic-name");
kafkaConnection.send("my-topic-name", Event.ofValue("event1"), Event.ofValue("event2"));
consumer.assertReceivedAtLeast(2, Duration.ofSeconds(5));
}
}
```

Here is an example of [Postgres Extension](postgres) where PostgresContainer is started `PER_RUN` mode and migrations are applied per method:

```java

@TestcontainersPostgreSQL(mode = ContainerMode.PER_RUN,
migration = @Migration(
engine = Migration.Engines.FLYWAY,
apply = Migration.Mode.PER_METHOD,
drop = Migration.Mode.PER_METHOD))
class ExampleTests {

@ConnectionPostgreSQL
private JdbcConnection postgresConnection;

@Test
void test() {
postgresConnection.execute("INSERT INTO users VALUES(1);");
var usersFound = postgresConnection.queryMany("SELECT * FROM users;", r -> r.getInt(1));
assertEquals(1, usersFound.size());
}
}
```
84 changes: 29 additions & 55 deletions cassandra/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ Features:

**Gradle**
```groovy
testImplementation "io.goodforgod:testcontainers-extensions-cassandra:0.9.6"
testImplementation "io.goodforgod:testcontainers-extensions-cassandra:0.10.0"
```

**Maven**
```xml
<dependency>
<groupId>io.goodforgod</groupId>
<artifactId>testcontainers-extensions-cassandra</artifactId>
<version>0.9.6</version>
<version>0.10.0</version>
<scope>test</scope>
</dependency>
```
Expand All @@ -53,10 +53,9 @@ testImplementation "com.datastax.oss:java-driver-core:4.17.0"
## Content
- [Usage](#usage)
- [Old Driver](#container-old-driver)
- [Container](#container)
- [Connection](#container-connection)
- [Migration](#container-migration)
- [Annotation](#container)
- [Connection](#connection)
- [Migration](#connection-migration)
- [Annotation](#annotation)
- [Manual](#manual-container)
- [Network](#network)
- [Connection](#annotation-connection)
Expand All @@ -78,7 +77,7 @@ Test with container start in `PER_RUN` mode and migration per method will look l
class ExampleTests {

@Test
void test(@ContainerCassandraConnection CassandraConnection connection) {
void test(@ConnectionCassandra CassandraConnection connection) {
connection.execute("INSERT INTO cassandra.users(id) VALUES(1);");
var usersFound = connection.queryMany("SELECT * FROM cassandra.users;", r -> r.getInt(0));
assertEquals(1, usersFound.size());
Expand All @@ -93,63 +92,39 @@ class ExampleTests {
Library excludes [com.datastax.cassandra:cassandra-driver-core](https://mvnrepository.com/artifact/com.datastax.cassandra/cassandra-driver-core/3.10.0)
old driver from dependency leaking due to lots of vulnerabilities, if you require it add such dependency manually yourself.

## Container
## Connection

Library provides special `CassandraContainerExtra` with ability for migration and connection.
It can be used with [Testcontainers JUnit Extension](https://java.testcontainers.org/test_framework_integration/junit_5/).
`CassandraConnection` is an abstraction with asserting data in database container and easily manipulate container connection settings.
You can inject connection via `@ConnectionCassandra` as field or method argument or manually create it from container or manual settings.

```java
class ExampleTests {

private static final CassandraContainer<?> container = new CassandraContainer<>();

@Test
void test() {
try (var container = new CassandraContainerExtra<>(DockerImageName.parse("cassandra:4.1"))) {
container.start();
}
}
}
```

### Container Connection

`CassandraConnection` provides connection parameters, useful asserts, checks, etc. for easier testing.

```java
class ExampleTests {

@Test
void test() {
try (var container = new CassandraContainerExtra<>(DockerImageName.parse("cassandra:4.1"))) {
container.start();
container.connection().assertQueriesNone("SELECT * FROM cassandra.users;");
CassandraConnection connection = CassandraConnection.forContainer(container);
connection.execute("INSERT INTO users VALUES(1);");
}
}
}
```

### Container Migration
### Connection Migration

`Migrations` allow easily migrate database between test executions and drop after tests.

Annotation parameters:
- `engine` - to use for migration.
- `apply` - parameter configures migration mode.
- `drop` - configures when to reset/drop/clear database.

Available migration engines:
- Scripts - For `apply` load scripts from specified paths or directories and execute in ASC order, for `drop` clean all Non System tables in all cassandra
You can migrate container via `@TestcontainersCassandra#migration` annotation parameter or manually using `CassandraConnection`.

```java
@TestcontainersMariaDB
class ExampleTests {

@Test
void test() {
try (var container = new CassandraContainerExtra<>(DockerImageName.parse("cassandra:4.1"))) {
container.start();
container.migrate(Migration.Engines.SCRIPTS, List.of("migration"));
container.connection().assertQueriesNone("SELECT * FROM cassandra.users;");
container.drop(Migration.Engines.SCRIPTS, List.of("migration"));
}
void test(@ConnectionCassandra CassandraConnection connection) {
connection.migrationEngine(Migration.Engines.SCRIPTS).apply("migration/setup.cql");
connection.execute("INSERT INTO users VALUES(1);");
connection.migrationEngine(Migration.Engines.SCRIPTS).drop("migration/setup.cql");
}
}
```
Expand All @@ -172,7 +147,7 @@ Simple example on how to start container per class, **no need to configure** con
class ExampleTests {

@Test
void test(@ContainerCassandraConnection CassandraConnection connection) {
void test(@ConnectionCassandra CassandraConnection connection) {
assertNotNull(connection);
}
}
Expand Down Expand Up @@ -212,12 +187,10 @@ class ExampleTests {

@ContainerCassandra
private static final CassandraContainer<?> container = new CassandraContainer<>()
.withEnv("CASSANDRA_DC", "mydc")
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger(CassandraContainer.class)))
.withNetwork(Network.SHARED);
.withEnv("CASSANDRA_DC", "mydc");

@Test
void test(@ContainerCassandraConnection CassandraConnection connection) {
void test(@ConnectionCassandra CassandraConnection connection) {
assertEquals("mydc", connection.params().datacenter());
}
}
Expand Down Expand Up @@ -262,19 +235,19 @@ Image syntax:

### Annotation Connection

`CassandraConnection` - can be injected to field or method parameter and used to communicate with running container via `@ContainerCassandraConnection` annotation.
`CassandraConnection` - can be injected to field or method parameter and used to communicate with running container via `@ConnectionCassandra` annotation.
`CassandraConnection` provides connection parameters, useful asserts, checks, etc. for easier testing.

Example:
```java
@TestcontainersCassandra(mode = ContainerMode.PER_CLASS, image = "cassandra:4.1")
class ExampleTests {

@ContainerCassandraConnection
private CassandraConnection connectionInField;
@ConnectionCassandra
private CassandraConnection connection;

@Test
void test(@ContainerCassandraConnection CassandraConnection connection) {
void test() {
connection.execute("INSERT INTO cassandra.users(id) VALUES(1);");
connection.execute("INSERT INTO cassandra.users(id) VALUES(2);");
var usersFound = connection.queryMany("SELECT * FROM cassandra.users;", r -> r.getInt(0));
Expand All @@ -291,6 +264,7 @@ Annotation parameters:
- `engine` - to use for migration.
- `apply` - parameter configures migration mode.
- `drop` - configures when to reset/drop/clear database.
- `locations` - configures locations where migrations are placed.

Available migration engines:
- Scripts - For `apply` load scripts from specified paths or directories and execute in ASC order, for `drop` clean all Non System tables in all cassandra
Expand Down Expand Up @@ -318,7 +292,7 @@ Test with container and migration per method will look like:
class ExampleTests {

@Test
void test(@ContainerCassandraConnection CassandraConnection connection) {
void test(@ConnectionCassandra CassandraConnection connection) {
connection.execute("INSERT INTO cassandra.users(id) VALUES(1);");
connection.execute("INSERT INTO cassandra.users(id) VALUES(2);");
var usersFound = connection.queryMany("SELECT * FROM cassandra.users;", r -> r.getInt(0));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
import java.util.Optional;
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.NotNull;
import org.testcontainers.containers.CassandraContainer;

/**
* Describes active Cassandra connection of currently running {@link CassandraContainerExtra}
* Describes active Cassandra connection of currently running {@link CassandraContainer}
*/
public interface CassandraConnection {
public interface CassandraConnection extends AutoCloseable {

@FunctionalInterface
interface RowMapper<R, E extends Throwable> {
Expand Down Expand Up @@ -64,7 +65,10 @@ interface Params {
* @return new Cassandra connection
*/
@NotNull
CqlSession get();
CqlSession getConnection();

@NotNull
CassandraMigrationEngine migrationEngine(@NotNull Migration.Engines engine);

/**
* @param keyspaceName to create
Expand Down Expand Up @@ -180,4 +184,30 @@ <T, E extends Throwable> List<T> queryMany(@NotNull @Language("CQL") String cql,
* @return true if executed CQL results in exact number of expected rows
*/
boolean checkQueriesEquals(int expected, @NotNull @Language("CQL") String cql);

@Override
void close();

static CassandraConnection forContainer(CassandraContainer<?> container) {
if (!container.isRunning()) {
throw new IllegalStateException(container.getClass().getSimpleName() + " container is not running");
}

var params = new CassandraConnectionImpl.ParamsImpl(container.getHost(),
container.getMappedPort(CassandraContainer.CQL_PORT),
container.getLocalDatacenter(), container.getUsername(), container.getPassword());
final Params network = new CassandraConnectionImpl.ParamsImpl(container.getNetworkAliases().get(0),
CassandraContainer.CQL_PORT,
container.getLocalDatacenter(), container.getUsername(), container.getPassword());
return new CassandraConnectionClosableImpl(params, network);
}

static CassandraConnection forParams(String host,
int port,
String datacenter,
String username,
String password) {
var params = new CassandraConnectionImpl.ParamsImpl(host, port, datacenter, username, password);
return new CassandraConnectionClosableImpl(params, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.goodforgod.testcontainers.extensions.cassandra;

import org.jetbrains.annotations.ApiStatus.Internal;

@Internal
final class CassandraConnectionClosableImpl extends CassandraConnectionImpl {

CassandraConnectionClosableImpl(Params params, Params network) {
super(params, network);
}

@Override
public void close() {
super.stop();
}
}
Loading

0 comments on commit 8582703

Please sign in to comment.