Skip to content

Commit

Permalink
Fail on startup when Flyway/Liquibase for inactive datasources are in…
Browse files Browse the repository at this point in the history
…jected into user beans

By reimplementing Flyway's/Liquibase's inactive/active handling and eager
startup through Arc's native features, which is better integrated and gives
us this behavior.
  • Loading branch information
yrodiere committed Sep 30, 2024
1 parent ee4f40a commit 94e82a0
Show file tree
Hide file tree
Showing 29 changed files with 318 additions and 229 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ void createBeans(FlywayRecorder recorder,
.addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainerProducer.class)))
.addInjectionPoint(ClassType.create(DotName.createSimple(DataSource.class)),
AgroalDataSourceBuildUtil.qualifier(dataSourceName))
.startup()
.isActive(recorder.flywayActiveSupplier(dataSourceName))
.createWith(recorder.flywayContainerFunction(dataSourceName, hasMigrations, createPossible));

AnnotationInstance flywayContainerQualifier;
Expand Down Expand Up @@ -249,6 +251,8 @@ void createBeans(FlywayRecorder recorder,
.setRuntimeInit()
.unremovable()
.addInjectionPoint(ClassType.create(DotName.createSimple(FlywayContainer.class)), flywayContainerQualifier)
.startup()
.isActive(recorder.flywayActiveSupplier(dataSourceName))
.createWith(recorder.flywayFunction(dataSourceName));

if (DataSourceUtil.isDefault(dataSourceName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.inject.CreationException;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

Expand All @@ -11,6 +10,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.test.QuarkusUnitTest;

public class FlywayExtensionConfigActiveFalseDefaultDatasourceDynamicInjectionTest {
Expand All @@ -26,10 +26,9 @@ public class FlywayExtensionConfigActiveFalseDefaultDatasourceDynamicInjectionTe
@DisplayName("If the default datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(flyway::get)
.isInstanceOf(CreationException.class)
.cause()
.hasMessageContainingAll("Unable to find datasource '<default>' for Flyway",
"Datasource '<default>' was deactivated through configuration properties.",
.isInstanceOf(InactiveBeanException.class)
.hasMessageContainingAll(
"Flyway for datasource '<default>' was deactivated automatically because this datasource was deactivated.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
package io.quarkus.flyway.test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.test.QuarkusUnitTest;

public class FlywayExtensionConfigActiveFalseDefaultDatasourceStaticInjectionTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.overrideConfigKey("quarkus.datasource.active", "false");
.overrideConfigKey("quarkus.datasource.active", "false")
.assertException(e -> assertThat(e)
// Can't use isInstanceOf due to weird classloading in tests
.satisfies(t -> assertThat(t.getClass().getName()).isEqualTo(InactiveBeanException.class.getName()))
.hasMessageContainingAll(
"Flyway for datasource '<default>' was deactivated automatically because this datasource was deactivated.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
"Refer to https://quarkus.io/guides/datasource for guidance.",
"This bean is injected into",
MyBean.class.getName() + "#flyway"));

@Inject
MyBean myBean;

@Test
@DisplayName("If the default datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(myBean::useFlyway)
.cause()
.hasMessageContainingAll("Unable to find datasource '<default>' for Flyway",
"Datasource '<default>' was deactivated through configuration properties.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.active'"
+ " to 'true' and configure datasource '<default>'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
public void test() {
Assertions.fail("Startup should have failed");
}

@ApplicationScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.inject.CreationException;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

Expand All @@ -11,6 +10,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.test.QuarkusUnitTest;

Expand All @@ -37,10 +37,9 @@ public class FlywayExtensionConfigActiveFalseNamedDataSourceDynamicInjectionTest
@DisplayName("If a named datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(flyway::get)
.isInstanceOf(CreationException.class)
.cause()
.hasMessageContainingAll("Unable to find datasource 'users' for Flyway",
"Datasource 'users' was deactivated through configuration properties.",
.isInstanceOf(InactiveBeanException.class)
.hasMessageContainingAll(
"Flyway for datasource 'users' was deactivated automatically because this datasource was deactivated.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package io.quarkus.flyway.test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.test.QuarkusUnitTest;

Expand All @@ -26,22 +27,25 @@ public class FlywayExtensionConfigActiveFalseNamedDataSourceStaticInjectionTest
.overrideConfigKey("quarkus.datasource.username", "sa")
.overrideConfigKey("quarkus.datasource.password", "sa")
.overrideConfigKey("quarkus.datasource.jdbc.url",
"jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1");
"jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1")
.assertException(e -> assertThat(e)
// Can't use isInstanceOf due to weird classloading in tests
.satisfies(t -> assertThat(t.getClass().getName()).isEqualTo(InactiveBeanException.class.getName()))
.hasMessageContainingAll(
"Flyway for datasource 'users' was deactivated automatically because this datasource was deactivated.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
"Refer to https://quarkus.io/guides/datasource for guidance.",
"This bean is injected into",
MyBean.class.getName() + "#flyway"));

@Inject
MyBean myBean;

@Test
@DisplayName("If a named datasource is deactivated, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(myBean::useFlyway)
.cause()
.hasMessageContainingAll("Unable to find datasource 'users' for Flyway",
"Datasource 'users' was deactivated through configuration properties.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".active'"
+ " to 'true' and configure datasource 'users'",
"Refer to https://quarkus.io/guides/datasource for guidance.");
public void test() {
Assertions.fail("Startup should have failed");
}

@ApplicationScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.inject.CreationException;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

Expand All @@ -11,6 +10,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.test.QuarkusUnitTest;

public class FlywayExtensionConfigUrlMissingDefaultDatasourceDynamicInjectionTest {
Expand All @@ -27,9 +27,9 @@ public class FlywayExtensionConfigUrlMissingDefaultDatasourceDynamicInjectionTes
@DisplayName("If the URL is missing for the default datasource, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(flyway::get)
.isInstanceOf(CreationException.class)
.cause()
.hasMessageContainingAll("Unable to find datasource '<default>' for Flyway",
.isInstanceOf(InactiveBeanException.class)
.hasMessageContainingAll(
"Flyway for datasource '<default>' was deactivated automatically because this datasource was deactivated",
"Datasource '<default>' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.jdbc.url'.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
package io.quarkus.flyway.test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.test.QuarkusUnitTest;

public class FlywayExtensionConfigUrlMissingDefaultDatasourceStaticInjectionTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
// The URL won't be missing if dev services are enabled
.overrideConfigKey("quarkus.devservices.enabled", "false");
.overrideConfigKey("quarkus.devservices.enabled", "false")
.assertException(e -> assertThat(e)
// Can't use isInstanceOf due to weird classloading in tests
.satisfies(t -> assertThat(t.getClass().getName()).isEqualTo(InactiveBeanException.class.getName()))
.hasMessageContainingAll(
"Flyway for datasource '<default>' was deactivated automatically because this datasource was deactivated",
"Datasource '<default>' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.jdbc.url'.",
"Refer to https://quarkus.io/guides/datasource for guidance.",
"This bean is injected into",
MyBean.class.getName() + "#flyway"));

@Inject
MyBean myBean;

@Test
@DisplayName("If the URL is missing for the default datasource, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(() -> myBean.useFlyway())
.cause()
.hasMessageContainingAll("Unable to find datasource '<default>' for Flyway",
"Datasource '<default>' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.jdbc.url'.",
"Refer to https://quarkus.io/guides/datasource for guidance.");
public void test() {
Assertions.fail("Startup should have failed");
}

@ApplicationScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static org.assertj.core.api.Assertions.assertThatThrownBy;

import jakarta.enterprise.inject.CreationException;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;

Expand All @@ -11,6 +10,7 @@
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.test.QuarkusUnitTest;

Expand Down Expand Up @@ -38,9 +38,9 @@ public class FlywayExtensionConfigUrlMissingNamedDataSourceDynamicInjectionTest
@DisplayName("If the URL is missing for a named datasource, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(flyway::get)
.isInstanceOf(CreationException.class)
.cause()
.hasMessageContainingAll("Unable to find datasource 'users' for Flyway",
.isInstanceOf(InactiveBeanException.class)
.hasMessageContainingAll(
"Flyway for datasource 'users' was deactivated automatically because this datasource was deactivated",
"Datasource 'users' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".jdbc.url'.",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
package io.quarkus.flyway.test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.assertThat;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;

import org.assertj.core.api.Assertions;
import org.flywaydb.core.Flyway;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.arc.InactiveBeanException;
import io.quarkus.flyway.FlywayDataSource;
import io.quarkus.test.QuarkusUnitTest;

Expand All @@ -27,21 +28,24 @@ public class FlywayExtensionConfigUrlMissingNamedDataSourceStaticInjectionTest {
.overrideConfigKey("quarkus.datasource.username", "sa")
.overrideConfigKey("quarkus.datasource.password", "sa")
.overrideConfigKey("quarkus.datasource.jdbc.url",
"jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1");
"jdbc:h2:tcp://localhost/mem:test-quarkus-migrate-at-start;DB_CLOSE_DELAY=-1")
.assertException(e -> assertThat(e)
// Can't use isInstanceOf due to weird classloading in tests
.satisfies(t -> assertThat(t.getClass().getName()).isEqualTo(InactiveBeanException.class.getName()))
.hasMessageContainingAll(
"Flyway for datasource 'users' was deactivated automatically because this datasource was deactivated.",
"Datasource 'users' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".jdbc.url'.",
"This bean is injected into",
MyBean.class.getName() + "#flyway"));

@Inject
MyBean myBean;

@Test
@DisplayName("If the URL is missing for a named datasource, the application should boot, but Flyway should be deactivated for that datasource")
public void testBootSucceedsButFlywayDeactivated() {
assertThatThrownBy(() -> myBean.useFlyway())
.cause()
.hasMessageContainingAll("Unable to find datasource 'users' for Flyway",
"Datasource 'users' was deactivated automatically because its URL is not set.",
"To avoid this exception while keeping the bean inactive", // Message from Arc with generic hints
"To activate the datasource, set configuration property 'quarkus.datasource.\"users\".jdbc.url'.",
"Refer to https://quarkus.io/guides/datasource for guidance.");
public void test() {
Assertions.fail("Startup should have failed");
}

@ApplicationScoped
Expand Down
Loading

0 comments on commit 94e82a0

Please sign in to comment.