Skip to content

Commit 4059ed7

Browse files
DavideDSanne
authored andcommitted
[#1686] Add verticle-postgres-it integration test
1 parent 195941c commit 4059ed7

File tree

8 files changed

+600
-0
lines changed

8 files changed

+600
-0
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
buildscript {
2+
repositories {
3+
// Example: ./gradlew build -PenableMavenLocalRepo
4+
if ( project.hasProperty( 'enableMavenLocalRepo' ) ) {
5+
// Useful for local development, it should be disabled otherwise
6+
mavenLocal()
7+
}
8+
mavenCentral()
9+
}
10+
}
11+
12+
description = 'Bytecode enhancements integration tests'
13+
14+
ext {
15+
jacksonDatabindVersion = '2.15.2'
16+
jbossLoggingVersion = '3.5.0.Final'
17+
assertjVersion = '3.24.2'
18+
}
19+
20+
dependencies {
21+
implementation project(':hibernate-reactive-core')
22+
implementation "io.vertx:vertx-web:${vertxVersion}"
23+
implementation "io.vertx:vertx-web-client:${vertxVersion}"
24+
25+
runtimeOnly "io.vertx:vertx-pg-client:${vertxVersion}"
26+
// The Pg client requires this dependency
27+
runtimeOnly "com.ongres.scram:client:2.1"
28+
runtimeOnly "com.fasterxml.jackson.core:jackson-databind:${jacksonDatabindVersion}"
29+
30+
// logging
31+
implementation "org.jboss.logging:jboss-logging:${jbossLoggingVersion}"
32+
33+
// Testcontainers
34+
implementation "org.testcontainers:postgresql:${testcontainersVersion}"
35+
36+
// Testing
37+
testImplementation "org.assertj:assertj-core:${assertjVersion}"
38+
testImplementation "io.vertx:vertx-junit5:${vertxVersion}"
39+
}
40+
41+
// Configuration for the tests
42+
tasks.withType( Test ).configureEach {
43+
defaultCharacterEncoding = "UTF-8"
44+
useJUnitPlatform()
45+
testLogging {
46+
showStandardStreams = project.hasProperty( 'showStandardOutput' )
47+
showStackTraces = true
48+
exceptionFormat = 'full'
49+
displayGranularity = 1
50+
events = ['PASSED', 'FAILED', 'SKIPPED']
51+
}
52+
systemProperty 'docker', project.hasProperty( 'docker' ) ? 'true' : 'false'
53+
systemProperty 'org.hibernate.reactive.common.InternalStateAssertions.ENFORCE', 'true'
54+
55+
if ( project.hasProperty( 'includeTests' ) ) {
56+
// Example: ./gradlew testAll -PincludeTests=DefaultPortTest
57+
filter {
58+
includeTestsMatching project.getProperty( 'includeTests' ) ?: '*'
59+
}
60+
}
61+
}
62+
63+
// Print a summary of the results of the tests (number of failures, successes and skipped)
64+
// This is the same as the one in hibernate-reactive-core
65+
def loggingSummary(db, result, desc) {
66+
if ( !desc.parent ) { // will match the outermost suite
67+
def output = "${db} results: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} passed, ${result.failedTestCount} failed, ${result.skippedTestCount} skipped)"
68+
def repeatLength = output.length() + 1
69+
logger.lifecycle '\n' + ('-' * repeatLength) + '\n' + output + '\n' + ('-' * repeatLength)
70+
}
71+
}
72+
73+
// Example:
74+
// gradle test -Pdb=MySQL
75+
test {
76+
def selectedDb = project.hasProperty( 'db' )
77+
? project.properties['db']
78+
: 'PostgreSQL'
79+
doFirst {
80+
systemProperty 'db', selectedDb
81+
}
82+
afterSuite { desc, result ->
83+
loggingSummary( selectedDb, result, desc )
84+
}
85+
}
86+
87+
// Rule to recognize calls to testDb<dbName>
88+
// and run the tests on the selected db
89+
// Example:
90+
// gradle testDbMySQL testDbDB2
91+
tasks.addRule( "Pattern testDb<id>" ) { String taskName ->
92+
if ( taskName.startsWith( "testDb" ) ) {
93+
tasks.register( taskName, Test ) {
94+
def dbName = taskName.substring( "testDb".length() )
95+
description = "Run tests for ${dbName}"
96+
97+
// We only want to test this on Postgres
98+
onlyIf { dbName.toLowerCase().startsWith( 'p' ) }
99+
doFirst() {
100+
systemProperty 'db', dbName
101+
}
102+
afterSuite { desc, result ->
103+
loggingSummary( dbName, result, desc )
104+
}
105+
106+
}
107+
}
108+
}
109+
110+
tasks.register( "startVertx", JavaExec ) {
111+
description = "Starts the Vert.x Server app"
112+
classpath = sourceSets.main.runtimeClasspath
113+
mainClass = "org.hibernate.reactive.it.verticle.VertxServer"
114+
}
115+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.it.verticle;
7+
8+
import java.math.BigDecimal;
9+
import java.util.Objects;
10+
11+
import jakarta.persistence.Column;
12+
import jakarta.persistence.Entity;
13+
import jakarta.persistence.Id;
14+
15+
@Entity
16+
public class Product {
17+
18+
@Id
19+
private Long id;
20+
21+
@Column(unique = true)
22+
private String name;
23+
24+
@Column(nullable = false)
25+
private BigDecimal price;
26+
27+
public Product() {
28+
}
29+
30+
public Product(int index) {
31+
this.id = Long.valueOf( index );
32+
this.name = "Product " + index;
33+
this.price = new BigDecimal( index + ".00" );
34+
}
35+
36+
public Long getId() {
37+
return id;
38+
}
39+
40+
public void setId(Long id) {
41+
this.id = id;
42+
}
43+
44+
public String getName() {
45+
return name;
46+
}
47+
48+
public void setName(String name) {
49+
this.name = name;
50+
}
51+
52+
public BigDecimal getPrice() {
53+
return price;
54+
}
55+
56+
public void setPrice(BigDecimal price) {
57+
this.price = price;
58+
}
59+
60+
@Override
61+
public boolean equals(Object o) {
62+
if ( this == o ) {
63+
return true;
64+
}
65+
if ( !( o instanceof Product ) ) {
66+
return false;
67+
}
68+
Product product = (Product) o;
69+
return Objects.equals( name, product.name )
70+
&& Objects.equals( price, product.price );
71+
}
72+
73+
@Override
74+
public int hashCode() {
75+
return Objects.hash( name, price );
76+
}
77+
78+
@Override
79+
public String toString() {
80+
final StringBuilder sb = new StringBuilder( "{" );
81+
sb.append( id );
82+
sb.append( ", " ).append( name );
83+
sb.append( ", " ).append( price );
84+
sb.append( '}' );
85+
return sb.toString();
86+
}
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.it.verticle;
7+
8+
import java.util.List;
9+
import java.util.function.Supplier;
10+
11+
import org.hibernate.reactive.mutiny.Mutiny;
12+
13+
import org.jboss.logging.Logger;
14+
15+
import io.smallrye.mutiny.Uni;
16+
import io.vertx.core.AbstractVerticle;
17+
import io.vertx.core.Future;
18+
import io.vertx.core.Promise;
19+
import io.vertx.core.http.HttpServer;
20+
import io.vertx.ext.web.Router;
21+
import io.vertx.ext.web.RoutingContext;
22+
import io.vertx.ext.web.handler.BodyHandler;
23+
24+
import static io.vertx.core.Future.all;
25+
26+
public class ProductVerticle extends AbstractVerticle {
27+
28+
private static final Logger LOG = Logger.getLogger( ProductVerticle.class );
29+
30+
private final Supplier<Mutiny.SessionFactory> emfSupplier;
31+
private Mutiny.SessionFactory emf;
32+
private HttpServer httpServer;
33+
34+
/**
35+
* The port to use to listen to requests
36+
*/
37+
public static final int HTTP_PORT = 8088;
38+
39+
public ProductVerticle(Supplier<Mutiny.SessionFactory> emfSupplier) {
40+
this.emfSupplier = emfSupplier;
41+
}
42+
43+
private void startHibernate(Promise<Object> p) {
44+
try {
45+
this.emf = emfSupplier.get();
46+
p.complete();
47+
}
48+
catch (Throwable t) {
49+
p.fail( t );
50+
}
51+
}
52+
53+
@Override
54+
public void start(Promise<Void> startPromise) {
55+
final Future<Object> startHibernate = vertx.executeBlocking( this::startHibernate )
56+
.onSuccess( s -> LOG.infof( "✅ Hibernate Reactive is ready" ) );
57+
58+
Router router = Router.router( vertx );
59+
BodyHandler bodyHandler = BodyHandler.create();
60+
router.post().handler( bodyHandler );
61+
62+
router.get( "/products" ).respond( this::listProducts );
63+
router.get( "/products/:id" ).respond( this::getProduct );
64+
router.post( "/products" ).respond( this::createProduct );
65+
66+
this.httpServer = vertx.createHttpServer();
67+
final Future<HttpServer> startHttpServer = httpServer
68+
.requestHandler( router )
69+
.listen( HTTP_PORT )
70+
.onSuccess( v -> LOG.infof( "✅ HTTP server listening on port %s", HTTP_PORT ) );
71+
72+
all( startHibernate, startHttpServer )
73+
.onSuccess( s -> startPromise.complete() )
74+
.onFailure( startPromise::fail );
75+
}
76+
77+
@Override
78+
public void stop(Promise<Void> stopPromise) {
79+
httpServer.close().onComplete( unused -> emf.close() )
80+
.onSuccess( s -> stopPromise.complete() )
81+
.onFailure( stopPromise::fail );
82+
}
83+
84+
private static void logFound(Product product) {
85+
LOG.tracef( "Product found: %s", product );
86+
}
87+
88+
private static void logCreated(Product product) {
89+
LOG.tracef( "Product added: %s", product );
90+
}
91+
92+
private Future<List<Product>> listProducts(RoutingContext ctx) {
93+
return toFuture( emf.withSession( session -> session
94+
.createQuery( "from Product", Product.class ).getResultList()
95+
) );
96+
}
97+
98+
private Future<Product> getProduct(RoutingContext ctx) {
99+
long id = Long.parseLong( ctx.pathParam( "id" ) );
100+
return toFuture( emf.withSession( session -> session.find( Product.class, id ) )
101+
.invoke( ProductVerticle::logFound )
102+
.onItem().ifNull().continueWith( Product::new )
103+
);
104+
}
105+
106+
private Future<Product> createProduct(RoutingContext ctx) {
107+
Product product = ctx.body().asPojo( Product.class );
108+
return toFuture( emf.withTransaction( session -> session.persist( product ) )
109+
.map( v -> product )
110+
.invoke( ProductVerticle::logCreated )
111+
.replaceWith( product )
112+
);
113+
}
114+
115+
private static <U> Future<U> toFuture(Uni<U> uni) {
116+
return Future.fromCompletionStage( uni.convert().toCompletionStage() );
117+
}
118+
}

0 commit comments

Comments
 (0)