From c283f9a0f8d0d5b3be0164cc7570e5c24b4b7677 Mon Sep 17 00:00:00 2001 From: blafond Date: Thu, 13 May 2021 22:09:58 -0500 Subject: [PATCH 1/3] single executable jbang-based junit test class --- tooling/jbang/SampleIssueTest.java | 177 +++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100755 tooling/jbang/SampleIssueTest.java diff --git a/tooling/jbang/SampleIssueTest.java b/tooling/jbang/SampleIssueTest.java new file mode 100755 index 000000000..3b6d60215 --- /dev/null +++ b/tooling/jbang/SampleIssueTest.java @@ -0,0 +1,177 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS org.hibernate:hibernate-core:5.4.31.Final +//DEPS junit:junit:4.12 +//DEPS javax.persistence:javax.persistence-api:2.2 +//DEPS org.hibernate.reactive:hibernate-reactive-core:1.0.0.CR4 +//DEPS org.assertj:assertj-core:3.13.2 +//DEPS io.vertx:vertx-pg-client:4.0.3 +//DEPS io.vertx:vertx-db2-client:4.0.3 +//DEPS io.vertx:vertx-mysql-client:4.0.3 +//DEPS io.vertx:vertx-sql-client:4.0.3 +//DEPS io.vertx:vertx-unit:4.0.3 +// +// + +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; +import org.hibernate.reactive.provider.Settings; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; + +import io.vertx.ext.unit.Async; +import io.vertx.ext.unit.TestContext; +import io.vertx.ext.unit.junit.VertxUnitRunner; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This test class provides a working example of setting up a simple hibernate-reactive test compatible with + * hibernate-reactive junit tests. + * See hibernate reactive junit tests + * + * Developers can copy/paste/edit this test to capture test failures or use-cases and attach an executable class to + * issues or other correspondence. + * + * This test can be executed using the jbang CLI with the command `jbang SampleIssueTest.java` + * See jbang" + * jbang will compile and execute a jar generated based on dependencies defined above in the + * file header (i.e. //DEPS org.hibernate:hibernate-core:5.4.31.Final ) + * + * > Customize your JDBC connection properties + * > Define your specific entity classes and relationships + * > Replicate your issue and or exception + * > attach the resulting file or a URL link to it + * + * Note this test utilizes a VertxUnitRunner class which provides the hooks to the reactive + * framework for transaction control. + * + */ +@RunWith(VertxUnitRunner.class) +public class SampleIssueTest { + + private Mutiny.SessionFactory sessionFactory; + + /** + * Define the configuration parameter values for your use-case + */ + private Configuration createConfiguration() { + Configuration configuration = new Configuration(); + + // Use the correct JDBC url + configuration.setProperty( Settings.URL, "jdbc:postgresql://localhost:5432/hreact" ); + + // Credentials + configuration.setProperty( Settings.USER, "hreact"); + configuration.setProperty( Settings.PASS, "hreact"); + + // Schema generation + configuration.setProperty( Settings.HBM2DDL_AUTO, "create" ); + + // Add additional entities here + configuration.addAnnotatedClass(MyEntity.class); + + // Query logging + configuration.setProperty( Settings.SHOW_SQL, "true" ); + configuration.setProperty( Settings.HIGHLIGHT_SQL, "true" ); + return configuration; + } + + @Before + public void createSessionFactory() { + Configuration configuration = createConfiguration(); + StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder() + .applySettings( configuration.getProperties() ); + StandardServiceRegistry registry = builder.build(); + + sessionFactory = configuration.buildSessionFactory( registry ) + .unwrap( Mutiny.SessionFactory.class ); + } + + @Test + public void firstTest(TestContext context) { + Async async = context.async(); + + MyEntity entity = new MyEntity( "first entity", 1 ); + sessionFactory + .withTransaction( (session, tx) -> session.persist( entity ) ) + .chain( () -> sessionFactory + .withSession( session -> session + .find( MyEntity.class, entity.getId() ) + .invoke( foundEntity -> assertThat( foundEntity.getName() ).isEqualTo( entity.getName() ) ) ) ) + .subscribe().with( res -> async.complete(), throwable -> context.fail( throwable ) + ); + } + + @After + public void closeFactory() { + if ( sessionFactory != null ) { + sessionFactory.close(); + } + } + + /* + * Define your hibernate entity classes and relationships. + * + * This initial test includes a single entity but you can create joined and/or embedded entities too + * + * Be sure to add all annotated entity classes in the createConfiguration() method above + * example: configuration.addAnnotatedClass(MyOtherEntity.class); + */ + @Entity(name = "MyEntity") + public static class MyEntity { + @Id + public Integer id; + + public String name; + + public MyEntity() {} + + public MyEntity(String name, Integer id) { + this.name = name; + this.id = id; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return "MyEntity" + + "\n\t id = " + id + + "\n\t name = " + name; + } + } + + public static void main(String[] args) { + Result result = JUnitCore.runClasses( SampleIssueTest.class); + + for ( Failure failure : result.getFailures()) { + System.out.println(failure.toString()); + } + + System.out.println(" All unit tests succeeded: " + result.wasSuccessful()); + } +} From ac958c46d1c3972cbcfd25852afce648be4d36b7 Mon Sep 17 00:00:00 2001 From: blafond Date: Wed, 19 May 2021 09:01:40 -0500 Subject: [PATCH 2/3] updated junit test and added java test --- tooling/jbang/Example.java | 210 +++++++++++++++++++++++++++++ tooling/jbang/SampleIssueTest.java | 18 ++- 2 files changed, 224 insertions(+), 4 deletions(-) create mode 100644 tooling/jbang/Example.java diff --git a/tooling/jbang/Example.java b/tooling/jbang/Example.java new file mode 100644 index 000000000..c3c807ab2 --- /dev/null +++ b/tooling/jbang/Example.java @@ -0,0 +1,210 @@ +/* Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS org.hibernate:hibernate-core:5.4.31.Final +//DEPS junit:junit:4.12 +//DEPS javax.persistence:javax.persistence-api:2.2 +//DEPS org.hibernate.reactive:hibernate-reactive-core:1.0.0.CR4 +//DEPS org.hibernate.validator:hibernate-validator:6.1.5.Final +//DEPS org.assertj:assertj-core:3.13.2 +//DEPS io.vertx:vertx-pg-client:4.0.3 +//DEPS io.vertx:vertx-db2-client:4.0.3 +//DEPS io.vertx:vertx-mysql-client:4.0.3 +//DEPS io.vertx:vertx-sql-client:4.0.3 +//DEPS io.vertx:vertx-unit:4.0.3 +// +// + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Past; +import javax.validation.constraints.Size; + +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.reactive.mutiny.Mutiny; +import org.hibernate.reactive.provider.ReactiveServiceRegistryBuilder; +import org.hibernate.reactive.provider.Settings; + +import static java.lang.System.out; +import static java.time.Month.JANUARY; +import static java.time.Month.JUNE; +import static java.time.Month.MAY; +import static javax.persistence.CascadeType.PERSIST; +import static javax.persistence.FetchType.LAZY; + +public class Example { + + /** + * Define the configuration parameter values for your use-case + */ + private static Configuration createConfiguration() { + Configuration configuration = new Configuration(); + + configuration.setProperty( Settings.URL, "jdbc:postgresql://localhost:5432/hreact" ); + + // ===== OTHER Test DBs ======================================================================= + // MYSQL: jdbc:mysql://localhost:3306/hreact?user=hreact&password=hreact&serverTimezone=UTC + // DB2: jdbc:db2://localhost:50000/hreact:user=hreact;password=hreact; + // CockroachDB jdbc:cockroachdb://localhost:26257/postgres?sslmode=disable&user=root + // NOTE: CockroachDB does not need password and requires //DEPS io.vertx:vertx-pg-client:4.0.3 + // ============================================================================================== + + configuration.setProperty( Settings.USER, "hreact" ); + configuration.setProperty( Settings.PASS, "hreact" ); + + configuration.setProperty( Settings.HBM2DDL_AUTO, "create" ); + + configuration.addAnnotatedClass( Author.class ); + configuration.addAnnotatedClass( Book.class ); + + configuration.setProperty( Settings.SHOW_SQL, "true" ); + configuration.setProperty( Settings.HIGHLIGHT_SQL, "true" ); + configuration.setProperty( Settings.FORMAT_SQL, "true" ); + return configuration; + } + + public static Mutiny.SessionFactory createSessionFactory() { + Configuration configuration = createConfiguration(); + StandardServiceRegistryBuilder builder = new ReactiveServiceRegistryBuilder() + .applySettings( configuration.getProperties() ); + StandardServiceRegistry registry = builder.build(); + + return configuration.buildSessionFactory( registry ) + .unwrap( Mutiny.SessionFactory.class ); + } + + public static void main(String[] args) { + out.println( "== Mutiny API Example ==" ); + + // define some test data + Author author1 = new Author( "Iain M. Banks" ); + Author author2 = new Author( "Neal Stephenson" ); + Book book1 = new Book( "1-85723-235-6", "Feersum Endjinn", author1, LocalDate.of( 1994, JANUARY, 1 ) ); + Book book2 = new Book( "0-380-97346-4", "Cryptonomicon", author2, LocalDate.of( 1999, MAY, 1 ) ); + Book book3 = new Book( "0-553-08853-X", "Snow Crash", author2, LocalDate.of( 1992, JUNE, 1 ) ); + author1.getBooks().add( book1 ); + author2.getBooks().add( book2 ); + author2.getBooks().add( book3 ); + + try (Mutiny.SessionFactory factory = createSessionFactory()) { + // obtain a reactive session + factory.withTransaction( + // persist the Authors with their Books in a transaction + (session, tx) -> session.persistAll( author1, author2 ) + ) + // wait for it to finish + .await().indefinitely(); + + factory.withSession( + // retrieve a Book + session -> session.find( Book.class, book1.getId() ) + // print its title + .invoke( book -> out.println( book.getTitle() + " is a great book!" ) ) + ) + .await().indefinitely(); + } + } + + @Entity + @Table(name = "authors") + static class Author { + @Id + @GeneratedValue + private Integer id; + + @NotNull + @Size(max = 100) + private String name; + + @OneToMany(mappedBy = "author", cascade = PERSIST) + private List books = new ArrayList<>(); + + Author(String name) { + this.name = name; + } + + Author() { + } + + Integer getId() { + return id; + } + + String getName() { + return name; + } + + List getBooks() { + return books; + } + } + + @Entity + @Table(name = "books") + static class Book { + @Id + @GeneratedValue + private Integer id; + + @Size(min = 13, max = 13) + private String isbn; + + @NotNull + @Size(max = 100) + private String title; + + @Basic(fetch = LAZY) + @NotNull + @Past + private LocalDate published; + + @NotNull + @ManyToOne(fetch = LAZY) + private Author author; + + Book(String isbn, String title, Author author, LocalDate published) { + this.title = title; + this.isbn = isbn; + this.author = author; + this.published = published; + } + + Book() { + } + + Integer getId() { + return id; + } + + String getIsbn() { + return isbn; + } + + String getTitle() { + return title; + } + + Author getAuthor() { + return author; + } + + LocalDate getPublished() { + return published; + } + } +} diff --git a/tooling/jbang/SampleIssueTest.java b/tooling/jbang/SampleIssueTest.java index 3b6d60215..15e00146d 100755 --- a/tooling/jbang/SampleIssueTest.java +++ b/tooling/jbang/SampleIssueTest.java @@ -78,6 +78,13 @@ private Configuration createConfiguration() { // Use the correct JDBC url configuration.setProperty( Settings.URL, "jdbc:postgresql://localhost:5432/hreact" ); + // ===== OTHER Test DBs ======================================================================= + // MYSQL: jdbc:mysql://localhost:3306/hreact?user=hreact&password=hreact&serverTimezone=UTC + // DB2: jdbc:db2://localhost:50000/hreact:user=hreact;password=hreact; + // CockroachDB jdbc:cockroachdb://localhost:26257/postgres?sslmode=disable&user=root + // NOTE: CockroachDB does not need password and requires //DEPS io.vertx:vertx-pg-client:4.0.3 + // ============================================================================================== + // Credentials configuration.setProperty( Settings.USER, "hreact"); configuration.setProperty( Settings.PASS, "hreact"); @@ -91,6 +98,7 @@ private Configuration createConfiguration() { // Query logging configuration.setProperty( Settings.SHOW_SQL, "true" ); configuration.setProperty( Settings.HIGHLIGHT_SQL, "true" ); + configuration.setProperty( Settings.FORMAT_SQL, "true" ); return configuration; } @@ -168,10 +176,12 @@ public String toString() { public static void main(String[] args) { Result result = JUnitCore.runClasses( SampleIssueTest.class); - for ( Failure failure : result.getFailures()) { - System.out.println(failure.toString()); + if( result.wasSuccessful() ) { + System.out.println( "All unit tests passed"); + } else { + for ( Failure failure : result.getFailures() ) { + System.out.println( "TEST FAILURE: " + failure.toString() ); + } } - - System.out.println(" All unit tests succeeded: " + result.wasSuccessful()); } } From 0944e9f028c3789e256ac5b0b1d3455cb6b7bfe9 Mon Sep 17 00:00:00 2001 From: blafond Date: Wed, 19 May 2021 09:26:05 -0500 Subject: [PATCH 3/3] Added instructions --- tooling/jbang/README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 tooling/jbang/README.md diff --git a/tooling/jbang/README.md b/tooling/jbang/README.md new file mode 100644 index 000000000..f3df2b55f --- /dev/null +++ b/tooling/jbang/README.md @@ -0,0 +1,25 @@ +Rapid Reactive testing with JBang + +If you want to see them in action, here's how it works: + +1. [Download JBang](https://www.jbang.dev/download) +2. Start the default Postgres we use for testing (you can replace podman with docker): + ``` + podman run --rm --name HibernateTestingPGSQL \ + -e POSTGRES_USER=hreact -e POSTGRES_PASSWORD=hreact -e POSTGRES_DB=hreact \ + -p 5432:5432 postgres:13.2 + ``` +3. JBang it: + ``` + jbang https://github.com/hibernate/hibernate-reactive/tree/main/tooling/jbang/Example.java + ``` + or + ``` + jbang https://github.com/hibernate/hibernate-reactive/tree/main/tooling/jbang/SampleIssueTest.java + ``` +4. (Optional) If you want to edit the class with Idea + ``` + jbang edit --open=idea https://github.com/hibernate/hibernate-reactive/tree/main/tooling/jbang/SampleIssueTest.java + ``` + +_NOTE: For testing with other DB's see [podman.md](https://github.com/hibernate/hibernate-reactive/blob/main/podman.md)_. \ No newline at end of file