// Path: src/main/java/org/dataloader/DataLoaderFactory.java
// public static <K, V> DataLoader<K, V> newDataLoader(BatchLoader<K, V> batchLoadFunction) {
//     return newDataLoader(batchLoadFunction, null);
// }
// 
// 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 <V> CompletableFuture<V> futureError() {
//     return failedFuture(new IllegalStateException("Error"));
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static Collection<Integer> listFrom(int i, int max) {
//     List<Integer> ints = new ArrayList<>();
//     for (int j = i; j < max; j++) {
//         ints.add(j);
//     }
//     return ints;
// }
// 
// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// public static <V> Throwable cause(CompletableFuture<V> completableFuture) {
//     if (!completableFuture.isCompletedExceptionally()) {
//         return null;
//     }
//     try {
//         completableFuture.get();
//         return null;
//     } catch (InterruptedException e) {
//         Thread.currentThread().interrupt();
//         return e;
//     } catch (ExecutionException e) {
//         Throwable cause = e.getCause();
//         if (cause != null) {
//             return cause;
//         }
//         return e;
//     }
// }


import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderFactory.newDataLoader;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.fixtures.TestKit.futureError;
import static org.dataloader.fixtures.TestKit.listFrom;
import static org.dataloader.impl.CompletableFutureKit.cause;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
    public void should_work_with_duplicate_keys_when_caching_disabled() throws ExecutionException, InterruptedException {
        List<Collection<String>> loadCalls = new ArrayList<>();
        DataLoader<String, String> identityLoader =
                idMapLoader(newOptions().setCachingEnabled(false), loadCalls);

        CompletableFuture<String> future1 = identityLoader.load("A");
        CompletableFuture<String> future2 = identityLoader.load("B");
        CompletableFuture<String> future3 = identityLoader.load("A");
        identityLoader.dispatch();

        await().until(() -> future1.isDone() && future2.isDone() && future3.isDone());
        assertThat(future1.get(), equalTo("A"));
        assertThat(future2.get(), equalTo("B"));
        assertThat(future3.get(), equalTo("A"));

        // the map batch functions use a set of keys as input and hence remove duplicates unlike list variant
        assertThat(loadCalls, equalTo(singletonList(asList("A", "B"))));
    }

    @Test
    public void should_work_with_duplicate_keys_when_caching_enabled() throws ExecutionException, InterruptedException {
        List<Collection<String>> loadCalls = new ArrayList<>();
        DataLoader<String, String> identityLoader =
                idMapLoader(newOptions().setCachingEnabled(true), loadCalls);

        CompletableFuture<String> future1 = identityLoader.load("A");
        CompletableFuture<String> future2 = identityLoader.load("B");
        CompletableFuture<String> future3 = identityLoader.load("A");
        identityLoader.dispatch();

// Path: src/main/java/org/dataloader/DataLoaderFactory.java
// public static <K, V> DataLoader<K, V> newDataLoader(BatchLoader<K, V> batchLoadFunction) {
//     return newDataLoader(batchLoadFunction, null);
// }
// 
// 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 <V> CompletableFuture<V> futureError() {
//     return failedFuture(new IllegalStateException("Error"));
// }
// 
// Path: src/test/java/org/dataloader/fixtures/TestKit.java
// public static Collection<Integer> listFrom(int i, int max) {
//     List<Integer> ints = new ArrayList<>();
//     for (int j = i; j < max; j++) {
//         ints.add(j);
//     }
//     return ints;
// }
// 
// Path: src/main/java/org/dataloader/impl/CompletableFutureKit.java
// public static <V> Throwable cause(CompletableFuture<V> completableFuture) {
//     if (!completableFuture.isCompletedExceptionally()) {
//         return null;
//     }
//     try {
//         completableFuture.get();
//         return null;
//     } catch (InterruptedException e) {
//         Thread.currentThread().interrupt();
//         return e;
//     } catch (ExecutionException e) {
//         Throwable cause = e.getCause();
//         if (cause != null) {
//             return cause;
//         }
//         return e;
//     }
// }


// Path: src/test/java/org/dataloader/DataLoaderMapBatchLoaderTest.java
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.awaitility.Awaitility.await;
import static org.dataloader.DataLoaderFactory.newDataLoader;
import static org.dataloader.DataLoaderOptions.newOptions;
import static org.dataloader.fixtures.TestKit.futureError;
import static org.dataloader.fixtures.TestKit.listFrom;
import static org.dataloader.impl.CompletableFutureKit.cause;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

    public void should_work_with_duplicate_keys_when_caching_disabled() throws ExecutionException, InterruptedException {
        List<Collection<String>> loadCalls = new ArrayList<>();
        DataLoader<String, String> identityLoader =
                idMapLoader(newOptions().setCachingEnabled(false), loadCalls);

        CompletableFuture<String> future1 = identityLoader.load("A");
        CompletableFuture<String> future2 = identityLoader.load("B");
        CompletableFuture<String> future3 = identityLoader.load("A");
        identityLoader.dispatch();

        await().until(() -> future1.isDone() && future2.isDone() && future3.isDone());
        assertThat(future1.get(), equalTo("A"));
        assertThat(future2.get(), equalTo("B"));
        assertThat(future3.get(), equalTo("A"));

        // the map batch functions use a set of keys as input and hence remove duplicates unlike list variant
        assertThat(loadCalls, equalTo(singletonList(asList("A", "B"))));
    }

    @Test
    public void should_work_with_duplicate_keys_when_caching_enabled() throws ExecutionException, InterruptedException {
        List<Collection<String>> loadCalls = new ArrayList<>();
        DataLoader<String, String> identityLoader =
                idMapLoader(newOptions().setCachingEnabled(true), loadCalls);

        CompletableFuture<String> future1 = identityLoader.load("A");
        CompletableFuture<String> future2 = identityLoader.load("B");
        CompletableFuture<String> future3 = identityLoader.load("A");
        identityLoader.dispatch();

        await().until(() -> future1.isDone() && future2.isDone() && future3.isDone());