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
Expand Up @@ -76,8 +76,9 @@ public Result<T> ifFailure(CheckedConsumer<Failure<T>> action)
return action.accept(this);
} catch (Exception ex) {
return new Failure<>(FailureDescription.of()
.type(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.type(FailureDescription.GenericFailureType.GENERIC_CONSUMER_EXCEPTION)
.cause(ex)
.precedingFailure(this.description)
.build());
}
}
Expand Down Expand Up @@ -108,6 +109,7 @@ public Result<T> orElseGet(CheckedSupplier<Result<T>> supplier)
return new Failure<>(FailureDescription.of()
.type(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.cause(ex)
.precedingFailure(this.description)
.build());
}
catch (Exception e)
Expand All @@ -117,10 +119,12 @@ public Result<T> orElseGet(CheckedSupplier<Result<T>> supplier)
case RuntimeException ex -> new Failure<>(FailureDescription.of()
.type(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.cause(ex)
.precedingFailure(this.description)
.build());
case Exception ex -> new Failure<>(FailureDescription.of()
.type(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.cause(ex)
.precedingFailure(this.description)
.build());
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@ public enum GenericFailureType implements FailureType
GENERIC("generic-failure", ""),
GENERIC_EXCEPTION("generic-checked-exception-failure", ""),
GENERIC_INTERRUPTED_EXCEPTION("generic-interrupted-exception-failure", ""),
GENERIC_RUNTIME_EXCEPTION("generic-runtime-exception-failure", "")
GENERIC_RUNTIME_EXCEPTION("generic-runtime-exception-failure", ""),
GENERIC_CONSUMER_EXCEPTION("generic-consumer-exception-failure", "")
;

private final String title;
Expand Down
99 changes: 99 additions & 0 deletions endeavour/src/test/java/org/saltations/endeavour/FailureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,105 @@ void whenIfFailureThenTakesAction() throws Exception {
assertSame(failure, result, "Should return same result");
}

@Test
@Order(62)
void whenIfFailureActionThrowsExceptionThenReturnsFailureWithPrecedingFailure()
{
Exception testException = new Exception("Test exception");

Result<Long> result = failure.ifFailure(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_CONSUMER_EXCEPTION)
.hasCause()
.hasCauseOfType(Exception.class)
.hasCauseWithMessage("Test exception");

// Verify preceding failure is preserved
Failure<Long> resultFailure = (Failure<Long>) result;
assertTrue(resultFailure.description().hasPrecedingFailure(), "Should have preceding failure");
assertEquals(((Failure<Long>)failure).description(), resultFailure.description().getPrecedingFailure(), "Preceding failure should match original failure");
}

@Test
@Order(63)
void whenIfFailureActionThrowsRuntimeExceptionThenReturnsFailureWithPrecedingFailure()
{
RuntimeException testException = new RuntimeException("Test runtime exception");

Result<Long> result = failure.ifFailure(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_CONSUMER_EXCEPTION)
.hasCause()
.hasCauseOfType(RuntimeException.class)
.hasCauseWithMessage("Test runtime exception");

Failure<Long> resultFailure = (Failure<Long>) result;
assertTrue(resultFailure.description().hasPrecedingFailure(), "Should have preceding failure");
}

@Test
@Order(64)
void whenIfFailureWithNullActionThenThrowsNullPointerException()
{
assertThrows(NullPointerException.class, () -> {
failure.ifFailure(null);
});
}

@Test
@Order(65)
void whenIfFailureReturnsDifferentResultThenUsesReturnedResult()
{
// CheckedConsumer<Failure<Long>>.accept returns Failure<Long>
// The ifFailure method returns action.accept(this), which returns the Failure
Result<Long> result = failure.ifFailure(x -> {
// Return a different Failure instance
return new Failure<>(FailureDescription.of()
.type(FailureDescription.GenericFailureType.GENERIC)
.detail("Different failure")
.build());
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC);
assertEquals("Different failure", ((Failure<Long>)result).getDetail(), "Should have different detail");
}

@Test
@Order(66)
void whenReduceFailureFunctionThrowsExceptionThenReturnsEmptyOptional()
{
Exception testException = new Exception("Test exception");

Optional<String> result = failure.reduce(
v -> "Success",
f -> { throw testException; }
);

assertTrue(result.isEmpty(), "Should return empty Optional on exception");
}

@Test
@Order(67)
void whenReduceFailureFunctionReturnsNullThenReturnsEmptyOptional()
{
Optional<String> result = failure.reduce(
v -> "Success",
f -> null
);

assertTrue(result.isEmpty(), "Should return empty Optional when function returns null");
}


@Test
@Order(70)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,47 @@ void whenTakingActionIfSuccessThenTakesAction() throws Exception
assertSame(qualSuccess, result, "Should return same result");
}

@Test
@Order(61)
void whenIfSuccessActionThrowsExceptionThenReturnsFailure()
{
Exception testException = new Exception("Test exception");

Result<Long> result = qualSuccess.ifSuccess(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.hasCause()
.hasCauseOfType(Exception.class)
.hasCauseWithMessage("Test exception");
}

@Test
@Order(62)
void whenIfSuccessWithNullActionThenThrowsNullPointerException()
{
assertThrows(NullPointerException.class, () -> {
qualSuccess.ifSuccess(null);
});
}

@Test
@Order(63)
void whenReduceSuccessFunctionThrowsExceptionThenReturnsEmptyOptional()
{
Exception testException = new Exception("Test exception");

Optional<String> result = qualSuccess.reduce(
v -> { throw testException; },
f -> "Failure"
);

assertTrue(result.isEmpty(), "Should return empty Optional on exception");
}


@Test
@Order(61)
Expand Down
108 changes: 108 additions & 0 deletions endeavour/src/test/java/org/saltations/endeavour/QuantSuccessTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,88 @@ void whenTakingActionOnSuccessThenTakesAction() throws Exception
assertSame(value, result, "Should return same result");
}

@Test
@Order(41)
void whenIfSuccessActionThrowsExceptionThenReturnsFailure()
{
Exception testException = new Exception("Test exception");

Result<Long> result = value.ifSuccess(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.hasCause()
.hasCauseOfType(Exception.class)
.hasCauseWithMessage("Test exception");
}

@Test
@Order(42)
void whenIfSuccessActionThrowsRuntimeExceptionThenReturnsFailure()
{
RuntimeException testException = new RuntimeException("Test runtime exception");

Result<Long> result = value.ifSuccess(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.hasCause()
.hasCauseOfType(RuntimeException.class)
.hasCauseWithMessage("Test runtime exception");
}

@Test
@Order(43)
void whenIfSuccessActionThrowsInterruptedExceptionThenReturnsFailure()
{
InterruptedException testException = new InterruptedException("Test interrupted");

Result<Long> result = value.ifSuccess(x -> {
throw testException;
});

assertThat(result)
.isFailure()
.hasFailureType(FailureDescription.GenericFailureType.GENERIC_EXCEPTION)
.hasCause()
.hasCauseOfType(InterruptedException.class)
.hasCauseWithMessage("Test interrupted");
}

@Test
@Order(44)
void whenIfSuccessWithNullActionThenThrowsNullPointerException()
{
assertThrows(NullPointerException.class, () -> {
value.ifSuccess(null);
});
}

@Test
@Order(45)
void whenIfSuccessReturnsDifferentResultThenUsesReturnedResult()
{
// CheckedConsumer<Success<Long>>.accept returns Success<Long>
// We need to return a Success<Long>, which Try.success() provides
// Since Success<Long> extends Result<Long>, the method can return it
Result<Long> result = value.ifSuccess(x -> {
// Return a different Success instance - Try.success returns Result but we know it's Success
Success<Long> newSuccess = (Success<Long>) Try.success(9999L);
return newSuccess;
});

assertThat(result)
.isSuccess()
.isQuantSuccess()
.hasValue(9999L);
}

@Test
@Order(60)
void whenTransformingResultOnFailureThenReturnsExistingSuccess()
Expand Down Expand Up @@ -209,6 +291,32 @@ void whenTransformingThenGivesTransformedResult()
assertEquals("Success with value", result.get(), "Transformed to 'Success with value'");
}

@Test
@Order(101)
void whenReduceSuccessFunctionThrowsExceptionThenReturnsEmptyOptional()
{
Exception testException = new Exception("Test exception");

Optional<String> result = value.reduce(
v -> { throw testException; },
f -> "Failure"
);

assertTrue(result.isEmpty(), "Should return empty Optional on exception");
}

@Test
@Order(102)
void whenReduceReturnsNullThenReturnsEmptyOptional()
{
Optional<String> result = value.reduce(
v -> null,
f -> "Failure"
);

assertTrue(result.isEmpty(), "Should return empty Optional when function returns null");
}

@Test
@Order(110)
void optReturnsOptionalWithValueForSuccess() {
Expand Down