Skip to content

Commit 8ba0fbe

Browse files
feat: flamingock test support foundation (#751)
1 parent aaa6a0e commit 8ba0fbe

11 files changed

Lines changed: 1049 additions & 2 deletions

File tree

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
dependencies {
2+
api(project(":core:flamingock-core"))
3+
4+
testImplementation(platform("org.junit:junit-bom:5.10.0"))
5+
testImplementation("org.junit.jupiter:junit-jupiter")
6+
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
7+
}
8+
9+
description = "Test support module for Flamingock framework"
10+
11+
12+
tasks.withType<JavaCompile>().configureEach {
13+
if (name.contains("Test", ignoreCase = true)) {
14+
options.compilerArgs.addAll(listOf(
15+
"-Asources=${projectDir}/src/test/java",
16+
"-Aresources=${projectDir}/src/test/resources"
17+
))
18+
}
19+
}
20+
21+
java {
22+
toolchain {
23+
languageVersion.set(JavaLanguageVersion.of(8))
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.internal.core.builder;
17+
18+
import io.flamingock.internal.core.store.AuditStore;
19+
20+
public class BuilderAccessor {
21+
22+
private AbstractChangeRunnerBuilder<?, ?> builder;
23+
24+
public BuilderAccessor(AbstractChangeRunnerBuilder<?,?> builder) {
25+
this.builder = builder;
26+
}
27+
28+
public AuditStore<?> getAuditStore(){
29+
return builder.auditStore;
30+
}
31+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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.support;
17+
18+
19+
import io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder;
20+
import io.flamingock.internal.core.builder.BuilderAccessor;
21+
import io.flamingock.support.impl.GivenStageImpl;
22+
23+
/**
24+
* Entry point for the Flamingock BDD-style test support framework.
25+
*
26+
* <p>This class provides a fluent API for testing Flamingock change executions using the
27+
* Given-When-Then pattern. It allows you to set up preconditions (existing audit state),
28+
* define expectations, and verify the resulting audit entries.</p>
29+
*
30+
* <h2>Usage Example</h2>
31+
* <pre>{@code
32+
* FlamingockTestSupport
33+
* .given(builder)
34+
* .andAppliedChanges(InitialSetupChange.class, SchemaV1Change.class)
35+
* .andFailedChanges(FailedMigrationChange.class)
36+
* .whenRun()
37+
* .thenExpectAuditSequenceStrict(
38+
* APPLIED("schema-v2-change")
39+
* .withClass(SchemaV2Change.class)
40+
* .withAuthor("dev-team"),
41+
* APPLIED("data-migration-change")
42+
* )
43+
* .verify();
44+
* }</pre>
45+
*
46+
* <h2>Test Flow</h2>
47+
* <ol>
48+
* <li><b>Given</b> ({@link GivenStage}): Define the initial audit state (preconditions)</li>
49+
* <li><b>When</b> ({@link WhenStage}): Define expectations for when the runner executes</li>
50+
* <li><b>Then</b> ({@link ThenStage}): Chain additional expectations</li>
51+
* <li><b>Verify</b> ({@link ThenStage#verify()}): Execute the runner and validate expectations</li>
52+
* </ol>
53+
*
54+
* <p><b>Note:</b> This is a lazy API similar to Java Streams. All methods except
55+
* {@link ThenStage#verify()} are intermediate operations. The actual execution
56+
* and verification only occur when {@code verify()} is called.</p>
57+
*
58+
* @see GivenStage
59+
* @see WhenStage
60+
* @see ThenStage
61+
* @see io.flamingock.support.domain.AuditEntryExpectation
62+
*/
63+
public final class FlamingockTestSupport {
64+
65+
private FlamingockTestSupport() {
66+
}
67+
68+
/**
69+
* Starts the test setup phase with the provided Flamingock builder.
70+
*
71+
* <p>This method initializes the BDD test flow by accepting a configured
72+
* {@link AbstractChangeRunnerBuilder}. The builder should be fully configured
73+
* with the pipeline, audit store, and any other required settings before
74+
* being passed to this method.</p>
75+
*
76+
* @param builder the configured change runner builder to test; must not be {@code null}
77+
* @return the {@link GivenStage} for defining initial audit state preconditions
78+
* @throws NullPointerException if {@code builder} is {@code null}
79+
*/
80+
public static GivenStage given(AbstractChangeRunnerBuilder<?, ?> builder) {
81+
if (builder == null) {
82+
throw new NullPointerException("builder must not be null");
83+
}
84+
BuilderAccessor builderAccessor = new BuilderAccessor(builder);
85+
return new GivenStageImpl();
86+
}
87+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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.support;
17+
18+
/**
19+
* Represents the "Given" phase of the BDD test flow for setting up preconditions.
20+
*
21+
* <p>This stage allows you to define the initial audit state that should exist
22+
* before running the change runner. You can specify which changes should be
23+
* marked as already applied, failed, or rolled back in the audit store.</p>
24+
*
25+
* <p>All methods in this interface are intermediate operations that return {@code this}
26+
* to enable fluent method chaining. When setup is complete, call {@link #whenRun()}
27+
* to transition to the assertion phase.</p>
28+
*
29+
* <p>Note: This is a lazy API. No execution occurs until {@link ThenStage#verify()}
30+
* is called at the end of the chain.</p>
31+
*
32+
* <h2>Example</h2>
33+
* <pre>{@code
34+
* FlamingockTestSupport
35+
* .given(builder)
36+
* .andAppliedChanges(SetupChange.class, MigrationV1.class)
37+
* .andFailedChanges(FailedChange.class)
38+
* .whenRun()
39+
* // ... assertions
40+
* }</pre>
41+
*
42+
* @see FlamingockTestSupport#given(io.flamingock.internal.core.builder.AbstractChangeRunnerBuilder)
43+
* @see WhenStage
44+
*/
45+
public interface GivenStage {
46+
47+
/**
48+
* Specifies changes that should be marked as successfully applied in the audit store
49+
* before running the change runner.
50+
*
51+
* <p>These changes will be treated as if they were already executed successfully
52+
* in a previous run. The change runner will skip them during execution.</p>
53+
*
54+
* @param changes the change classes to mark as applied
55+
* @return this stage for method chaining
56+
*/
57+
GivenStage andAppliedChanges(Class<?>... changes);
58+
59+
/**
60+
* Specifies changes that should be marked as failed in the audit store
61+
* before running the change runner.
62+
*
63+
* <p>These changes will be treated as if they failed in a previous run.
64+
* Depending on the runner configuration, they may be retried or skipped.</p>
65+
*
66+
* @param changes the change classes to mark as failed
67+
* @return this stage for method chaining
68+
*/
69+
GivenStage andFailedChanges(Class<?>... changes);
70+
71+
/**
72+
* Specifies changes that should be marked as rolled back in the audit store
73+
* before running the change runner.
74+
*
75+
* <p>These changes will be treated as if they were executed but subsequently
76+
* rolled back in a previous run.</p>
77+
*
78+
* @param changes the change classes to mark as rolled back
79+
* @return this stage for method chaining
80+
*/
81+
GivenStage andRolledBackChanges(Class<?>... changes);
82+
83+
/**
84+
* Completes the setup phase and transitions to the assertion phase.
85+
*
86+
* <p>This method finalizes the preconditions and returns a {@link WhenStage}
87+
* where you can define expectations about the audit entries that should
88+
* result from running the change runner.</p>
89+
*
90+
* <p>Note: The actual execution occurs when {@link ThenStage#verify()} is called.</p>
91+
*
92+
* @return the {@link WhenStage} for defining assertions
93+
*/
94+
WhenStage whenRun();
95+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
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.support;
17+
18+
import io.flamingock.support.domain.AuditEntryExpectation;
19+
20+
import java.util.function.Consumer;
21+
22+
/**
23+
* Represents the "Then" phase of the BDD test flow for chaining assertions
24+
* and triggering final execution and verification.
25+
*
26+
* <p>This stage allows you to add additional expectations after the initial assertion
27+
* defined in {@link WhenStage}. The {@code andExpectXxx()} methods are intermediate
28+
* operations that accumulate expectations.</p>
29+
*
30+
* <p>The {@link #verify()} method is the <b>terminal operation</b> that:</p>
31+
* <ol>
32+
* <li>Executes the change runner with the configured preconditions</li>
33+
* <li>Validates all accumulated expectations against the actual results</li>
34+
* <li>Throws an assertion error if any expectation is not met</li>
35+
* </ol>
36+
*
37+
* <h2>Example with Multiple Assertions</h2>
38+
* <pre>{@code
39+
* .whenRun()
40+
* .thenExpectAuditSequenceStrict(
41+
* AuditEntryExpectation.APPLIED("change-1")
42+
* )
43+
* .andExpectAuditSequenceStrict(
44+
* AuditEntryExpectation.APPLIED("change-2")
45+
* )
46+
* .verify(); // Execution and verification happen here
47+
* }</pre>
48+
*
49+
* @see WhenStage
50+
* @see AuditEntryExpectation
51+
*/
52+
public interface ThenStage {
53+
54+
/**
55+
* Adds an additional strict expectation for the audit entry sequence.
56+
*
57+
* <p>This method has the same semantics as
58+
* {@link WhenStage#thenExpectAuditSequenceStrict(AuditEntryExpectation...)}
59+
* but allows chaining multiple sequence expectations.</p>
60+
*
61+
* <p><b>Strict validation</b> means:</p>
62+
* <ul>
63+
* <li>The number of actual audit entries must exactly match the number of expectations</li>
64+
* <li>The order of audit entries must exactly match the order of expectations</li>
65+
* <li>Each audit entry is validated against its corresponding expectation</li>
66+
* </ul>
67+
*
68+
* @param expectations the expected audit entries in exact order
69+
* @return this stage for method chaining
70+
* @see AuditEntryExpectation
71+
*/
72+
ThenStage andExpectAuditSequenceStrict(AuditEntryExpectation... expectations);
73+
74+
/**
75+
* Adds an expectation that the execution should throw a specific exception.
76+
*
77+
* <p>This method has the same semantics as
78+
* {@link WhenStage#thenExpectException(Class, Consumer)}
79+
* but allows chaining after other assertions.</p>
80+
*
81+
* @param exceptionClass the expected exception type
82+
* @param validator a consumer to perform additional assertions on the thrown exception;
83+
* may be {@code null} if no additional validation is needed
84+
* @return this stage for method chaining
85+
*/
86+
ThenStage andExpectException(Class<? extends Throwable> exceptionClass, Consumer<Throwable> validator);
87+
88+
/**
89+
* Terminal operation that executes the change runner and verifies all expectations.
90+
*
91+
* <p>This method performs the following steps:</p>
92+
* <ol>
93+
* <li>Sets up the audit store with the preconditions defined in {@link GivenStage}</li>
94+
* <li>Executes the Flamingock change runner</li>
95+
* <li>Validates all accumulated expectations against the actual audit entries</li>
96+
* </ol>
97+
*
98+
* <p>This is the only method in the fluent chain that triggers actual execution.
99+
* All preceding methods ({@code given}, {@code andAppliedChanges}, {@code whenRun},
100+
* {@code thenExpect...}) are intermediate operations that only configure the test.</p>
101+
*
102+
* @throws AssertionError if any expectation is not met
103+
*/
104+
void verify();
105+
}

0 commit comments

Comments
 (0)