package com.google.job.data;

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.*;
import com.google.firestore.FireStoreUtils;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

/** Tests for {@link JobsDatabase} class. */
public final class JobsDatabaseTest {
    // TODO(issue/15): Add failure test case
    // TODO(issue/16): Move the clear database method as @After

    private static final String TEST_JOB_COLLECTION = "Jobs";

    JobsDatabase jobsDatabase;
    Firestore firestore;

    @Before
    public void setUp() throws IOException {
        jobsDatabase = new JobsDatabase();
        firestore = FireStoreUtils.getFireStore();
    }

    @Test
    public void addJob_NormalInput_success() throws ExecutionException, InterruptedException {
        // Arrange.
        String expectedJobName = "Software Engineer";
        Job job = new Job(expectedJobName);

        // Act.
        Future<DocumentReference> addedJobFuture = jobsDatabase.addJob(job);

        // Assert.
        DocumentReference documentReference = addedJobFuture.get();
        // Asynchronously retrieve the document.
        ApiFuture<DocumentSnapshot> future = documentReference.get();

        // future.get() blocks on response.
        DocumentSnapshot document = future.get();
        String jobId = document.getId();

        Job actualJob = document.toObject(Job.class);
        String actualJobName = actualJob.getJobTitle();

        assertEquals(expectedJobName, actualJobName);

        firestore.collection(TEST_JOB_COLLECTION).document(jobId).delete();
    }

    @Test
    public void setJob_NormalInput_success() throws ExecutionException, InterruptedException {
        // Arrange.
        Job oldJob = new Job("Noogler");
        Future<DocumentReference> addedJobFuture = firestore.collection(TEST_JOB_COLLECTION)
                .add(oldJob);

        DocumentReference documentReference = addedJobFuture.get();
        // Asynchronously retrieve the document.
        ApiFuture<DocumentSnapshot> future = documentReference.get();

        // future.get() blocks on response.
        DocumentSnapshot document = future.get();
        String jobId = document.getId();

        String expectedJobName = "Googler";
        Job job = new Job(expectedJobName);

        // Act.
        Future<WriteResult> editedDocRef = jobsDatabase.setJob(jobId, job);

        // Assert.
        // future.get() blocks on response.
        editedDocRef.get();

        ApiFuture<DocumentSnapshot> documentSnapshotApiFuture = firestore.collection(TEST_JOB_COLLECTION).
                document(jobId).get();
        DocumentSnapshot documentSnapshot = documentSnapshotApiFuture.get();
        Job actualJob = documentSnapshot.toObject(Job.class);

        String actualJobName = actualJob.getJobTitle();

        assertEquals(expectedJobName, actualJobName);

        firestore.collection(TEST_JOB_COLLECTION).document(jobId).delete();
    }

    @Test
    public void fetchJob_NormalInput_success() throws ExecutionException, InterruptedException {
        // Arrange.
        Job job = new Job("Programmer");

        Future<DocumentReference> addedJobFuture = firestore.collection(TEST_JOB_COLLECTION).add(job);

        DocumentReference documentReference = addedJobFuture.get();
        // Asynchronously retrieve the document.
        ApiFuture<DocumentSnapshot> future = documentReference.get();

        // future.get() blocks on response.
        DocumentSnapshot document = future.get();
        String jobId = document.getId();

        // Act.
        Optional<Job> jobOptional = jobsDatabase.fetchJob(jobId).get();

        // Assert.
        assertTrue(jobOptional.isPresent());

        Job actualJob = jobOptional.get();

        String actualJobName = actualJob.getJobTitle();
        String expectedJobName = "Programmer";

        assertEquals(expectedJobName, actualJobName);

        firestore.collection(TEST_JOB_COLLECTION).document(jobId).delete();
    }
}

@@ -138,24 +138,48 @@
 StoreUtils;%0A
+import org.junit.After;%0A
 import org.j
@@ -231,22 +231,17 @@
 ava.
-io.IOException
+util.List
 ;%0Aim
@@ -693,16 +693,62 @@
  %22Jobs%22;
+%0A    private static final int BATCH_SIZE = 10;
 %0A%0A    Jo
@@ -798,16 +798,1066 @@
 store;%0A%0A
+    /** Delete a collection in batches to avoid out-of-memory errors.%0A     * Batch size may be tuned based on document size (atmost 1MB) and application requirements.%0A     */%0A    private void deleteCollection(CollectionReference collection, int batchSize) %7B%0A        try %7B%0A            // retrieve a small batch of documents to avoid out-of-memory errors%0A            ApiFuture%3CQuerySnapshot%3E future = collection.limit(batchSize).get();%0A            int deleted = 0;%0A            // future.get() blocks on document retrieval%0A            List%3CQueryDocumentSnapshot%3E documents = future.get().getDocuments();%0A            for (QueryDocumentSnapshot document : documents) %7B%0A                document.getReference().delete();%0A                ++deleted;%0A            %7D%0A            if (deleted %3E= batchSize) %7B%0A                // retrieve and delete another batch%0A                deleteCollection(collection, batchSize);%0A            %7D%0A        %7D catch (Exception e) %7B%0A            System.err.println(%22Error deleting collection : %22 + e.getMessage());%0A        %7D%0A    %7D%0A%0A%0A
     @Bef
@@ -1887,27 +1887,8 @@
 Up()
- throws IOException
  %7B%0A 
@@ -2813,32 +2813,35 @@
 bName);%0A%0A       
+ //
  firestore.colle
@@ -4185,32 +4185,35 @@
 bName);%0A%0A       
+ //
  firestore.colle
@@ -5225,32 +5225,35 @@
 bName);%0A%0A       
+ //
  firestore.colle
@@ -5310,11 +5310,146 @@
 );%0A    %7D
+%0A%0A    @After%0A    public void clearCollection() %7B%0A        deleteCollection(firestore.collection(TEST_JOB_COLLECTION), BATCH_SIZE);%0A    %7D
 %0A%7D%0A

Clear the entire collection after all tests