diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/context/TestContext.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/context/TestContext.java new file mode 100644 index 000000000..2c21f3e6d --- /dev/null +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/context/TestContext.java @@ -0,0 +1,92 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.support.context; + +import io.flamingock.internal.common.core.audit.AuditReader; +import io.flamingock.internal.common.core.audit.AuditWriter; +import io.flamingock.internal.core.builder.BuilderAccessor; +import io.flamingock.support.domain.AuditEntryDefinition; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Context object that carries test execution data through the BDD stages. + * + *

This class encapsulates the builder accessor and preconditions, providing + * controlled access to only what each stage needs. It follows the principle of + * exposing behavior, not implementation details.

+ * + *

Key design decisions:

+ * + */ +public class TestContext { + + private final BuilderAccessor builderAccessor; + private final List preconditions; + + /** + * Creates a new test context with the given builder accessor and preconditions. + * + * @param builderAccessor the builder accessor for running and accessing audit store + * @param preconditions the list of audit entry definitions to insert as preconditions + */ + public TestContext(BuilderAccessor builderAccessor, List preconditions) { + this.builderAccessor = builderAccessor; + this.preconditions = preconditions != null + ? new ArrayList<>(preconditions) + : new ArrayList<>(); + } + + /** + * Returns the audit reader for reading audit entries. + * + * @return the audit reader + */ + public AuditReader getAuditReader() { + return builderAccessor.getAuditStore().getPersistence(); + } + + /** + * Returns the audit writer for writing audit entries. + * + * @return the audit writer + */ + public AuditWriter getAuditWriter() { + return builderAccessor.getAuditStore().getPersistence(); + } + + /** + * Runs the change runner by building and executing it. + */ + public void run() { + builderAccessor.run(); + } + + /** + * Returns an unmodifiable view of the preconditions. + * + * @return the preconditions list + */ + public List getPreconditions() { + return Collections.unmodifiableList(preconditions); + } +} diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/domain/AuditEntryDefinition.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/domain/AuditEntryDefinition.java index 6dfa0950d..0ec6816f7 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/domain/AuditEntryDefinition.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/domain/AuditEntryDefinition.java @@ -22,8 +22,10 @@ import io.flamingock.api.annotations.Rollback; import io.flamingock.api.annotations.TargetSystem; import io.flamingock.internal.common.core.audit.AuditEntry; +import io.flamingock.internal.common.core.audit.AuditTxType; import java.lang.annotation.Annotation; +import java.util.UUID; import java.lang.reflect.Method; import java.time.LocalDateTime; @@ -505,4 +507,48 @@ public String getOrder() { public Boolean getTransactional() { return transactional; } + + // ========== Conversion Methods ========== + + /** + * Converts this definition to an {@link AuditEntry} for insertion into the audit store. + * + *

Fields that are not set will use sensible defaults:

+ *
    + *
  • {@code executionId} - UUID-based if not specified
  • + *
  • {@code stageId} - UUID-based if not specified
  • + *
  • {@code createdAt} - current time if not specified
  • + *
  • {@code executionMillis} - 0 if not specified
  • + *
  • {@code executionHostname} - "test-host" if not specified
  • + *
  • {@code type} - {@code ExecutionType.EXECUTION}
  • + *
  • {@code txStrategy} - {@code AuditTxType.NON_TX}
  • + *
  • {@code systemChange} - false
  • + *
  • {@code recoveryStrategy} - {@code RecoveryStrategy.MANUAL_INTERVENTION} if not specified
  • + *
+ * + * @return an {@link AuditEntry} instance representing this definition + */ + public AuditEntry toAuditEntry() { + return new AuditEntry( + executionId != null ? executionId : "precondition-" + UUID.randomUUID().toString(), + stageId != null ? stageId : "precondition-stage-" + UUID.randomUUID().toString(), + changeId, + author, + createdAt != null ? createdAt : LocalDateTime.now(), + state, + AuditEntry.ExecutionType.EXECUTION, + className, + methodName, + executionMillis != null ? executionMillis : 0L, + executionHostname != null ? executionHostname : "test-host", + metadata, + false, // systemChange + errorTrace, + AuditTxType.NON_TX, + targetSystemId, + order, + recoveryStrategy != null ? recoveryStrategy : RecoveryStrategy.MANUAL_INTERVENTION, + transactional + ); + } } diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/precondition/PreconditionInserter.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/precondition/PreconditionInserter.java new file mode 100644 index 000000000..b00c86b05 --- /dev/null +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/precondition/PreconditionInserter.java @@ -0,0 +1,60 @@ +/* + * Copyright 2025 Flamingock (https://www.flamingock.io) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.flamingock.support.precondition; + +import io.flamingock.internal.common.core.audit.AuditWriter; +import io.flamingock.support.domain.AuditEntryDefinition; + +import java.util.List; + +/** + * Inserts audit entry preconditions into the audit store before test execution. + * + *

This class is responsible for converting {@link AuditEntryDefinition} instances + * to actual audit entries and inserting them into the audit store. It follows the + * dependency injection pattern by receiving the {@link AuditWriter} via constructor.

+ */ +public class PreconditionInserter { + + private final AuditWriter auditWriter; + + /** + * Creates a new precondition inserter with the given audit writer. + * + * @param auditWriter the audit writer to use for inserting entries + */ + public PreconditionInserter(AuditWriter auditWriter) { + this.auditWriter = auditWriter; + } + + /** + * Inserts the given preconditions into the audit store. + * + *

Each {@link AuditEntryDefinition} is converted to an {@code AuditEntry} + * and written to the audit store. If the preconditions list is null or empty, + * this method does nothing.

+ * + * @param preconditions the list of audit entry definitions to insert + */ + public void insert(List preconditions) { + if (preconditions == null || preconditions.isEmpty()) { + return; + } + for (AuditEntryDefinition definition : preconditions) { + auditWriter.writeEntry(definition.toAuditEntry()); + } + } +} diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/GivenStageImpl.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/GivenStageImpl.java index af772b6cc..cf7606896 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/GivenStageImpl.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/GivenStageImpl.java @@ -16,6 +16,7 @@ package io.flamingock.support.stages; import io.flamingock.internal.core.builder.BuilderAccessor; +import io.flamingock.support.context.TestContext; import io.flamingock.support.domain.AuditEntryDefinition; import java.util.ArrayList; @@ -41,7 +42,8 @@ public GivenStage andExistingAudit(AuditEntryDefinition... definitions) { @Override public WhenStage whenRun() { - return new WhenStageImpl(builderAccessor); + TestContext testContext = new TestContext(builderAccessor, existingAudit); + return new WhenStageImpl(testContext); } /** diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/ThenStageImpl.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/ThenStageImpl.java index 0412e6f7c..f8f810ecb 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/ThenStageImpl.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/ThenStageImpl.java @@ -15,8 +15,9 @@ */ package io.flamingock.support.stages; -import io.flamingock.internal.core.builder.BuilderAccessor; +import io.flamingock.support.context.TestContext; import io.flamingock.support.domain.AuditEntryDefinition; +import io.flamingock.support.precondition.PreconditionInserter; import io.flamingock.support.validation.ValidationHandler; import io.flamingock.support.validation.Validator; import io.flamingock.support.validation.ValidatorFactory; @@ -29,11 +30,11 @@ final class ThenStageImpl implements ThenStage { private final List validators = new ArrayList<>(); private final ValidatorFactory validatorFactory; - private final BuilderAccessor builderAccessor; + private final TestContext testContext; - ThenStageImpl(BuilderAccessor builderAccessor) { - this.builderAccessor = builderAccessor; - validatorFactory = new ValidatorFactory(builderAccessor); + ThenStageImpl(TestContext testContext) { + this.testContext = testContext; + validatorFactory = new ValidatorFactory(testContext.getAuditReader()); } @Override @@ -50,10 +51,13 @@ public ThenStage andExpectException(Class exceptionClass, C @Override public void verify() throws AssertionError { + // Insert preconditions first + PreconditionInserter preconditionInserter = new PreconditionInserter(testContext.getAuditWriter()); + preconditionInserter.insert(testContext.getPreconditions()); ValidationHandler validationHandler; try { - builderAccessor.run(); + testContext.run(); validationHandler = new ValidationHandler(validators); } catch (Throwable actualException) { diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/WhenStageImpl.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/WhenStageImpl.java index 5de71b4ab..69cb67bfa 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/WhenStageImpl.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/stages/WhenStageImpl.java @@ -15,27 +15,27 @@ */ package io.flamingock.support.stages; -import io.flamingock.internal.core.builder.BuilderAccessor; +import io.flamingock.support.context.TestContext; import io.flamingock.support.domain.AuditEntryDefinition; import java.util.function.Consumer; public class WhenStageImpl implements WhenStage { - private final BuilderAccessor builderAccessor; + private final TestContext testContext; - WhenStageImpl(BuilderAccessor builderAccessor) { - this.builderAccessor = builderAccessor; + WhenStageImpl(TestContext testContext) { + this.testContext = testContext; } @Override public ThenStage thenExpectAuditSequenceStrict(AuditEntryDefinition... definitions) { - return new ThenStageImpl(builderAccessor).andExpectAuditSequenceStrict(definitions); + return new ThenStageImpl(testContext).andExpectAuditSequenceStrict(definitions); } @Override public ThenStage thenExpectException(Class exceptionClass, Consumer validator) { - return new ThenStageImpl(builderAccessor).andExpectException(exceptionClass, validator); + return new ThenStageImpl(testContext).andExpectException(exceptionClass, validator); } } diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/ValidatorFactory.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/ValidatorFactory.java index 475daa18e..bd226c436 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/ValidatorFactory.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/ValidatorFactory.java @@ -15,7 +15,7 @@ */ package io.flamingock.support.validation; -import io.flamingock.internal.core.builder.BuilderAccessor; +import io.flamingock.internal.common.core.audit.AuditReader; import io.flamingock.support.domain.AuditEntryDefinition; import io.flamingock.support.validation.impl.AuditSequenceStrictValidator; import io.flamingock.support.validation.impl.DefaultExceptionValidator; @@ -24,14 +24,14 @@ public class ValidatorFactory { - private final BuilderAccessor builderAccessor; + private final AuditReader auditReader; - public ValidatorFactory(BuilderAccessor builderAccessor) { - this.builderAccessor = builderAccessor; + public ValidatorFactory(AuditReader auditReader) { + this.auditReader = auditReader; } public Validator getAuditSeqStrictValidator(AuditEntryDefinition... definitions) { - return new AuditSequenceStrictValidator(builderAccessor.getAuditStore(), definitions); + return new AuditSequenceStrictValidator(auditReader, definitions); } public Validator getExceptionValidator(Class exceptionClass, Consumer exceptionConsumer) { diff --git a/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditSequenceStrictValidator.java b/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditSequenceStrictValidator.java index 972418599..d8d89b853 100644 --- a/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditSequenceStrictValidator.java +++ b/core/flamingock-test-support/src/main/java/io/flamingock/support/validation/impl/AuditSequenceStrictValidator.java @@ -15,7 +15,7 @@ */ package io.flamingock.support.validation.impl; -import io.flamingock.internal.core.store.AuditStore; +import io.flamingock.internal.common.core.audit.AuditReader; import io.flamingock.support.domain.AuditEntryDefinition; import io.flamingock.support.validation.SimpleValidator; import io.flamingock.support.validation.error.ValidationResult; @@ -28,12 +28,12 @@ public class AuditSequenceStrictValidator implements SimpleValidator { private static final String VALIDATOR_NAME = "Audit Sequence (Strict)"; - private final AuditStore auditStore; + private final AuditReader auditReader; private final List expectations; - public AuditSequenceStrictValidator(AuditStore auditStore, AuditEntryDefinition... definitions) { - this.auditStore = auditStore; + public AuditSequenceStrictValidator(AuditReader auditReader, AuditEntryDefinition... definitions) { + this.auditReader = auditReader; this.expectations = Arrays.stream(definitions) .map(AuditEntryExpectation::new) .collect(Collectors.toList());