You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by aw...@apache.org on 2020/05/24 19:54:47 UTC

[fineract] branch develop updated: intro. SchedulerJobsHelper.executeAndAwaitJob() [FINERACT-922] (#817)

This is an automated email from the ASF dual-hosted git repository.

awasum pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new b3da50c  intro. SchedulerJobsHelper.executeAndAwaitJob() [FINERACT-922] (#817)
b3da50c is described below

commit b3da50c96063cef75b8e9ea2a42c8d509ad75e8d
Author: Michael Vorburger ⛑️ <mi...@vorburger.ch>
AuthorDate: Sun May 24 21:54:37 2020 +0200

    intro. SchedulerJobsHelper.executeAndAwaitJob() [FINERACT-922] (#817)
---
 .../integrationtests/SchedulerJobsTest.java        |  34 +----
 .../common/SchedulerJobHelper.java                 | 165 ++++++++++++++++-----
 2 files changed, 128 insertions(+), 71 deletions(-)

diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java
index f256047..5739a86 100644
--- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SchedulerJobsTest.java
@@ -20,8 +20,6 @@ package org.apache.fineract.integrationtests;
 
 import static org.awaitility.Awaitility.await;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 
 import io.restassured.builder.RequestSpecBuilder;
 import io.restassured.http.ContentType;
@@ -35,7 +33,6 @@ import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
 import org.apache.fineract.integrationtests.common.Utils;
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 
 public class SchedulerJobsTest {
@@ -129,34 +126,9 @@ public class SchedulerJobsTest {
     }
 
     @Test
-    @Ignore // TODO FINERACT-852 & FINERACT-922
-    public void testSchedulerJobs() throws InterruptedException {
-        // For each retrieved scheduled job (by ID)...
-        for (Integer jobId : schedulerJobHelper.getAllSchedulerJobIds()) {
-            // Retrieving Scheduler Job by ID
-            Map<String, Object> schedulerJob = schedulerJobHelper.getSchedulerJobById(jobId);
-
-            // Executing Scheduler Job
-            SchedulerJobHelper.runSchedulerJob(requestSpec, jobId.toString());
-
-            // Retrieving Scheduler Job by ID
-            schedulerJob = schedulerJobHelper.getSchedulerJobById(jobId);
-            assertNotNull(schedulerJob);
-
-            // Waiting for Job to complete
-            while ((Boolean) schedulerJob.get("currentlyRunning") == true) {
-                Thread.sleep(500);
-                schedulerJob = schedulerJobHelper.getSchedulerJobById(jobId);
-                assertNotNull(schedulerJob);
-            }
-            @SuppressWarnings({ "unchecked", "rawtypes" })
-            List<Map> jobHistoryData = schedulerJobHelper.getSchedulerJobHistory(jobId);
-
-            // Verifying the Status of the Recently executed Scheduler Job
-            assertFalse("Job History is empty :(  Was it too slow? Failures in background job?",
-                    jobHistoryData.isEmpty());
-            assertEquals("Verifying Last Scheduler Job Status", "success",
-                    jobHistoryData.get(jobHistoryData.size() - 1).get("status"));
+    public void testTriggeringManualExecutionOfAllSchedulerJobs() {
+        for (String jobName : schedulerJobHelper.getAllSchedulerJobNames()) {
+            schedulerJobHelper.executeAndAwaitJob(jobName);
         }
     }
 }
\ No newline at end of file
diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java
index 8382ca7..21dafcb 100644
--- a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java
+++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/common/SchedulerJobHelper.java
@@ -18,23 +18,32 @@
  */
 package org.apache.fineract.integrationtests.common;
 
+import static java.time.Instant.now;
+import static org.awaitility.Awaitility.await;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
 
 import com.google.gson.Gson;
 import io.restassured.builder.ResponseSpecBuilder;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
-import java.util.ArrayList;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.function.ToIntFunction;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
 import java.util.stream.Collectors;
-import org.junit.Assert;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@SuppressWarnings({ "rawtypes", "unchecked" })
 public class SchedulerJobHelper {
     private final static Logger LOG = LoggerFactory.getLogger(SchedulerJobHelper.class);
     private final RequestSpecification requestSpec;
@@ -53,16 +62,24 @@ public class SchedulerJobHelper {
         this.response202Spec = responseSpec;
     }
 
-    private List getAllSchedulerJobs() {
+    private List<Map<String, Object>> getAllSchedulerJobs() {
         final String GET_ALL_SCHEDULER_JOBS_URL = "/fineract-provider/api/v1/jobs?" + Utils.TENANT_IDENTIFIER;
         LOG.info("------------------------ RETRIEVING ALL SCHEDULER JOBS -------------------------");
-        final ArrayList response = Utils.performServerGet(requestSpec, response200Spec, GET_ALL_SCHEDULER_JOBS_URL, "");
+        List<Map<String, Object>> response = Utils.performServerGet(requestSpec, response200Spec, GET_ALL_SCHEDULER_JOBS_URL, "");
+        assertNotNull(response);
         return response;
     }
 
+    private <T> List<T> getAllSchedulerJobDetails(Function<Map<String, Object>, T> mapper) {
+        return getAllSchedulerJobs().stream().map(mapper).collect(Collectors.toList());
+    }
+
     public List<Integer> getAllSchedulerJobIds() {
-        ToIntFunction<Map> mapper = map -> (Integer) map.get("jobId");
-        return getAllSchedulerJobs().stream().mapToInt(mapper).boxed().collect(Collectors.toList());
+        return getAllSchedulerJobDetails(map -> (Integer) map.get("jobId"));
+    }
+
+    public List<String> getAllSchedulerJobNames() {
+        return getAllSchedulerJobDetails(map -> (String) map.get("displayName"));
     }
 
     public Map<String, Object> getSchedulerJobById(int jobId) {
@@ -103,14 +120,15 @@ public class SchedulerJobHelper {
         return new Gson().toJson(map);
     }
 
-    public List getSchedulerJobHistory(int jobId) {
+    @SuppressWarnings("unchecked")
+    private List<Map<String, Object>> getSchedulerJobHistory(int jobId) {
         final String GET_SCHEDULER_STATUS_URL = "/fineract-provider/api/v1/jobs/" + jobId + "/runhistory?" + Utils.TENANT_IDENTIFIER;
         LOG.info("------------------------ RETRIEVING SCHEDULER JOB HISTORY -------------------------");
-        final Map response = Utils.performServerGet(requestSpec, response200Spec, GET_SCHEDULER_STATUS_URL, "");
-        return (ArrayList) response.get("pageItems");
+        final Map<String, Object> response = Utils.performServerGet(requestSpec, response200Spec, GET_SCHEDULER_STATUS_URL, "");
+        return (List<Map<String, Object>>) response.get("pageItems");
     }
 
-    public static void runSchedulerJob(final RequestSpecification requestSpec, final String jobId) {
+    private void runSchedulerJob(int jobId) {
         final ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(202).build();
         final String RUN_SCHEDULER_JOB_URL = "/fineract-provider/api/v1/jobs/" + jobId + "?command=executeJob&" + Utils.TENANT_IDENTIFIER;
         LOG.info("------------------------ RUN SCHEDULER JOB -------------------------");
@@ -124,45 +142,112 @@ public class SchedulerJobHelper {
         return runSchedulerJob;
     }
 
-    public void executeJob(String jobName) throws InterruptedException {
-        List<Map> allSchedulerJobsData = getAllSchedulerJobs();
-        Assert.assertNotNull(allSchedulerJobsData);
-
+    private int getSchedulerJobIdByName(String jobName) {
+        List<Map<String, Object>> allSchedulerJobsData = getAllSchedulerJobs();
         for (Integer jobIndex = 0; jobIndex < allSchedulerJobsData.size(); jobIndex++) {
             if (allSchedulerJobsData.get(jobIndex).get("displayName").equals(jobName)) {
-                Integer jobId = (Integer) allSchedulerJobsData.get(jobIndex).get("jobId");
+                return (Integer) allSchedulerJobsData.get(jobIndex).get("jobId");
+            }
+        }
+        throw new IllegalArgumentException("No such named Job (see org.apache.fineract.infrastructure.jobs.service.JobName enum):" + jobName);
+    }
 
-                // Executing Scheduler Job
-                runSchedulerJob(this.requestSpec, jobId.toString());
+    // FINERACT-922 TODO Gradually replace use of this method with new executeAndAwaitJob() below, if it proves to be more stable than this one
+    public void executeJob(String jobName) throws InterruptedException {
+        // Stop the Scheduler while we manually trigger execution of job, to avoid side effects and simplify debugging when readings logs
+        updateSchedulerStatus(false);
+
+        int jobId = getSchedulerJobIdByName(jobName);
 
-                // Retrieving Scheduler Job by ID
-                Map schedulerJob = getSchedulerJobById(jobId);
-                Assert.assertNotNull(schedulerJob);
+        // Executing Scheduler Job
+        runSchedulerJob(jobId);
 
-                // Waiting for Job to complete
-                while ((Boolean) schedulerJob.get("currentlyRunning") == true) {
-                    Thread.sleep(15000);
-                    schedulerJob = getSchedulerJobById(jobId);
-                    Assert.assertNotNull(schedulerJob);
-                    LOG.info("Job is Still Running");
-                }
+        // Retrieving Scheduler Job by ID
+        Map<String, Object> schedulerJob = getSchedulerJobById(jobId);
 
-                List<Map> jobHistoryData = getSchedulerJobHistory(jobId);
+        // Waiting for Job to complete
+        while ((Boolean) schedulerJob.get("currentlyRunning") == true) {
+            Thread.sleep(15000);
+            schedulerJob = getSchedulerJobById(jobId);
+            assertNotNull(schedulerJob);
+            LOG.info("Job is Still Running");
+        }
 
-                Assert.assertFalse("Job History is empty :(  Was it too slow? Failures in background job?", jobHistoryData.isEmpty());
+        List<Map<String, Object>> jobHistoryData = getSchedulerJobHistory(jobId);
 
-                // print error associated with recent job failure (if any)
-                LOG.info("Job run error message (printed only if the job fails: {}"
-                        , jobHistoryData.get(jobHistoryData.size() - 1).get("jobRunErrorMessage"));
-                LOG.info("Job failure error log (printed only if the job fails: {}"
-                        , jobHistoryData.get(jobHistoryData.size() - 1).get("jobRunErrorLog"));
+        // print error associated with recent job failure (if any)
+        LOG.error("Last Job run error message (only relevent if the job fails: {}",
+                jobHistoryData.get(jobHistoryData.size() - 1).get("jobRunErrorMessage"));
+        LOG.error("Lsat Job failure error log (only relevent if the job fails: {}",
+                jobHistoryData.get(jobHistoryData.size() - 1).get("jobRunErrorLog"));
 
-                // Verifying the Status of the Recently executed Scheduler Job
-                Assert.assertEquals("Verifying Last Scheduler Job Status", "success",
-                        jobHistoryData.get(jobHistoryData.size() - 1).get("status"));
+        assertFalse("Job History is empty :(  Was it too slow? Failures in background job?", jobHistoryData.isEmpty());
+
+        // Verifying the Status of the Recently executed Scheduler Job
+        assertEquals("Verifying Last Scheduler Job Status", "success",
+                jobHistoryData.get(jobHistoryData.size() - 1).get("status"));
+    }
 
-                break;
+    /**
+     * Launches a Job and awaits its completion.
+     * @param jobName displayName (see {@link org.apache.fineract.infrastructure.jobs.service.JobName}) of Scheduler Job
+     *
+     * @author Michael Vorburger.ch
+     */
+    public void executeAndAwaitJob(String jobName) {
+        Duration TIMEOUT = Duration.ofSeconds(30);
+        Duration PAUSE = Duration.ofMillis(500);
+        DateTimeFormatter df = DateTimeFormatter.ISO_INSTANT; // FINERACT-926
+        Instant beforeExecuteTime = now().truncatedTo(ChronoUnit.SECONDS);
+
+        // Stop the Scheduler while we manually trigger execution of job, to avoid side effects and simplify debugging when readings logs
+        updateSchedulerStatus(false);
+
+        // Executing Scheduler Job
+        int jobId = getSchedulerJobIdByName(jobName);
+        runSchedulerJob(jobId);
+
+        // Await JobDetailData.lastRunHistory [JobDetailHistoryData] jobRunStartTime >= beforeExecuteTime (or timeout)
+        await().atMost(TIMEOUT).pollInterval(PAUSE).until(jobLastRunHistorySupplier(jobId), lastRunHistory -> {
+            String jobRunStartText = lastRunHistory.get("jobRunStartTime");
+            if (jobRunStartText == null) {
+                return false;
             }
+            Instant jobRunStartTime = df.parse(jobRunStartText, Instant::from);
+            return jobRunStartTime.equals(beforeExecuteTime) || jobRunStartTime.isAfter(beforeExecuteTime);
+        });
+
+        // Await JobDetailData.lastRunHistory [JobDetailHistoryData] jobRunEndTime to be both set and >= jobRunStartTime (or timeout)
+        Map<String, String> finalLastRunHistory = await().atMost(TIMEOUT).pollInterval(PAUSE).until(jobLastRunHistorySupplier(jobId), lastRunHistory -> {
+            String jobRunEndText = lastRunHistory.get("jobRunEndTime");
+            if (jobRunEndText == null) {
+                return false;
+            }
+            Instant jobRunEndTime = df.parse(jobRunEndText, Instant::from);
+            Instant jobRunStartTime = df.parse(lastRunHistory.get("jobRunStartTime"), Instant::from);
+            return jobRunEndTime.equals(jobRunStartTime) || jobRunEndTime.isAfter(jobRunStartTime);
+        });
+
+        // Verify triggerType
+        assertThat(finalLastRunHistory.get("triggerType"), is("application"));
+
+        // Verify status & propagate jobRunErrorMessage and/or jobRunErrorLog (if any)
+        String status = finalLastRunHistory.get("status");
+        if (!status.equals("success")) {
+            fail("Job status is not success: " + finalLastRunHistory.toString());
         }
+
+        // PS: Checking getSchedulerJobHistory() [/runhistory] is pointless, because the lastRunHistory JobDetailHistoryData is already part of JobDetailData anyway.
+    }
+
+    @SuppressWarnings("unchecked")
+    private Callable<Map<String, String>> jobLastRunHistorySupplier(int jobId) {
+        return () -> {
+            Map<String, Object> job = getSchedulerJobById(jobId);
+            if (job == null) {
+                return null;
+            }
+            return (Map<String, String>) job.get("lastRunHistory");
+        };
     }
 }