Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow compareCollections to return an implementation of Collection #110

Merged
merged 3 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
Expand Up @@ -107,9 +107,10 @@ public T compare(final Supplier<T> currentFlow, final Supplier<T> newFlow) {
* @param newFlow A supplier that return the result of the new service call
* that you want to start using.
* @param clazz The model that the current and new flow should be mapped to for comparison.
* @param <C> The type of collection to compare, for example a List
* @return This will always return the value of currentFlow supplier.
*/
public Collection<T> compareCollections(final Supplier<Collection<T>> currentFlow, final Supplier<Collection<T>> newFlow, final Class<T> clazz) {
public <C extends Collection<T>> C compareCollections(final Supplier<C> currentFlow, final Supplier<C> newFlow, final Class<T> clazz) {
GuusdeWit marked this conversation as resolved.
Show resolved Hide resolved
final var currentFlowResponse = currentFlow.get();
doShadowFlow(() -> javers.compareCollections(currentFlowResponse, newFlow.get(), clazz));

Expand Down Expand Up @@ -157,9 +158,10 @@ public Mono<T> compare(final Mono<T> currentFlow, final Mono<T> newFlow) {
* @param newFlow A mono that returns the result of the new service call
* that you want to start using.
* @param clazz The model that the current and new flow should be mapped to for comparison.
* @param <C> The type of collection to compare, for example a List
* @return This will always return the mono of currentFlow.
*/
public Mono<Collection<T>> compareCollections(final Mono<? extends Collection<T>> currentFlow, final Mono<? extends Collection<T>> newFlow, final Class<T> clazz) {
public <C extends Collection<T>> Mono<C> compareCollections(final Mono<? extends C> currentFlow, final Mono<? extends C> newFlow, final Class<T> clazz) {
final var callNewFlow = shouldCallNewFlow();

return Mono.deferContextual(contextView ->
Expand Down Expand Up @@ -339,7 +341,7 @@ public ShadowFlow<T> build() {
private int validatePercentage(final int percentage) {
if (percentage < ZERO || percentage > HUNDRED) {
logger.error("Invalid percentage! Must be within the range of 0 and 100. Got {}. " +
"The shadow flow will be effectively disabled by setting it to 0%.", percentage);
"The shadow flow will be effectively disabled by setting it to 0%.", percentage);
return ZERO;
}

Expand Down
69 changes: 57 additions & 12 deletions src/test/java/io/github/rabobank/shadow_tool/ShadowFlowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
Expand All @@ -30,6 +33,7 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -74,10 +78,10 @@ void shouldAlwaysReturnCurrentFlow(final ExecutorService executorService) {
@ParameterizedTest
@MethodSource("executorArguments")
void shouldAlwaysReturnCurrentFlowReactive(final ExecutorService executorService) {
var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
final var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
.withExecutorService(executorService).build();

var result = shadowFlow.compare(
final var result = shadowFlow.compare(
Mono.just(dummyObjectA),
Mono.just(dummyObjectB)
);
Expand All @@ -88,10 +92,10 @@ void shouldAlwaysReturnCurrentFlowReactive(final ExecutorService executorService
@ParameterizedTest
@MethodSource("executorArguments")
void shouldCallCurrentFlowOnlyOnce(final ExecutorService executorService) {
var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
final var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
.withExecutorService(executorService).build();

var callCounter = new AtomicInteger(0);
final var callCounter = new AtomicInteger(0);
shadowFlow.compare(
Mono.fromCallable(() -> {
callCounter.incrementAndGet();
Expand All @@ -107,7 +111,7 @@ void shouldCallCurrentFlowOnlyOnce(final ExecutorService executorService) {
void shouldAlwaysReturnCurrentFlowReactiveWithErrorInShadowFlow() {
final List<Throwable> exceptions = new ArrayList<>();

var result = createBlockingShadowFlow(100).compare(
final var result = createBlockingShadowFlow(100).compare(
Mono.just(dummyObjectA),
Mono.<DummyObject>error(new Exception("Something happened in the shadow flow!")).doOnError(exceptions::add)
);
Expand All @@ -117,13 +121,14 @@ void shouldAlwaysReturnCurrentFlowReactiveWithErrorInShadowFlow() {
assertEquals("Something happened in the shadow flow!", exceptions.get(0).getMessage());
}

@SuppressWarnings("ReactiveStreamsUnusedPublisher")
@ParameterizedTest
@MethodSource("executorArguments")
void shouldAlwaysReturnCurrentFlowReactiveWithErrorInCurrentFlow(final ExecutorService executorService) {
var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
final var shadowFlow = new ShadowFlowBuilder<DummyObject>(100)
.withExecutorService(executorService).build();

var result = shadowFlow.compare(
final var result = shadowFlow.compare(
Mono.error(new IllegalArgumentException("Something happened in the current flow!")),
Mono.just(dummyObjectB)
);
Expand All @@ -143,7 +148,7 @@ void verifyDifferencesAreLoggedReactive() {

@Test
void shouldRunShadowFlowAsynchronouslyByDefaultReactive() {
Executable shadowCall = () -> new ShadowFlowBuilder<DummyObject>(100).build().compare(
final Executable shadowCall = () -> new ShadowFlowBuilder<DummyObject>(100).build().compare(
Mono.just(dummyObjectA),
Mono.just(dummyObjectB).delayElement(Duration.ofSeconds(5))
).block(Duration.ofMillis(100));
Expand All @@ -164,8 +169,8 @@ void shouldRunShadowFlowMonoCollections() {

@Test
void shouldRunShadowFlowMonoCollectionsWithVariables() {
var asyncCallA = List.of(dummyObjectA);
var asyncCallB = List.of(dummyObjectB);
final var asyncCallA = List.of(dummyObjectA);
final var asyncCallB = List.of(dummyObjectB);

createBlockingShadowFlow(100).compareCollections(
Mono.just(asyncCallA),
Expand All @@ -179,8 +184,8 @@ void shouldRunShadowFlowMonoCollectionsWithVariables() {
// Same test, slightly different input type to validate "? extends Collection<T>"
@Test
void shouldRunShadowFlowMonoWithCollectionExtendWithVariables() {
var asyncCallA = Mono.just(List.of(dummyObjectA));
var asyncCallB = Mono.just(List.of(dummyObjectB));
final var asyncCallA = Mono.just(List.of(dummyObjectA));
final var asyncCallB = Mono.just(List.of(dummyObjectB));

createBlockingShadowFlow(100).compareCollections(
asyncCallA,
Expand Down Expand Up @@ -308,6 +313,46 @@ void shouldBeAbleToCompareCollectionOfObjects() {
assertThatLogContains("The following differences were found: place, madrigals");
}

@ParameterizedTest
@ValueSource(classes = {ArrayList.class, HashSet.class})
void typeOfCollectionListShouldBeTheResult(final Class<? extends Collection<?>> clazz) {
final var shadowFlow = createBlockingShadowFlow(100);

final var result = shadowFlow.compareCollections(
() -> createDummyCollectionOfType(clazz, dummyObjectA),
() -> createDummyCollectionOfType(clazz, dummyObjectB),
DummyObject.class
);

assertInstanceOf(clazz, result);
GuusdeWit marked this conversation as resolved.
Show resolved Hide resolved
}

@ParameterizedTest
@ValueSource(classes = {ArrayList.class, HashSet.class})
void typeOfCollectionShouldBeTheResultForMonos(final Class<? extends Collection<?>> clazz) {
final var asyncCallA = Mono.just(createDummyCollectionOfType(clazz, dummyObjectA));
final var asyncCallB = Mono.just(createDummyCollectionOfType(clazz, dummyObjectB));

final var result = createBlockingShadowFlow(100).compareCollections(
asyncCallA,
asyncCallB,
DummyObject.class
).block();

assertInstanceOf(clazz, result);
}

@SuppressWarnings("unchecked")
private static Collection<DummyObject> createDummyCollectionOfType(final Class<?> clazz, final DummyObject dummyObjectA) {
try {
final var collection = (Collection<DummyObject>) clazz.getDeclaredConstructor().newInstance();
collection.add(dummyObjectA);
return collection;
} catch (final Exception e) {
throw new RuntimeException(e);
}
}

private ShadowFlow<DummyObject> createBlockingShadowFlow(final int percentage) {
return new ShadowFlowBuilder<DummyObject>(percentage)
.withExecutorService(new SameThreadExecutorService())
Expand Down