Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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.</p>
*
* <p>Key design decisions:</p>
* <ul>
* <li>No getter for {@code BuilderAccessor} - exposes only what's needed</li>
* <li>Separate {@code getAuditReader()} and {@code getAuditWriter()} methods</li>
* <li>Immutable preconditions list (defensive copy)</li>
* </ul>
*/
public class TestContext {

private final BuilderAccessor builderAccessor;
private final List<AuditEntryDefinition> 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<AuditEntryDefinition> 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<AuditEntryDefinition> getPreconditions() {
return Collections.unmodifiableList(preconditions);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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.
*
* <p>Fields that are not set will use sensible defaults:</p>
* <ul>
* <li>{@code executionId} - UUID-based if not specified</li>
* <li>{@code stageId} - UUID-based if not specified</li>
* <li>{@code createdAt} - current time if not specified</li>
* <li>{@code executionMillis} - 0 if not specified</li>
* <li>{@code executionHostname} - "test-host" if not specified</li>
* <li>{@code type} - {@code ExecutionType.EXECUTION}</li>
* <li>{@code txStrategy} - {@code AuditTxType.NON_TX}</li>
* <li>{@code systemChange} - false</li>
* <li>{@code recoveryStrategy} - {@code RecoveryStrategy.MANUAL_INTERVENTION} if not specified</li>
* </ul>
*
* @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
);
}
}
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>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.</p>
*/
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.
*
* <p>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.</p>
*
* @param preconditions the list of audit entry definitions to insert
*/
public void insert(List<AuditEntryDefinition> preconditions) {
if (preconditions == null || preconditions.isEmpty()) {
return;
}
for (AuditEntryDefinition definition : preconditions) {
auditWriter.writeEntry(definition.toAuditEntry());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,11 +30,11 @@ final class ThenStageImpl implements ThenStage {

private final List<Validator> 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
Expand All @@ -50,10 +51,13 @@ public ThenStage andExpectException(Class<? extends Throwable> 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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<? extends Throwable> exceptionClass, Consumer<Throwable> validator) {
return new ThenStageImpl(builderAccessor).andExpectException(exceptionClass, validator);
return new ThenStageImpl(testContext).andExpectException(exceptionClass, validator);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<? extends Throwable> exceptionClass, Consumer<Throwable> exceptionConsumer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<AuditEntryExpectation> 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());
Expand Down
Loading