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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<dependency>
<groupId>it.aboutbits</groupId>
<artifactId>spring-boot-toolbox</artifactId>
<version>1.0.0-RC1</version>
<version>1.0.0-RC2</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package it.aboutbits.springboot.testing.persistance;

import it.aboutbits.springboot.testing.spring.BeanAccessor;
import it.aboutbits.springboot.toolbox.persistence.ChangeAware;
import it.aboutbits.springboot.toolbox.type.identity.EntityId;
import it.aboutbits.springboot.toolbox.type.identity.Identified;
import jakarta.persistence.EntityManager;
import lombok.AccessLevel;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

import static org.assertj.core.api.Assertions.assertThat;

public final class PersistenceAssert {
private PersistenceAssert() {
}

private static EntityManager getEntityManager() {
return BeanAccessor.getBean(EntityManager.class);
}

@SuppressWarnings("unused")
public static <E extends Identified<?> & ChangeAware> WriteOperationAsserter<E> assertThatEntity(@NonNull E before) {
return new WriteOperationAsserter<>(getEntityManager(), before);
}

@SuppressWarnings("unused")
public static <ID extends EntityId<?>> WriteOperationIdAsserter<ID> assertThatEntity(@NonNull ID id) {
return new WriteOperationIdAsserter<>(getEntityManager(), id);
}

@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public static final class WriteOperationAsserter<E extends Identified<?> & ChangeAware> {
private final EntityManager entityManager;
private final E entity;

@SuppressWarnings("unused")
public void hasBeenCreatedInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(
savedInstance.getId()
).isNotNull();

assertThat(
savedInstance.getCreatedAt()
).isNotNull();
}

@SuppressWarnings("unused")
public void hasBeenUpdatedInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(
savedInstance.getUpdatedAt()
).isAfter(
entity.getUpdatedAt()
);
}

@SuppressWarnings("unused")
public void hasNotChangedInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(savedInstance).isNotNull();

assertThat(
savedInstance.getUpdatedAt()
).isEqualTo(
entity.getUpdatedAt()
);
}

@SuppressWarnings("unused")
public void isAbsentInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(savedInstance).isNull();
}

@SuppressWarnings("unused")
public void isPresentInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(savedInstance).isNotNull();
}

@SuppressWarnings("unchecked")
private E getSavedInstance() {
return (E) entityManager.find(
entity.getClass(),
entity.getId()
);
}
}

public static final class WriteOperationIdAsserter<ID extends EntityId<?>> {
private final EntityManager entityManager;
private final ID id;
private final Class<? extends Identified<? extends ID>> clazz;

@SuppressWarnings("unchecked")
private WriteOperationIdAsserter(
EntityManager entityManager,
ID id
) {
this.entityManager = entityManager;
this.id = id;

var enclosingClass = id.getClass().getEnclosingClass();
if (Identified.class.isAssignableFrom(enclosingClass)) {
this.clazz = (Class<? extends Identified<? extends ID>>) enclosingClass;
} else {
throw new IllegalArgumentException("EntityId is not a subclass of Identified!");
}
}

@SuppressWarnings("unused")
public void isAbsentInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(savedInstance).isNull();
}

@SuppressWarnings("unused")
public void isPresentInDatabase() {
entityManager.clear();
var savedInstance = getSavedInstance();

assertThat(savedInstance).isNotNull();
}

private Object getSavedInstance() {
return entityManager.find(
clazz,
id
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package it.aboutbits.springboot.testing.spring;

import lombok.NonNull;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class BeanAccessor implements ApplicationContextAware {
private static ApplicationContext applicationContext;

public static <T> T getBean(@NonNull Class<T> clazz) {
if (applicationContext == null) {
throw new IllegalStateException("ApplicationContext is not set. Did you @EnableBeanAccessor?");
}
return applicationContext.getBean(clazz);
}

@Override
public void setApplicationContext(@NonNull ApplicationContext applicationContext) {
BeanAccessor.applicationContext = applicationContext;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package it.aboutbits.springboot.testing.spring;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import(BeanAccessor.class)
public @interface EnableBeanAccessor {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package it.aboutbits.springboot.testing.testdata.base;

import it.aboutbits.springboot.testing.spring.BeanAccessor;
import jakarta.persistence.EntityManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.support.TransactionTemplate;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.ObjIntConsumer;
import java.util.function.UnaryOperator;

@SuppressWarnings("unchecked")
@Slf4j
public abstract class ModifyableTestDataCreator<CREATOR extends ModifyableTestDataCreator<CREATOR, ITEM, PARAMETER>, ITEM, PARAMETER> extends TestDataCreator<ITEM> {
private boolean mutatorSet = false;
private boolean mutatorCalled = false;

protected BiFunction<PARAMETER, Integer, PARAMETER> parameterMutator = (parameter, index) -> {
mutatorCalled = true;
return parameter;
};
protected ObjIntConsumer<ITEM> resultMutator = (item, index) -> {
};

protected ModifyableTestDataCreator(int count) {
super(count);
}

public CREATOR modifyParameter(BiFunction<PARAMETER, Integer, PARAMETER> parameterMutator) {
this.parameterMutator = (parameter, index) -> {
mutatorCalled = true;
return parameterMutator.apply(parameter, index);
};
mutatorSet = true;
return (CREATOR) this;
}

public CREATOR modifyParameter(UnaryOperator<PARAMETER> parameterMutator) {
this.parameterMutator = (parameter, index) -> {
mutatorCalled = true;
return parameterMutator.apply(parameter);
};
mutatorSet = true;
return (CREATOR) this;
}

public CREATOR modifyResult(ObjIntConsumer<ITEM> resultMutator) {
this.resultMutator = resultMutator;
return (CREATOR) this;
}

public CREATOR modifyResult(Consumer<ITEM> resultMutator) {
this.resultMutator = (item, index) -> resultMutator.accept(item);
return (CREATOR) this;
}

@Override
protected List<ITEM> create() {
var result = new ArrayList<ITEM>();

for (var index = 0; index < numberOfItems; index++) {
var item = create(index);

resultMutator.accept(item, index);

result.add(
saveMutation(item)
);
}

if (mutatorSet && !mutatorCalled) {
log.error("Parameter-mutation is defined but was never called.");
throw new IllegalStateException("Parameter-mutation is defined but was never called.");
}

return result;
}

protected ITEM saveMutation(ITEM item) {
var entityManager = BeanAccessor.getBean(EntityManager.class);
var transactionTemplate = BeanAccessor.getBean(TransactionTemplate.class);

return transactionTemplate.execute(status -> {
try {
return entityManager.merge(item);
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package it.aboutbits.springboot.testing.testdata.base;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;

public abstract class TestDataCreator<ITEM> {
protected final int numberOfItems;

protected TestDataCreator(int numberIfItems) {
this.numberOfItems = numberIfItems;
}

public void commit() {
create();
}

public ITEM returnFirst() {
return create().getFirst();
}

public List<ITEM> returnAll() {
return create();
}

public List<ITEM> returnSorted(Comparator<ITEM> comparator) {
return returnAll().stream().sorted(comparator).toList();
}

public <U extends Comparable<? super U>> List<ITEM> returnSorted(Function<ITEM, U> comparator) {
return returnAll().stream()
.sorted(Comparator.comparing(comparator))
.toList();
}

public Set<ITEM> returnSet() {
return new HashSet<>(create());
}

protected List<ITEM> create() {
var result = new ArrayList<ITEM>();

for (var index = 0; index < numberOfItems; index++) {
result.add(
create(index)
);
}

return result;
}

protected abstract ITEM create(int index);

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public abstract class BaseValidationAssert<R extends BaseRuleBuilder<?>> {
*
* @param type The class to whitelist.
*/
@SuppressWarnings("unused")
public static void registerNonBeanType(Class<?> type) {
NON_BEAN_TYPES.add(type);
}
Expand All @@ -48,16 +49,18 @@ public <P> CallBuilder<R, P> of(@NonNull P parameterUnderTest) {
public static final class CallBuilder<R extends BaseRuleBuilder<?>, P> {
private final BaseValidationAssert<R> parent;

@SuppressWarnings("unused")
public R calling(@NonNull Consumer<P> functionToCallWithParameter) {
parent.setFunctionToCallWithParameter(functionToCallWithParameter);
return parent.ruleBuilder;
}

@SuppressWarnings("unused")
public R usingBeanValidation() {
return parent.ruleBuilder;
}

@SuppressWarnings("unchecked")
@SuppressWarnings({"unused", "unchecked"})
public <ID> R calling(
@NonNull BiConsumer<ID, P> functionToCallWithParameter,
@NonNull ID id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class BiggerThanValueSource implements ValueSource {
TYPE_SOURCES.put(ScaledBigDecimal.class, BiggerThanValueSource::getScaledBigDecimalStream);
}

@SuppressWarnings("unchecked")
public static void registerType(Class<?> type, Function<Object[], Stream<?>> source) {
TYPE_SOURCES.put(type, source);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class BlankValueSource implements ValueSource {
);
}

@SuppressWarnings("unused")
public static void registerType(Class<?> type, Function<Object[], Stream<?>> source) {
TYPE_SOURCES.put(type, source);
}
Expand Down
Loading