Skip to content

Commit

Permalink
merge: #8814
Browse files Browse the repository at this point in the history
8814: [Backport stable/1.3] Extend Either/EitherAssert capabilities r=npepinpe a=npepinpe

## Description

This PR backports #8797 to stable/1.3, which is necessary to in the end backport the fix for #5525. There were some conflicts which I corrected with the last commit (one extra dependency pulled).

## Related issues

backports #8797 



Co-authored-by: Nicolas Pepin-Perreault <nicolas.pepin-perreault@camunda.com>
Co-authored-by: Nicolas Pepin-Perreault <43373+npepinpe@users.noreply.github.com>
  • Loading branch information
3 people committed Feb 21, 2022
2 parents 7c7bb32 + f709d6c commit d19ab52
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
import io.camunda.zeebe.protocol.impl.encoding.ExecuteQueryResponse;
import io.camunda.zeebe.protocol.record.ErrorCode;
import io.camunda.zeebe.protocol.record.ValueType;
import io.camunda.zeebe.test.util.asserts.EitherAssert;
import io.camunda.zeebe.transport.ServerOutput;
import io.camunda.zeebe.util.Either;
import io.camunda.zeebe.util.EitherAssert;
import io.camunda.zeebe.util.buffer.BufferUtil;
import io.camunda.zeebe.util.sched.ActorScheduler;
import java.util.Optional;
Expand Down
6 changes: 6 additions & 0 deletions test-util/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@
<artifactId>netty-common</artifactId>
</dependency>

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,95 @@

import io.camunda.zeebe.util.Either;
import org.assertj.core.api.AbstractObjectAssert;
import org.assertj.core.api.AssertFactory;
import org.assertj.core.api.InstanceOfAssertFactory;
import org.assertj.core.api.ObjectAssert;

/**
* AssertJ assertions to help you test {@link Either} instances. Typically, you will check if it is
* of the expected side using {@link #isLeft()} or {@link #isRight()}, and then extract that value
* using {@link #left()} and {@link #right()} to assert properties on the actual result. You can
* further chain these with a standard {@link #asInstanceOf(InstanceOfAssertFactory)} to get a more
* specific type.
*
* @param <L> the left type
* @param <R> the right type
*/
public final class EitherAssert<L, R>
extends AbstractObjectAssert<EitherAssert<L, R>, Either<L, R>> {
public EitherAssert(final Either<L, R> actual) {
super(actual, EitherAssert.class);
}

/**
* Convenience factory method which you can use as an {@link AssertFactory}.
*
* @param actual the actual object under test
* @param <L> the left type
* @param <R> the right type
* @return an instance of {@link EitherAssert} on {@code actual}
*/
public static <L, R> EitherAssert<L, R> assertThat(final Either<L, R> actual) {
return new EitherAssert<>(actual);
}

/**
* Fails if {@link #actual} has no right member or is null.
*
* @return itself for chaining
*/
public EitherAssert<L, R> isRight() {
isNotNull();

if (actual.isLeft()) {
failWithMessage("Expected <%s> to be right, but was left <%s>", actual, actual.getLeft());
}

return myself;
}

/**
* Extracts the right member of {@link #actual} and returns an {@link ObjectAssert} wrapping it.
* Fails if {@link #actual} has no right member or is null.
*
* @return a new {@link ObjectAssert} around the right member of {@link #actual}
*/
public ObjectAssert<R> right() {
isNotNull();

// use asInstanceOf to preserve the current assertion info and description
return asInstanceOf(
new InstanceOfAssertFactory<>(
Either.Right.class, object -> new ObjectAssert<>(actual.get())));
}

/**
* Fails if {@link #actual} has no left member or is null.
*
* @return itself for chaining
*/
public EitherAssert<L, R> isLeft() {
isNotNull();

if (actual.isRight()) {
failWithMessage("Expected <%s> to be left, but was right <%s>", actual, actual.get());
}

return myself;
}

/**
* Extracts the left member of {@link #actual} and returns an {@link ObjectAssert} wrapping it.
* Fails if {@link #actual} has no left member or is null.
*
* @return a new {@link ObjectAssert} around the left member of {@link #actual}
*/
public ObjectAssert<L> left() {
isNotNull();

// use asInstanceOf to preserve the current assertion info and description
return asInstanceOf(
new InstanceOfAssertFactory<>(
Either.Left.class, object -> new ObjectAssert<>(actual.getLeft())));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Zeebe Community License 1.1. You may not use this file
* except in compliance with the Zeebe Community License 1.1.
*/
package io.camunda.zeebe.test.util.asserts;

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

import io.camunda.zeebe.util.Either;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

@Execution(ExecutionMode.CONCURRENT)
final class EitherAssertTest {

/**
* The test cases must be specified as static methods, but pre-Java 17, it is not allowed to have
* static methods in inner classes.
*/
private static Stream<Arguments> failOnNullCases() {
return Stream.of(
of("isLeft", EitherAssert::isLeft),
of("isRight", EitherAssert::isRight),
of("left", EitherAssert::left),
of("right", EitherAssert::right));
}

/** Convenience method to infer the type of the consumer in the test cases above. */
private static Arguments of(
final String testName, final Consumer<EitherAssert<Object, Object>> assertion) {
return Arguments.of(testName, assertion);
}

@Nested
final class RightTest {
private final Either<Object, Object> actual = Either.right("right");
private final EitherAssert<Object, Object> assertion = EitherAssert.assertThat(actual);

@Test
void shouldFailIsLeftOnRight() {
assertThatCode(assertion::isLeft).isInstanceOf(AssertionError.class);
}

@Test
void shouldFailLeftOnRight() {
assertThatCode(assertion::left).isInstanceOf(AssertionError.class);
}

@Test
void isRightOnRight() {
assertion.isRight();
}

@Test
void shouldExtractRightOnRight() {
assertion.right().isEqualTo("right");
}
}

@Nested
final class LeftTest {
private final Either<Object, Object> actual = Either.left("left");
private final EitherAssert<Object, Object> assertion = EitherAssert.assertThat(actual);

@Test
void shouldFailIsRightOnLeft() {
assertThatCode(assertion::isRight).isInstanceOf(AssertionError.class);
}

@Test
void shouldFailRightOnLeft() {
assertThatCode(assertion::right).isInstanceOf(AssertionError.class);
}

@Test
void isLeftOnLeft() {
assertion.isLeft();
}

@Test
void shouldExtractLeftOnLeft() {
assertion.left().isEqualTo("left");
}
}

@Nested
final class FailOnNullTest {
private final EitherAssert<Object, Object> eitherAssert = EitherAssert.assertThat(null);

@ParameterizedTest(name = "{0}")
@MethodSource("io.camunda.zeebe.test.util.asserts.EitherAssertTest#failOnNullCases")
void shouldFailOnNull(
@SuppressWarnings("unused") final String testName,
final Consumer<EitherAssert<Object, Object>> assertion) {
assertThatCode(() -> assertion.accept(eitherAssert)).isInstanceOf(AssertionError.class);
}
}
}
18 changes: 18 additions & 0 deletions util/src/main/java/io/camunda/zeebe/util/Either.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,14 @@ Collector<Either<L, R>, Tuple<List<L>, List<R>>, Either<List<L>, List<R>>> colle
*/
R get();

/**
* Returns the right value, or a default value if this is a {@link Left}.
*
* @param defaultValue the default value
* @return the right value, or the default value if this is a {@link Left}
*/
R getOrElse(R defaultValue);

/**
* Returns the left value, if this is a {@link Left}.
*
Expand Down Expand Up @@ -308,6 +316,11 @@ public R get() {
return value;
}

@Override
public R getOrElse(final R defaultValue) {
return value;
}

@Override
public L getLeft() {
throw new NoSuchElementException("Expected a left, but this is right");
Expand Down Expand Up @@ -397,6 +410,11 @@ public R get() {
throw new NoSuchElementException("Expected a right, but this is left");
}

@Override
public R getOrElse(final R defaultValue) {
return defaultValue;
}

@Override
public L getLeft() {
return value;
Expand Down
Loading

0 comments on commit d19ab52

Please sign in to comment.