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

hansva pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/hop.git


The following commit(s) were added to refs/heads/main by this push:
     new 08e800f359 Report the execution start and end date of a workflow, 
fixes #4052 (#7545)
08e800f359 is described below

commit 08e800f359aecf7342a3cb76dbcfde7bb82087c9
Author: Hans Van Akelyen <[email protected]>
AuthorDate: Thu Jul 16 22:12:20 2026 +0200

    Report the execution start and end date of a workflow, fixes #4052 (#7545)
---
 .../apache/hop/www/GetWorkflowStatusServlet.java   |  2 +
 .../hop/www/GetWorkflowStatusServletTest.java      | 82 ++++++++++++++++++++++
 2 files changed, 84 insertions(+)

diff --git 
a/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java 
b/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
index dcc26956c2..283c09aad7 100644
--- a/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
+++ b/engine/src/main/java/org/apache/hop/www/GetWorkflowStatusServlet.java
@@ -130,6 +130,8 @@ public class GetWorkflowStatusServlet extends 
BaseHttpServlet implements IHopSer
           workflowStatus.setFirstLoggingLineNr(startLineNr);
           workflowStatus.setLastLoggingLineNr(lastLineNr);
           workflowStatus.setLogDate(workflow.getExecutionStartDate());
+          
workflowStatus.setExecutionStartDate(workflow.getExecutionStartDate());
+          workflowStatus.setExecutionEndDate(workflow.getExecutionEndDate());
 
           // Add status of executed actions
           for (ActionResult actionResult : workflow.getActionResults()) {
diff --git 
a/engine/src/test/java/org/apache/hop/www/GetWorkflowStatusServletTest.java 
b/engine/src/test/java/org/apache/hop/www/GetWorkflowStatusServletTest.java
index 4961c2f44a..f70b528fa2 100644
--- a/engine/src/test/java/org/apache/hop/www/GetWorkflowStatusServletTest.java
+++ b/engine/src/test/java/org/apache/hop/www/GetWorkflowStatusServletTest.java
@@ -18,8 +18,10 @@
 package org.apache.hop.www;
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -32,14 +34,18 @@ import jakarta.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
 import org.apache.hop.core.gui.Point;
 import org.apache.hop.core.logging.HopLogStore;
 import org.apache.hop.core.logging.ILogChannel;
+import org.apache.hop.core.xml.XmlHandler;
 import org.apache.hop.workflow.Workflow;
 import org.apache.hop.workflow.WorkflowMeta;
 import org.apache.hop.workflow.engine.IWorkflowEngine;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 import org.owasp.encoder.Encode;
 
@@ -100,6 +106,82 @@ class GetWorkflowStatusServletTest {
     assertFalse(out.toString().contains(ServletTestUtils.BAD_STRING_TO_TEST));
   }
 
+  /**
+   * The workflow status has to report when the workflow started and ended, 
the way the pipeline
+   * status does. See issue #4052.
+   */
+  @Test
+  void statusReportsExecutionStartAndEndDate() throws ServletException, 
IOException {
+    Date startDate = new Date(1700000000000L);
+    Date endDate = new Date(1700000012345L);
+
+    String xml = getStatusOutput(startDate, endDate, "Y", null);
+
+    assertTrue(
+        xml.contains("<execution_start_date>" + 
XmlHandler.date2string(startDate)),
+        "The status should report the start date, was: " + xml);
+    assertTrue(
+        xml.contains("<execution_end_date>" + XmlHandler.date2string(endDate)),
+        "The status should report the end date, was: " + xml);
+  }
+
+  /** The same dates are reported in JSON, which is what the REST API of the 
server serves. */
+  @Test
+  void jsonStatusReportsExecutionStartAndEndDate() throws ServletException, 
IOException {
+    Date startDate = new Date(1700000000000L);
+    Date endDate = new Date(1700000012345L);
+
+    String json = getStatusOutput(startDate, endDate, null, "Y");
+
+    assertTrue(json.contains("\"executionStartDate\""), "The JSON should hold 
the start date");
+    assertTrue(json.contains("\"executionEndDate\""), "The JSON should hold 
the end date");
+    assertFalse(
+        json.contains("\"executionStartDate\" : null"),
+        "The start date should not be null: " + json);
+    assertFalse(
+        json.contains("\"executionEndDate\" : null"), "The end date should not 
be null: " + json);
+  }
+
+  /** Runs the servlet for a finished workflow and returns what it wrote to 
the response. */
+  private String getStatusOutput(Date startDate, Date endDate, String useXml, 
String useJson)
+      throws ServletException, IOException {
+    HopLogStore.init();
+    HttpServletRequest mockHttpServletRequest = mock(HttpServletRequest.class);
+    HttpServletResponse mockHttpServletResponse = 
mock(HttpServletResponse.class);
+    IWorkflowEngine<WorkflowMeta> mockWorkflow = Mockito.mock(Workflow.class);
+    WorkflowMeta mockWorkflowMeta = mock(WorkflowMeta.class);
+    ILogChannel mockLogChannelInterface = mock(ILogChannel.class);
+    ServletOutputStream outMock = mock(ServletOutputStream.class);
+
+    String id = "123";
+
+    
when(mockHttpServletRequest.getContextPath()).thenReturn(GetWorkflowStatusServlet.CONTEXT_PATH);
+    when(mockHttpServletRequest.getParameter("id")).thenReturn(id);
+    when(mockHttpServletRequest.getParameter("xml")).thenReturn(useXml);
+    when(mockHttpServletRequest.getParameter("json")).thenReturn(useJson);
+    when(mockHttpServletResponse.getOutputStream()).thenReturn(outMock);
+    when(mockWorkflowMap.findWorkflow(id)).thenReturn(mockWorkflow);
+    Mockito.when(mockWorkflow.getWorkflowName()).thenReturn("a-workflow");
+    
Mockito.when(mockWorkflow.getLogChannel()).thenReturn(mockLogChannelInterface);
+    Mockito.when(mockWorkflow.getWorkflowMeta()).thenReturn(mockWorkflowMeta);
+    Mockito.when(mockWorkflow.isFinished()).thenReturn(true);
+    Mockito.when(mockWorkflow.getLogChannelId()).thenReturn("logId");
+    Mockito.when(mockWorkflow.getExecutionStartDate()).thenReturn(startDate);
+    Mockito.when(mockWorkflow.getExecutionEndDate()).thenReturn(endDate);
+    Mockito.when(mockWorkflowMeta.getMaximum()).thenReturn(new Point(10, 10));
+    when(mockWorkflow.getStatusDescription()).thenReturn("Finished");
+
+    getWorkflowStatusServlet.doGet(mockHttpServletRequest, 
mockHttpServletResponse);
+
+    ArgumentCaptor<byte[]> captor = ArgumentCaptor.forClass(byte[].class);
+    verify(outMock, atLeastOnce()).write(captor.capture());
+    StringBuilder written = new StringBuilder();
+    for (byte[] data : captor.getAllValues()) {
+      written.append(new String(data, StandardCharsets.UTF_8));
+    }
+    return written.toString();
+  }
+
   @Test
   void testGetJobStatus() throws ServletException, IOException {
     HopLogStore.init();

Reply via email to