All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.leakyabstractions.result.assertj.AbstractResultAssert Maven / Gradle / Ivy

There is a newer version: 1.0.0.0
Show newest version
/*
 * Copyright 2024 Guillermo Calvo
 *
 * 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 com.leakyabstractions.result.assertj;

import static com.leakyabstractions.result.assertj.ResultShouldBe.shouldBeFailure;
import static com.leakyabstractions.result.assertj.ResultShouldBe.shouldBeSuccess;
import static com.leakyabstractions.result.assertj.ResultShouldHave.shouldHave;
import static com.leakyabstractions.result.assertj.ResultShouldHave.shouldHaveInstanceOf;
import static com.leakyabstractions.result.assertj.ResultShouldHave.shouldHaveSame;
import static org.assertj.core.api.ObjectAssertProxy.assertWithAssertionState;
import static org.assertj.core.util.Preconditions.checkArgument;

import java.util.function.Consumer;

import org.assertj.core.api.AbstractAssert;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.assertj.core.api.InstanceOfAssertFactory;
import org.assertj.core.api.ObjectAssert;
import org.assertj.core.api.ObjectAssertProxy;
import org.assertj.core.api.WritableAssertionInfo;
import org.assertj.core.internal.Conditions;
import org.assertj.core.internal.StandardComparisonStrategy;
import org.assertj.core.util.CheckReturnValue;

import com.leakyabstractions.result.api.Result;

/**
 * Assertions for {@link Result}.
 *
 * @param  the "self" type of this assertion class.
 * @param  type of the success value contained in the {@link Result}.
 * @param  type of the failure value contained in the {@link Result}.
 * @author Guillermo Calvo
 */
@SuppressWarnings("java:S119") // Type parameter names should comply with a naming convention
abstract class AbstractResultAssert, S, F>
        extends AbstractAssert> {

    protected AbstractResultAssert(Result actual, Class selfType) {
        super(actual, selfType);
    }

    /**
     * Verifies that the actual {@link Result} is successful.
     * 

* Assertions will pass: * *

     *  
     * assertThat(Results.success("yay")).hasSuccess();
     * assertThat(Results.success(123)).hasSuccess();
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure("nay")).hasSuccess();
     * assertThat(Results.failure(123)).hasSuccess();
     * 
     * 
* * @return this assertion object. */ public SELF hasSuccess() { this.assertHasSuccess(); return myself; } /** * Verifies that the actual {@link Result} is a successful result containing the given value. *

* Assertions will pass: * *

     *  
     * assertThat(Results.success("yay")).hasSuccess("yay");
     * assertThat(Results.success(1234)).hasSuccess(1234);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("yay")).hasSuccess("nay");
     * assertThat(Results.failure("yay")).hasSuccess("yay");
     * 
     * 
* * @param expectedValue the expected success value inside the {@link Result}. * @return this assertion object. */ public SELF hasSuccess(S expectedValue) { final S value = this.assertHasSuccess(); this.checkNotNull(expectedValue); if (!StandardComparisonStrategy.instance().areEqual(value, expectedValue)) { throw this.assertionError(shouldHave(this.actual(), expectedValue, value)); } return myself; } /** * Verifies that the actual {@link Result} is a successful result containing the instance given as an argument (i.e. * it must be the same instance). *

* Given: * *

     *  
     * static {
     *     final String FOOBAR = "foobar";
     * }
     * 
     * 
* * Assertions will pass: * *
     *  
     * assertThat(Results.success(FOOBAR)).hasSuccessSameAs(FOOBAR);
     * assertThat(Results.success(10)).hasSuccessSameAs(10);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("yay")).hasSuccessSameAs("nay");
     * assertThat(Results.failure(FOOBAR)).hasSuccessSameAs(FOOBAR);
     * 
     * 
* * @param expectedValue the expected success value inside the {@link Result}. * @return this assertion object. */ public SELF hasSuccessSameAs(S expectedValue) { final S value = this.assertHasSuccess(); this.checkNotNull(expectedValue); if (value != expectedValue) { throw assertionError(shouldHaveSame(this.actual(), expectedValue)); } return myself; } /** * Verifies that the actual {@link Result} is a successful result and invokes the given {@link Consumer} with the * success value for further assertions. *

* Should be used as a way of deeper asserting on the containing object, as further requirement(s) for the value. *

* Assertions will pass: * *

     *  
     * // one requirement
     * assertThat(Results.success(10)).hasSuccessSatisfying(s -> {
     *     assertThat(s).isGreaterThan(9);
     * });
     * assertThat(Results.success("yay")).hasSuccessSatisfying(s -> {
     *     assertThat(s).isEqualTo("yay");
     * });
     *
     * // multiple requirements
     * assertThat(Results.success("hello")).hasSuccessSatisfying(s -> {
     *     assertThat(s).isEqualTo("hello");
     *     assertThat(s).startsWith("h");
     *     assertThat(s).endsWith("o");
     * });
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("yay")).hasSuccessSatisfying(s -> assertThat(s).isEqualTo("nay"));
     * assertThat(Results.failure("yay")).hasSuccessSatisfying(s -> {});
     * 
     * 
* * @param requirement to further assert on the success value held by the {@link Result} * @return this assertion object. */ public SELF hasSuccessSatisfying(Consumer requirement) { final S value = this.assertHasSuccess(); requirement.accept(value); return myself; } /** * Verifies that the actual {@link Result} is a successful result whose success value satisfies the given * {@link Condition}. *

* Given: * *

     *  
     * static {
     *     final Condition<Integer> IS_NEGATIVE = new Condition<>(i -> i < 0, "a negative number");
     * }
     * 
     * 
* * Assertion will pass: * *
     *  
     * assertThat(Results.success(-1)).hasSuccessSatisfying(IS_NEGATIVE);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success(1234)).hasSuccessSatisfying(IS_NEGATIVE);
     * assertThat(Results.failure(-123)).hasSuccessSatisfying(IS_NEGATIVE);
     * 
     * 
* * @param condition the given condition * @return this assertion object. */ public SELF hasSuccessSatisfying(Condition condition) { final S value = this.assertHasSuccess(); Conditions.instance().assertIs(this.info(), value, condition); return myself; } /** * Verifies that the actual {@link Result} is a successful result containing a value that is an instance of the * given class. *

* Assertions will pass: * *

     *  
     * assertThat(Results.success("hello"))
     *         .hasSuccessInstanceOf(String.class)
     *         .hasSuccessInstanceOf(Object.class);
     * assertThat(Results.success(1234)).hasSuccessInstanceOf(Integer.class);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("hello")).hasSuccessInstanceOf(Integer.class);
     * assertThat(Results.failure("hello")).hasSuccessInstanceOf(String.class);
     * 
     * 
* * @param clazz the expected class of the success value inside the {@link Result} * @return this assertion object. */ public SELF hasSuccessInstanceOf(Class clazz) { final S value = this.assertHasSuccess(); this.checkNotNull(clazz); if (!clazz.isInstance(value)) { throw assertionError(shouldHaveInstanceOf(this.actual(), clazz, value)); } return myself; } /** * Verifies that the actual {@link Result} is a successful result and returns an Object assertion that allows * chaining (object) assertions on its success value. *

* Assertions will pass: * *

     *  
     * assertThat(Results.success(0)).hasSuccessThat().isZero();
     * assertThat(Results.success(100)).hasSuccessThat().isGreaterThan(10);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure(0)).hasSuccessThat().isZero();
     * assertThat(Results.success(1)).hasSuccessThat().isGreaterThan(10);
     * 
     * 
* * @return a new {@link ObjectAssert} for assertions chaining on the success value. * @see #hasSuccessThat(InstanceOfAssertFactory) */ @CheckReturnValue public ObjectAssert hasSuccessThat() { final S value = this.assertHasSuccess(); return assertWithAssertionState(myself, value); } /** * Verifies that the actual {@link Result} is a successful result and returns an new assertion instance to chain * assertions on its success value. *

* The {@code assertFactory} parameter allows to specify an {@link InstanceOfAssertFactory}, which is used to get * the assertions narrowed to the factory type. *

* Wrapping the given {@link InstanceOfAssertFactory} with {@link Assertions#as(InstanceOfAssertFactory)} makes the * assertion more readable. *

* Assertions will pass: * *

     *  
     * assertThat(Results.success(0)).hasSuccessThat(as(InstanceOfAssertFactories.INTEGER)).isZero();
     * assertThat(Results.success("hello")).hasSuccessThat(as(InstanceOfAssertFactories.STRING)).startsWith("h");
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("hello")).hasSuccessThat(as(InstanceOfAssertFactories.INTEGER)).isZero();
     * assertThat(Results.failure("hello")).hasSuccessThat(as(InstanceOfAssertFactories.STRING)).startsWith("h");
     * 
     * 
* * @param the type of the resulting {@code Assert} * @param assertFactory the factory which verifies the type and creates the new {@code Assert} * @return a new narrowed {@link ObjectAssertProxy} instance for assertions chaining on the success value */ @CheckReturnValue public > T hasSuccessThat( InstanceOfAssertFactory assertFactory) { final S value = this.assertHasSuccess(); return assertWithAssertionState(myself, value).asInstanceOf(assertFactory); } /** * Verifies that the actual {@link Result} is failed. *

* Assertions will pass: * *

     *  
     * assertThat(Results.failure("yay")).hasFailure();
     * assertThat(Results.failure(123)).hasFailure();
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("nay")).hasFailure();
     * assertThat(Results.success(123)).hasFailure();
     * 
     * 
* * @return this assertion object. */ public SELF hasFailure() { this.assertHasFailure(); return myself; } /** * Verifies that the actual {@link Result} is a failed result containing the given value. *

* Assertions will pass: * *

     *  
     * assertThat(Results.failure("yay")).hasFailure("yay");
     * assertThat(Results.failure(1234)).hasFailure(1234);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure("yay")).hasFailure("nay");
     * assertThat(Results.success("yay")).hasFailure("yay");
     * 
     * 
* * @param expectedValue the expected failure value inside the {@link Result}. * @return this assertion object. */ public SELF hasFailure(F expectedValue) { final F value = this.assertHasFailure(); this.checkNotNull(expectedValue); if (!StandardComparisonStrategy.instance().areEqual(value, expectedValue)) { throw this.assertionError(shouldHave(this.actual(), expectedValue, value)); } return myself; } /** * Verifies that the actual {@link Result} is a failed result containing the instance given as an argument (i.e. it * must be the same instance). *

* Given: * *

     *  
     * static {
     *     final String FOOBAR = "foobar";
     * }
     * 
     * 
* * Assertions will pass: * *
     *  
     * assertThat(Results.failure(FOOBAR)).hasFailureSameAs(FOOBAR);
     * assertThat(Results.failure(10)).hasFailureSameAs(10);
     * 
     * 
* * Assertions will fail: * *
     *  
     * // not equal:
     * assertThat(Results.failure("yay")).hasFailureSameAs("nay");
     * assertThat(Results.success(FOOBAR)).hasFailureSameAs(FOOBAR);
     * 
     * 
* * @param expectedValue the expected failure value inside the {@link Result}. * @return this assertion object. */ public SELF hasFailureSameAs(F expectedValue) { final F value = this.assertHasFailure(); this.checkNotNull(expectedValue); if (value != expectedValue) { throw assertionError(shouldHaveSame(this.actual(), expectedValue)); } return myself; } /** * Verifies that the actual {@link Result} is a failed result and invokes the given {@link Consumer} with the * failure value for further assertions. *

* Should be used as a way of deeper asserting on the containing object, as further requirement(s) for the value. *

* Assertions will pass: * *

     *  
     * // one requirement
     * assertThat(Results.failure(10)).hasFailureSatisfying(f -> {
     *     assertThat(f).isGreaterThan(9);
     * });
     * assertThat(Results.failure("yay")).hasFailureSatisfying(f -> {
     *     assertThat(f).isEqualTo("yay");
     * });
     *
     * // multiple requirements
     * assertThat(Results.failure("hello")).hasFailureSatisfying(f -> {
     *     assertThat(f).isEqualTo("hello");
     *     assertThat(f).startsWith("h");
     *     assertThat(f).endsWith("o");
     * });
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success("hello")).hasSuccessSatisfying(s -> assertThat(s).isEqualTo("hello"));
     * assertThat(Results.failure("hello")).hasSuccessSatisfying(s -> {});
     * 
     * 
* * @param requirement to further assert on the failure value held by the {@link Result} * @return this assertion object. */ public SELF hasFailureSatisfying(Consumer requirement) { final F value = this.assertHasFailure(); requirement.accept(value); return myself; } /** * Verifies that the actual {@link Result} is a failed result whose failure value satisfies the given * {@link Condition}. *

* Given: * *

     *  
     * static {
     *     final Condition<Integer> IS_NEGATIVE = new Condition<>(i -> i < 0, "a negative number");
     * }
     * 
     * 
* * Assertion will pass: * *
     *  
     * assertThat(Results.failure(-1)).hasFailureSatisfying(IS_NEGATIVE);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure(1234)).hasFailureSatisfying(IS_NEGATIVE);
     * assertThat(Results.success(-123)).hasFailureSatisfying(IS_NEGATIVE);
     * 
     * 
* * @param condition the given condition * @return this assertion object. */ public SELF hasFailureSatisfying(Condition condition) { final F value = this.assertHasFailure(); Conditions.instance().assertIs(this.info(), value, condition); return myself; } /** * Verifies that the actual {@link Result} is a failed result containing a value that is an instance of the given * class. *

* Assertions will pass: * *

     *  
     * assertThat(Results.failure("hello"))
     *         .hasFailureInstanceOf(String.class)
     *         .hasFailureInstanceOf(Object.class);
     * assertThat(Results.failure(123)).hasSuccessInstanceOf(Integer.class);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure("hello")).hasFailureInstanceOf(Integer.class);
     * assertThat(Results.success("hello")).hasFailureInstanceOf(String.class);
     * 
     * 
* * @param clazz the expected class of the failure value inside the {@link Result} * @return this assertion object. */ public SELF hasFailureInstanceOf(Class clazz) { final F value = this.assertHasFailure(); this.checkNotNull(clazz); if (!clazz.isInstance(value)) { throw assertionError(shouldHaveInstanceOf(this.actual(), clazz, value)); } return myself; } /** * Verifies that the actual {@link Result} is a failed result and returns an Object assertion that allows chaining * (object) assertions on its failure value. *

* Assertions will pass: * *

     *  
     * assertThat(Results.failure(10)).hasFailureThat().isZero();
     * assertThat(Results.failure(100)).hasFailureThat().isGreaterThan(10);
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.success(0)).hasFailureThat().isZero();
     * assertThat(Results.failure(1)).hasFailureThat().isGreaterThan(10);
     * 
     * 
* * @return a new {@link ObjectAssert} for assertions chaining on the success value. * @see #hasSuccessThat(InstanceOfAssertFactory) */ @CheckReturnValue public ObjectAssert hasFailureThat() { final F value = this.assertHasFailure(); return assertWithAssertionState(myself, value); } /** * Verifies that the actual {@link Result} is a failed result and returns a new assertion instance to chain * assertions on its failure value. *

* The {@code assertFactory} parameter allows to specify an {@link InstanceOfAssertFactory}, which is used to get * the assertions narrowed to the factory type. *

* Wrapping the given {@link InstanceOfAssertFactory} with {@link Assertions#as(InstanceOfAssertFactory)} makes the * assertion more readable. *

* Assertions will pass: * *

     *  
     * assertThat(Results.failure(0)).hasFailureThat(as(InstanceOfAssertFactories.INTEGER)).isZero();
     * assertThat(Results.failure("hello")).hasFailureThat(as(InstanceOfAssertFactories.STRING)).startsWith("h");
     * 
     * 
* * Assertions will fail: * *
     *  
     * assertThat(Results.failure("hello")).hasFailureThat(as(InstanceOfAssertFactories.INTEGER)).isZero();
     * assertThat(Results.success("hello")).hasFailureThat(as(InstanceOfAssertFactories.STRING)).startsWith("h");
     * 
     * 
* * @param the type of the resulting {@code Assert} * @param assertFactory the factory which verifies the type and creates the new {@code Assert} * @return a new narrowed {@link ObjectAssertProxy} instance for assertions chaining on the success value */ @CheckReturnValue public > T hasFailureThat( InstanceOfAssertFactory assertFactory) { final F value = this.assertHasFailure(); return assertWithAssertionState(myself, value).asInstanceOf(assertFactory); } private void checkNotNull(Object expectedValue) { checkArgument(expectedValue != null, "The expected value should not be ."); } private S assertHasSuccess() { isNotNull(); return this.actual() .getSuccess() .orElseThrow(() -> this.assertionError(shouldBeSuccess(this.actual()))); } private F assertHasFailure() { isNotNull(); return this.actual() .getFailure() .orElseThrow(() -> this.assertionError(shouldBeFailure(this.actual()))); } // Class members annotated with "@VisibleForTesting" should not be accessed from production code @SuppressWarnings("java:S5803") private Result actual() { return this.actual; } @SuppressWarnings("java:S5803") private WritableAssertionInfo info() { return this.info; } }