Skip to content

Commit 816fc7b

Browse files
committed
refactor(springboot-test-support): added @FlamingockSpringBootTest annotation
1 parent 0bc36b3 commit 816fc7b

File tree

5 files changed

+131
-21
lines changed

5 files changed

+131
-21
lines changed

platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/FlamingockAutoConfiguration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,15 @@ public class FlamingockAutoConfiguration {
4848
@ConditionalOnExpression("'${flamingock.runner-type:ApplicationRunner}'.toLowerCase().equals('applicationrunner')")
4949
public ApplicationRunner applicationRunner(RunnerBuilder runnerBuilder,
5050
@Value("${flamingock.autorun:true}") boolean autoRun) {
51-
return SpringbootUtil.toApplicationRunner(runnerBuilder.build(), autoRun);
51+
return SpringbootUtil.toApplicationRunner(runnerBuilder, autoRun);
5252
}
5353

5454
@Bean("flamingock-runner")
5555
@Profile(Constants.NON_CLI_PROFILE)
5656
@ConditionalOnExpression("'${flamingock.runner-type:null}'.toLowerCase().equals('initializingbean')")
5757
public InitializingBean initializingBeanRunner(RunnerBuilder runnerBuilder,
5858
@Value("${flamingock.autorun:true}") boolean autoRun) {
59-
return SpringbootUtil.toInitializingBean(runnerBuilder.build(), autoRun);
59+
return SpringbootUtil.toInitializingBean(runnerBuilder, autoRun);
6060
}
6161

6262
@Bean("flamingock-builder")

platform-plugins/flamingock-springboot-integration/src/main/java/io/flamingock/springboot/SpringbootUtil.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.flamingock.springboot;
1717

1818
import io.flamingock.internal.core.runner.Runner;
19+
import io.flamingock.internal.core.runner.RunnerBuilder;
1920
import io.flamingock.internal.util.log.FlamingockLoggerFactory;
2021
import org.slf4j.Logger;
2122
import org.springframework.beans.factory.InitializingBean;
@@ -28,16 +29,17 @@ public final class SpringbootUtil {
2829
private SpringbootUtil() {
2930
}
3031

31-
public static InitializingBean toInitializingBean(Runner runner, boolean autoRun) {
32-
return () -> runIfApply(runner, autoRun);
32+
public static InitializingBean toInitializingBean(RunnerBuilder runnerBuilder, boolean autoRun) {
33+
return () -> runIfApply(runnerBuilder, autoRun);
3334
}
3435

35-
public static ApplicationRunner toApplicationRunner(Runner runner, boolean autoRun) {
36-
return args -> runIfApply(runner, autoRun);
36+
public static ApplicationRunner toApplicationRunner(RunnerBuilder runnerBuilder, boolean autoRun) {
37+
return args -> runIfApply(runnerBuilder, autoRun);
3738
}
3839

39-
private static void runIfApply(Runner runner, boolean autoRun) {
40+
private static void runIfApply(RunnerBuilder runnerBuilder, boolean autoRun) {
4041
if(autoRun) {
42+
Runner runner = runnerBuilder.build();
4143
runner.run();
4244
} else {
4345
logger.info(

platform-plugins/flamingock-springboot-test-support/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ dependencies {
1010
compileOnly("org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}")
1111
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}")
1212

13+
// Required for @FlamingockSpringBootTest annotation
14+
implementation("org.springframework.boot:spring-boot-test:${springBootVersion}")
15+
implementation("org.springframework:spring-test:${springFrameworkVersion}")
16+
1317

1418
testImplementation(platform("org.springframework.boot:spring-boot-dependencies:${springBootVersion}"))
1519

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2025 Flamingock (https://www.flamingock.io)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.flamingock.springboot.testsupport;
17+
18+
import org.springframework.boot.test.context.SpringBootTest;
19+
import org.springframework.core.annotation.AliasFor;
20+
21+
import java.lang.annotation.Documented;
22+
import java.lang.annotation.ElementType;
23+
import java.lang.annotation.Inherited;
24+
import java.lang.annotation.Retention;
25+
import java.lang.annotation.RetentionPolicy;
26+
import java.lang.annotation.Target;
27+
28+
/**
29+
* Annotation for Flamingock integration tests with Spring Boot.
30+
*
31+
* <p>This annotation configures a Spring Boot test context with Flamingock's
32+
* auto-run disabled ({@code flamingock.autorun=false}), allowing tests to
33+
* manually control when Flamingock executes.</p>
34+
*
35+
* <p><strong>Usage:</strong></p>
36+
* <pre>{@code
37+
* @ExtendWith(SpringExtension.class) // Required for Spring Boot < 2.1
38+
* @FlamingockSpringBootTest(classes = {MyApplication.class, TestConfig.class})
39+
* class MyFlamingockTest {
40+
*
41+
* @Autowired
42+
* private AbstractChangeRunnerBuilder<?, ?> flamingockBuilder;
43+
*
44+
* @Test
45+
* void testMigration() {
46+
* // Setup preconditions...
47+
*
48+
* // Execute Flamingock manually
49+
* flamingockBuilder.build().run();
50+
*
51+
* // Verify results...
52+
* }
53+
* }
54+
* }</pre>
55+
*
56+
* <p><strong>Note:</strong> For Spring Boot 2.0.x, you must add
57+
* {@code @ExtendWith(SpringExtension.class)} to your test class.
58+
* Spring Boot 2.1+ does not require this.</p>
59+
*
60+
* @see SpringBootTest
61+
* @since 1.0
62+
*/
63+
@Target(ElementType.TYPE)
64+
@Retention(RetentionPolicy.RUNTIME)
65+
@Documented
66+
@Inherited
67+
@SpringBootTest(properties = "flamingock.autorun=false")
68+
public @interface FlamingockSpringBootTest {
69+
70+
/**
71+
* Alias for {@link SpringBootTest#classes()}.
72+
* The component classes to use for loading an ApplicationContext.
73+
*
74+
* @return the component classes
75+
* @see SpringBootTest#classes()
76+
*/
77+
@AliasFor(annotation = SpringBootTest.class, attribute = "classes")
78+
Class<?>[] classes() default {};
79+
80+
/**
81+
* Alias for {@link SpringBootTest#webEnvironment()}.
82+
* The type of web environment to create when applicable.
83+
*
84+
* @return the web environment mode
85+
* @see SpringBootTest#webEnvironment()
86+
*/
87+
@AliasFor(annotation = SpringBootTest.class, attribute = "webEnvironment")
88+
SpringBootTest.WebEnvironment webEnvironment() default SpringBootTest.WebEnvironment.MOCK;
89+
}

platform-plugins/flamingock-springboot-test-support/src/test/java/io/flamingock/springboot/testsupport/SpringBootFlamingockIntegrationTest.java

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
package io.flamingock.springboot.testsupport;
1717

1818
import io.flamingock.internal.common.core.audit.AuditEntry;
19+
import io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder;
1920
import io.flamingock.internal.core.store.CommunityAuditStore;
2021
import org.junit.jupiter.api.Test;
2122
import org.junit.jupiter.api.extension.ExtendWith;
2223
import org.springframework.beans.factory.annotation.Autowired;
23-
import org.springframework.boot.test.context.SpringBootTest;
2424
import org.springframework.context.ApplicationContext;
2525
import org.springframework.test.context.junit.jupiter.SpringExtension;
2626

@@ -30,12 +30,17 @@
3030
import static org.assertj.core.api.Assertions.assertThat;
3131

3232
/**
33-
* Integration test that verifies Flamingock Spring Boot integration works correctly.
34-
* This test runs a real Spring Boot application with @EnableFlamingock and verifies
35-
* that the change is executed and recorded in the audit store.
33+
* Integration test that verifies @FlamingockSpringBootTest annotation works correctly.
34+
*
35+
* <p>This test demonstrates that:</p>
36+
* <ul>
37+
* <li>Flamingock does NOT auto-run on startup (flamingock.autorun=false)</li>
38+
* <li>The builder can be injected and executed manually</li>
39+
* <li>Changes are applied when executed manually</li>
40+
* </ul>
3641
*/
37-
@ExtendWith(SpringExtension.class)
38-
@SpringBootTest(classes = {TestApplication.class, FlamingockTestConfiguration.class})
42+
@ExtendWith(SpringExtension.class) // Required for Spring Boot 2.0.x
43+
@FlamingockSpringBootTest(classes = {TestApplication.class, FlamingockTestConfiguration.class})
3944
class SpringBootFlamingockIntegrationTest {
4045

4146
@Autowired
@@ -44,31 +49,41 @@ class SpringBootFlamingockIntegrationTest {
4449
@Autowired
4550
private CommunityAuditStore auditStore;
4651

52+
@Autowired
53+
private AbstractChangeRunnerBuilder<?, ?> flamingockBuilder;
54+
4755
@Test
4856
void contextLoads() {
4957
assertThat(context).isNotNull();
5058
}
5159

5260
@Test
53-
void flamingockRunnerBeanExists() {
54-
assertThat(context.containsBean("flamingock-runner")).isTrue();
61+
void flamingockBuilderBeanExists() {
62+
assertThat(context.containsBean("flamingock-builder")).isTrue();
5563
}
5664

5765
@Test
58-
void flamingockBuilderBeanExists() {
59-
assertThat(context.containsBean("flamingock-builder")).isTrue();
66+
void flamingockRunnerBeanExists() {
67+
assertThat(context.containsBean("flamingock-runner")).isTrue();
6068
}
6169

6270
@Test
63-
void changeWasApplied() {
64-
// Verify that the change was executed by checking the audit store
65-
List<AuditEntry> entries = auditStore.getPersistence()
71+
void manualExecutionWorks() {
72+
// This test implicitly verifies that Flamingock did NOT auto-run:
73+
// If it had auto-run, the change would already be applied and we'd get a different result
74+
// Execute Flamingock manually
75+
flamingockBuilder.build().run();
76+
77+
// Verify that the change was applied
78+
List<AuditEntry> appliedEntries = auditStore.getPersistence()
6679
.getAuditHistory()
6780
.stream()
6881
.filter(e -> "simple-test-change".equals(e.getTaskId()))
6982
.filter(e -> AuditEntry.Status.APPLIED.equals(e.getState()))
7083
.collect(Collectors.toList());
7184

72-
assertThat(entries).hasSize(1);
85+
assertThat(appliedEntries)
86+
.as("Change should be applied after manual execution")
87+
.hasSize(1);
7388
}
7489
}

0 commit comments

Comments
 (0)