// Path: src/test/java/org/dataloader/fixtures/CaffeineValueCache.java
// public class CaffeineValueCache implements ValueCache<String, Object> {
// 
//     public final Cache<String, Object> cache;
// 
//     public CaffeineValueCache(Cache<String, Object> cache) {
//         this.cache = cache;
//     }
// 
//     @Override
//     public CompletableFuture<Object> get(String key) {
//         Object value = cache.getIfPresent(key);
//         if (value == null) {
//             // we use get exceptions here to indicate not in cache
//             return CompletableFutureKit.failedFuture(new RuntimeException(key + " not present"));
//         }
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Object> set(String key, Object value) {
//         cache.put(key, value);
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Void> delete(String key) {
//         cache.invalidate(key);
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     @Override
//     public CompletableFuture<Void> clear() {
//         cache.invalidateAll();
//         return CompletableFuture.completedFuture(null);
//     }
// }
// 
// Path: src/test/java/org/dataloader/fixtures/CustomValueCache.java
// public class CustomValueCache implements ValueCache<String, Object> {
// 
//     public final Map<String, Object> store = new ConcurrentHashMap<>();
// 
//     @Override
//     public CompletableFuture<Object> get(String key) {
//         if (!store.containsKey(key)) {
//             return CompletableFutureKit.failedFuture(new RuntimeException("The key is missing"));
//         }
//         return CompletableFuture.completedFuture(store.get(key));
//     }
// 
//     @Override
//     public CompletableFuture<Object> set(String key, Object value) {
//         store.put(key, value);
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Void> delete(String key) {
//         store.remove(key);
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     @Override
//     public CompletableFuture<Void> clear() {
//         store.clear();
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     public Map<String, Object> asMap() {
//         return store;
//     }
// }
// 
// Path: src/main/java/org/dataloader/impl/DataLoaderAssertionException.java
// public class DataLoaderAssertionException extends IllegalStateException {
//     public DataLoaderAssertionException(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/org/dataloader/DataLoaderOptions.java
// public static DataLoaderOptions newOptions() {
//     return new DataLoaderOptions();
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static <K, V> DataLoader<K, V> idLoader() {
//     return idLoader(null, new ArrayList<>());
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static void snooze(int millis) {
//     try {
//         Thread.sleep(millis);
//     } catch (InterruptedException e) {
//         throw new RuntimeException(e);
//     }
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static <T> List<T> sort(Collection<? extends T> collection) {
//     return collection.stream().sorted().collect(toList());
// }
// 
// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// public static <V> CompletableFuture<V> failedFuture(Exception e) {
//     CompletableFuture<V> future = new CompletableFuture<>();
//     future.completeExceptionally(e);
//     return future;
// }


import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.dataloader.fixtures.CaffeineValueCache;
import org.dataloader.fixtures.CustomValueCache;
import org.dataloader.impl.DataLoaderAssertionException;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.fixtures.TestKit.idLoader;
import static org.dataloader.fixtures.TestKit.snooze;
import static org.dataloader.fixtures.TestKit.sort;
import static org.dataloader.impl.CompletableFutureKit.failedFuture;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
                        snooze(1000);
                        throw new IllegalStateException("no a in cache");
                    });
                } else {
                    return CompletableFuture.supplyAsync(() -> {
                        snooze(1000);
                        return key;
                    });
                }
            }

        };


        List<List<String>> loadCalls = new ArrayList<>();
        DataLoaderOptions options = newOptions().setValueCache(customValueCache);
        DataLoader<String, String> identityLoader = idLoader(options, loadCalls);

        CompletableFuture<String> fA = identityLoader.load("a");
        CompletableFuture<String> fB = identityLoader.load("b");
        CompletableFuture<String> fC = identityLoader.load("missC");
        CompletableFuture<String> fD = identityLoader.load("missD");

        await().until(identityLoader.dispatch()::isDone);

        assertThat(fA.join(), equalTo("a"));
        assertThat(fB.join(), equalTo("b"));
        assertThat(fC.join(), equalTo("missC"));
        assertThat(fD.join(), equalTo("missD"));

// Path: src/test/java/org/dataloader/fixtures/CaffeineValueCache.java
// public class CaffeineValueCache implements ValueCache<String, Object> {
// 
//     public final Cache<String, Object> cache;
// 
//     public CaffeineValueCache(Cache<String, Object> cache) {
//         this.cache = cache;
//     }
// 
//     @Override
//     public CompletableFuture<Object> get(String key) {
//         Object value = cache.getIfPresent(key);
//         if (value == null) {
//             // we use get exceptions here to indicate not in cache
//             return CompletableFutureKit.failedFuture(new RuntimeException(key + " not present"));
//         }
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Object> set(String key, Object value) {
//         cache.put(key, value);
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Void> delete(String key) {
//         cache.invalidate(key);
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     @Override
//     public CompletableFuture<Void> clear() {
//         cache.invalidateAll();
//         return CompletableFuture.completedFuture(null);
//     }
// }
// 
// Path: src/test/java/org/dataloader/fixtures/CustomValueCache.java
// public class CustomValueCache implements ValueCache<String, Object> {
// 
//     public final Map<String, Object> store = new ConcurrentHashMap<>();
// 
//     @Override
//     public CompletableFuture<Object> get(String key) {
//         if (!store.containsKey(key)) {
//             return CompletableFutureKit.failedFuture(new RuntimeException("The key is missing"));
//         }
//         return CompletableFuture.completedFuture(store.get(key));
//     }
// 
//     @Override
//     public CompletableFuture<Object> set(String key, Object value) {
//         store.put(key, value);
//         return CompletableFuture.completedFuture(value);
//     }
// 
//     @Override
//     public CompletableFuture<Void> delete(String key) {
//         store.remove(key);
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     @Override
//     public CompletableFuture<Void> clear() {
//         store.clear();
//         return CompletableFuture.completedFuture(null);
//     }
// 
//     public Map<String, Object> asMap() {
//         return store;
//     }
// }
// 
// Path: src/main/java/org/dataloader/impl/DataLoaderAssertionException.java
// public class DataLoaderAssertionException extends IllegalStateException {
//     public DataLoaderAssertionException(String message) {
//         super(message);
//     }
// }
// 
// Path: src/main/java/org/dataloader/DataLoaderOptions.java
// public static DataLoaderOptions newOptions() {
//     return new DataLoaderOptions();
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static <K, V> DataLoader<K, V> idLoader() {
//     return idLoader(null, new ArrayList<>());
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static void snooze(int millis) {
//     try {
//         Thread.sleep(millis);
//     } catch (InterruptedException e) {
//         throw new RuntimeException(e);
//     }
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static <T> List<T> sort(Collection<? extends T> collection) {
//     return collection.stream().sorted().collect(toList());
// }
// 
// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// public static <V> CompletableFuture<V> failedFuture(Exception e) {
//     CompletableFuture<V> future = new CompletableFuture<>();
//     future.completeExceptionally(e);
//     return future;
// }


// Path: src/test/java/org/dataloader/DataLoaderValueCacheTest.java
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.dataloader.fixtures.CaffeineValueCache;
import org.dataloader.fixtures.CustomValueCache;
import org.dataloader.impl.DataLoaderAssertionException;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.fixtures.TestKit.idLoader;
import static org.dataloader.fixtures.TestKit.snooze;
import static org.dataloader.fixtures.TestKit.sort;
import static org.dataloader.impl.CompletableFutureKit.failedFuture;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

                        snooze(1000);
                        throw new IllegalStateException("no a in cache");
                    });
                } else {
                    return CompletableFuture.supplyAsync(() -> {
                        snooze(1000);
                        return key;
                    });
                }
            }

        };


        List<List<String>> loadCalls = new ArrayList<>();
        DataLoaderOptions options = newOptions().setValueCache(customValueCache);
        DataLoader<String, String> identityLoader = idLoader(options, loadCalls);

        CompletableFuture<String> fA = identityLoader.load("a");
        CompletableFuture<String> fB = identityLoader.load("b");
        CompletableFuture<String> fC = identityLoader.load("missC");
        CompletableFuture<String> fD = identityLoader.load("missD");

        await().until(identityLoader.dispatch()::isDone);

        assertThat(fA.join(), equalTo("a"));
        assertThat(fB.join(), equalTo("b"));
        assertThat(fC.join(), equalTo("missC"));
        assertThat(fD.join(), equalTo("missD"));

        assertThat(loadCalls, equalTo(singletonList(asList("missC", "missD"))));