Skip to content

Commit a399b8b

Browse files
committed
feature: flamingock test support for springboot
1 parent 816fc7b commit a399b8b

File tree

7 files changed

+122
-45
lines changed

7 files changed

+122
-45
lines changed

core/flamingock-test-support/src/main/java/io/flamingock/support/FlamingockTestSupport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ private FlamingockTestSupport() {
8484
* @return the {@link GivenStage} for defining initial audit state preconditions
8585
* @throws NullPointerException if {@code builder} is {@code null}
8686
*/
87-
public static GivenStage given(AbstractChangeRunnerBuilder<?, ?> builder) {
87+
public static GivenStage givenBuilder(AbstractChangeRunnerBuilder<?, ?> builder) {
8888
if (builder == null) {
8989
throw new NullPointerException("builder must not be null");
9090
}

core/flamingock-test-support/src/main/java/io/flamingock/support/stages/GivenStage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
* // ... assertions
4848
* }</pre>
4949
*
50-
* @see FlamingockTestSupport#given(io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder)
50+
* @see FlamingockTestSupport#givenBuilder(io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder)
5151
* @see WhenStage
5252
* @see AuditEntryDefinition
5353
*/

core/flamingock-test-support/src/test/java/io/flamingock/support/integration/FlamingockTestSupportIntegrationTest.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ void shouldExecuteNonTransactionalChange() {
5050
NonTransactionalTargetSystem targetSystem = new NonTransactionalTargetSystem("kafka");
5151

5252
FlamingockTestSupport
53-
.given(testKit.createBuilder().addTargetSystem(targetSystem))
53+
.givenBuilder(testKit.createBuilder().addTargetSystem(targetSystem))
5454
.whenRun()
5555
.thenExpectAuditSequenceStrict(
5656
APPLIED(_001__SimpleNonTransactionalChange.class)
@@ -75,7 +75,7 @@ void shouldVerifyMultipleChangesInSequence() {
7575
);
7676

7777
FlamingockTestSupport
78-
.given(testKit.createBuilder()
78+
.givenBuilder(testKit.createBuilder()
7979
.addTargetSystem(new NonTransactionalTargetSystem("okta"))
8080
.addTargetSystem(new NonTransactionalTargetSystem("elasticsearch"))
8181
.addTargetSystem(new NonTransactionalTargetSystem("s3")))
@@ -101,7 +101,7 @@ void shouldVerifyFailingTransactionalChangeTriggersRollback() {
101101
);
102102

103103
FlamingockTestSupport
104-
.given(testKit.createBuilder()
104+
.givenBuilder(testKit.createBuilder()
105105
.addTargetSystem(new NonTransactionalTargetSystem("salesforce"))
106106
.addTargetSystem(new NonTransactionalTargetSystem("okta"))
107107
.addTargetSystem(new NonTransactionalTargetSystem("elasticsearch"))
@@ -129,7 +129,7 @@ void shouldVerifyAlreadyAppliedChangesAreSkipped() {
129129
);
130130

131131
FlamingockTestSupport
132-
.given(testKit.createBuilder()
132+
.givenBuilder(testKit.createBuilder()
133133
.addTargetSystem(new NonTransactionalTargetSystem("stripe-api"))
134134
.addTargetSystem(new NonTransactionalTargetSystem("okta"))
135135
.addTargetSystem(new NonTransactionalTargetSystem("elasticsearch"))
@@ -164,7 +164,7 @@ void shouldVerifyDependencyInjectionInRollbackForNonTransactionalChanges() {
164164
);
165165

166166
FlamingockTestSupport
167-
.given(testKit.createBuilder().addTargetSystem(targetSystem))
167+
.givenBuilder(testKit.createBuilder().addTargetSystem(targetSystem))
168168
.whenRun()
169169
.thenExpectException(PipelineExecutionException.class, ex -> {
170170
assertTrue(ex.getMessage().contains("Intentional failure"));
@@ -192,7 +192,7 @@ void shouldVerifyTransactionalChangeExecutesSuccessfully() {
192192
);
193193

194194
FlamingockTestSupport
195-
.given(testKit.createBuilder()
195+
.givenBuilder(testKit.createBuilder()
196196
.addTargetSystem(new NonTransactionalTargetSystem("okta"))
197197
.addTargetSystem(new NonTransactionalTargetSystem("elasticsearch"))
198198
.addTargetSystem(new NonTransactionalTargetSystem("s3")))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package io.flamingock.springboot.testsupport;
2+
3+
import io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder;
4+
import io.flamingock.support.FlamingockTestSupport;
5+
import io.flamingock.support.stages.GivenStage;
6+
7+
public class FlamingockSpringBootTestSupport {
8+
9+
private final AbstractChangeRunnerBuilder<?, ?> builderFromContext;
10+
11+
FlamingockSpringBootTestSupport(AbstractChangeRunnerBuilder<?,?> builderFromContext) {
12+
this.builderFromContext = builderFromContext;
13+
}
14+
15+
public GivenStage givenBuilderFromContext() {
16+
return FlamingockTestSupport.givenBuilder(builderFromContext);
17+
}
18+
19+
public GivenStage givenBuilder(AbstractChangeRunnerBuilder<?,?> builderFromContextOverwrite) {
20+
return FlamingockTestSupport.givenBuilder(builderFromContextOverwrite);
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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 io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder;
19+
import io.flamingock.support.FlamingockTestSupport;
20+
import io.flamingock.support.stages.GivenStage;
21+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
22+
import org.springframework.context.annotation.Bean;
23+
import org.springframework.context.annotation.Configuration;
24+
import org.springframework.context.annotation.Scope;
25+
26+
/**
27+
* Auto-configuration that provides a {@link GivenStage} bean for Flamingock testing.
28+
*
29+
* <p>This configuration is automatically picked up when using {@link FlamingockSpringBootTest}
30+
* and provides a ready-to-use {@code GivenStage} bean that can be autowired directly in tests.</p>
31+
*
32+
* <p><strong>Example usage:</strong></p>
33+
* <pre>{@code
34+
* @FlamingockSpringBootTest(classes = {MyApp.class, TestConfig.class})
35+
* class MyFlamingockTest {
36+
*
37+
* @Autowired
38+
* private GivenStage givenStage;
39+
*
40+
* @Test
41+
* void testMigration() {
42+
* givenStage
43+
* .whenRun()
44+
* .thenExpectAuditSequenceStrict(APPLIED(MyChange.class))
45+
* .verify();
46+
* }
47+
* }
48+
* }</pre>
49+
*
50+
* <p><strong>Note:</strong> The {@code GivenStage} bean has prototype scope, meaning a new instance
51+
* is created for each injection point. This is necessary because {@code GivenStage} accumulates
52+
* internal state (preconditions, expectations) and is not reusable between tests.</p>
53+
*
54+
* @see FlamingockSpringBootTest
55+
* @see FlamingockTestSupport
56+
* @see GivenStage
57+
* @since 1.0
58+
*/
59+
@Configuration
60+
public class FlamingockTestAutoConfiguration {
61+
62+
/**
63+
* Creates a {@link GivenStage} bean for Flamingock testing.
64+
*
65+
* <p>The bean has prototype scope to ensure each test gets its own fresh instance,
66+
* as {@code GivenStage} accumulates state and cannot be shared between tests.</p>
67+
*
68+
* @param builderFromContext the Flamingock builder, automatically configured by Spring Boot
69+
* @return a new {@code GivenStage} instance ready for testing
70+
*/
71+
@Bean
72+
@Scope("prototype")
73+
@ConditionalOnBean(AbstractChangeRunnerBuilder.class)
74+
public FlamingockSpringBootTestSupport flamingockGivenStage(AbstractChangeRunnerBuilder<?, ?> builderFromContext) {
75+
return new FlamingockSpringBootTestSupport(builderFromContext);
76+
}
77+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2+
io.flamingock.springboot.testsupport.FlamingockTestAutoConfiguration

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

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,42 +15,25 @@
1515
*/
1616
package io.flamingock.springboot.testsupport;
1717

18-
import io.flamingock.internal.common.core.audit.AuditEntry;
19-
import io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder;
20-
import io.flamingock.internal.core.store.CommunityAuditStore;
18+
import io.flamingock.springboot.testsupport.changes._001__SimpleTestChange;
2119
import org.junit.jupiter.api.Test;
2220
import org.junit.jupiter.api.extension.ExtendWith;
2321
import org.springframework.beans.factory.annotation.Autowired;
2422
import org.springframework.context.ApplicationContext;
2523
import org.springframework.test.context.junit.jupiter.SpringExtension;
2624

27-
import java.util.List;
28-
import java.util.stream.Collectors;
29-
25+
import static io.flamingock.support.domain.AuditEntryDefinition.APPLIED;
3026
import static org.assertj.core.api.Assertions.assertThat;
3127

32-
/**
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>
41-
*/
42-
@ExtendWith(SpringExtension.class) // Required for Spring Boot 2.0.x
28+
@ExtendWith(SpringExtension.class) // Required for Spring Boot 2.0.x. For >= 2.1.0, not required
4329
@FlamingockSpringBootTest(classes = {TestApplication.class, FlamingockTestConfiguration.class})
4430
class SpringBootFlamingockIntegrationTest {
4531

4632
@Autowired
4733
private ApplicationContext context;
4834

4935
@Autowired
50-
private CommunityAuditStore auditStore;
51-
52-
@Autowired
53-
private AbstractChangeRunnerBuilder<?, ?> flamingockBuilder;
36+
private FlamingockSpringBootTestSupport testSupport;
5437

5538
@Test
5639
void contextLoads() {
@@ -68,22 +51,15 @@ void flamingockRunnerBeanExists() {
6851
}
6952

7053
@Test
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()
79-
.getAuditHistory()
80-
.stream()
81-
.filter(e -> "simple-test-change".equals(e.getTaskId()))
82-
.filter(e -> AuditEntry.Status.APPLIED.equals(e.getState()))
83-
.collect(Collectors.toList());
54+
void shouldUseTestSupportAsExpected() {
8455

85-
assertThat(appliedEntries)
86-
.as("Change should be applied after manual execution")
87-
.hasSize(1);
56+
testSupport
57+
.givenBuilderFromContext()
58+
.whenRun()
59+
.thenExpectAuditSequenceStrict(
60+
APPLIED(_001__SimpleTestChange.class)
61+
)
62+
.verify();
8863
}
64+
8965
}

0 commit comments

Comments
 (0)