package com.millross.blog.async;

import org.junit.Test;

import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

/**
 *
 */
public class ExceptionsInThenApplyTest extends CompletableFutureTestBase{

    /**
     * Note that while we throw an IntentionalException, get() causes it to be wrapped in an ExecutionException, in
     * effect, get acts as a subsequent completion stage from the point of view of exception propagation.
     * @throws Exception
     */
    @Test(expected = CompletionException.class)
    public void exceptionCallingThenApply() throws Exception {
        final CompletableFuture<Integer> future = CompletableFuture.supplyAsync(delayedValueSupplier(1), executor)
                .thenApply(i -> {
                    throw new IntentionalException();
                });
        assertThat(future.join(), is(4));

    }

    @Test(expected = CompletionException.class)
    public void natureOfExceptionCallingThenApply() throws Exception {
        final AtomicReference<Throwable> thrownException = new AtomicReference<>(null);
        final CompletableFuture<Void> future = CompletableFuture.supplyAsync(delayedValueSupplier(1), executor)
                .thenApply(i -> {
                    throw new IntentionalException();
                })
                // whenComplete allows us to retain information about exceptional completion
                .whenComplete((v, t) -> Optional.ofNullable(t)
                        // Let's extract the exception from the CompletionStage and record it
                        .ifPresent(thrownException::set))
                // thenAccept effectively loses information about exceptional completion
                .thenAccept(o -> assertThat((thrownException.get() instanceof IntentionalException), is(true)));
        future.join();

    }

}

@@ -231,106 +231,8 @@
 e;%0A%0A
-import static org.hamcrest.CoreMatchers.is;%0Aimport static org.hamcrest.MatcherAssert.assertThat;%0A%0A
 /**%0A
@@ -593,34 +593,35 @@
 (expected = 
-Comple
+Inten
 tion
+al
 Exception.cl
@@ -672,33 +672,33 @@
 ly() throws 
-Exception
+Throwable
  %7B%0A        f
@@ -911,24 +911,25 @@
     %7D);%0A
+%0A
         
 assertTh
@@ -924,41 +924,126 @@
     
-assertThat(future.join(), is(4));
+try %7B%0A            future.join();%0A        %7D catch (CompletionException ex) %7B%0A            throw ex.getCause();%0A        %7D
 %0A%0A  
@@ -1115,17 +1115,9 @@
 oid 
-natureOfE
+e
 xcep
@@ -1128,32 +1128,55 @@
 CallingThenApply
+AsObservedFromNextStage
 () throws Except
@@ -1169,25 +1169,25 @@
  throws 
-Exception
+Throwable
  %7B%0A     
@@ -1305,12 +1305,14 @@
 ure%3C
-Void
+Object
 %3E fu
@@ -1506,19 +1506,17 @@
         
-// 
+.
 whenComp
@@ -1523,284 +1523,147 @@
 lete
- allows us to retain information about exceptional completion%0A                .whenComplete((v, t) -%3E Optional.ofNullable(t)%0A                        // Let's extract the exception from the CompletionStage and record it%0A                        .ifPresent(thrownException::set))
+((v, t) -%3E %7B%0A                    if (t != null) %7B%0A                        thrownException.set(t);%0A                    %7D%0A                %7D);
 %0A   
@@ -1671,93 +1671,84 @@
     
- 
+try %7B%0A
        
-// thenAccept effectively loses information about exceptional completion%0A    
+     future.join();%0A        %7D catch (CompletionException ex) %7B%0A
     
@@ -1759,128 +1759,80 @@
     
-.
 th
-enAccept(o -%3E assertThat((thrownException.get() instanceof IntentionalException), is(true)));%0A        future.join();%0A
+row (Optional.ofNullable(thrownException.get()).orElse(ex));%0A        %7D
 %0A   

Add exception propagation demo for thenApply