diff --git a/qqq-middleware-javalin/pom.xml b/qqq-middleware-javalin/pom.xml
index a07cbb4f..2f2386dd 100644
--- a/qqq-middleware-javalin/pom.xml
+++ b/qqq-middleware-javalin/pom.xml
@@ -138,6 +138,17 @@
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ com/kingsrook/qqq/middleware/javalin/executors/io/*.class
+ com/kingsrook/qqq/middleware/javalin/tools/codegenerators/*.class
+ com/kingsrook/qqq/middleware/javalin/specs/**/*.class
+
+
+
diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGenerator.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGenerator.java
index 92a79cbd..1ab8a0eb 100644
--- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGenerator.java
+++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGenerator.java
@@ -80,7 +80,7 @@ class ExecutorCodeGenerator
/***************************************************************************
**
***************************************************************************/
- private void writeAllFiles(String rootPath, String baseName) throws IOException
+ void writeAllFiles(String rootPath, String baseName) throws IOException
{
if(baseName.endsWith("Executor"))
{
diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGenerator.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGenerator.java
index 64af0f2f..5a5e09c2 100644
--- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGenerator.java
+++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGenerator.java
@@ -89,7 +89,7 @@ class SpecCodeGenerator
/***************************************************************************
**
***************************************************************************/
- private void writeAllFiles(String rootPath, String version, String baseName) throws IOException
+ void writeAllFiles(String rootPath, String version, String baseName) throws IOException
{
writeAllFiles(rootPath, version, baseName, baseName);
}
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandlerTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandlerTest.java
index be0483aa..563ad04a 100644
--- a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandlerTest.java
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandlerTest.java
@@ -190,7 +190,7 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
/*******************************************************************************
** test init'ing a process that goes async
**
- ** Note: ported to v1, but needs todo more - the status part too in a higher-level
+ ** Note: ported to v1
*******************************************************************************/
@Test
public void test_processInitGoingAsync() throws InterruptedException
@@ -242,6 +242,7 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
/*******************************************************************************
** test running a step a process that goes async
**
+ ** Note: ported to v1
*******************************************************************************/
@Test
public void test_processStepGoingAsync() throws InterruptedException
@@ -348,6 +349,7 @@ class QJavalinProcessHandlerTest extends QJavalinTestBase
/*******************************************************************************
** test init'ing a process that goes async and then throws
**
+ ** Note: ported to v1
*******************************************************************************/
@Test
public void test_processInitGoingAsyncThenThrowing() throws InterruptedException
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QMiddlewareApiSpecHandlerTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QMiddlewareApiSpecHandlerTest.java
new file mode 100644
index 00000000..c4e66445
--- /dev/null
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/QMiddlewareApiSpecHandlerTest.java
@@ -0,0 +1,152 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.middleware.javalin;
+
+
+import java.util.List;
+import java.util.Map;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.kingsrook.qqq.backend.core.utils.JsonUtils;
+import com.kingsrook.qqq.backend.core.utils.YamlUtils;
+import com.kingsrook.qqq.middleware.javalin.specs.AbstractMiddlewareVersion;
+import com.kingsrook.qqq.middleware.javalin.specs.v1.MiddlewareVersionV1;
+import io.javalin.Javalin;
+import kong.unirest.HttpResponse;
+import kong.unirest.Unirest;
+import org.json.JSONObject;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/*******************************************************************************
+ ** Unit test for QMiddlewareApiSpecHandler
+ *******************************************************************************/
+class QMiddlewareApiSpecHandlerTest
+{
+ private static int PORT = 6264;
+
+ protected static Javalin service;
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @BeforeAll
+ static void beforeAll()
+ {
+ service = Javalin.create(config ->
+ {
+ List middlewareVersionList = List.of(new MiddlewareVersionV1());
+ config.router.apiBuilder(new QMiddlewareApiSpecHandler(middlewareVersionList).defineJavalinEndpointGroup());
+ }
+ ).start(PORT);
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private String getBaseUrlAndPath()
+ {
+ return "http://localhost:" + PORT + "/qqq";
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testIndex()
+ {
+ HttpResponse response = Unirest.get(getBaseUrlAndPath()).asString();
+ assertEquals(200, response.getStatus());
+ assertThat(response.getBody()).contains(" response = Unirest.get(getBaseUrlAndPath() + "/versions.json").asString();
+ assertEquals(200, response.getStatus());
+ JSONObject object = new JSONObject(response.getBody());
+ object.getJSONArray("supportedVersions");
+ assertEquals("v1", object.getString("currentVersion"));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testSpecYaml() throws JsonProcessingException
+ {
+ HttpResponse response = Unirest.get(getBaseUrlAndPath() + "/v1/openapi.yaml").asString();
+ assertEquals(200, response.getStatus());
+ Map map = YamlUtils.toMap(response.getBody());
+ assertTrue(map.containsKey("openapi"));
+ assertTrue(map.containsKey("info"));
+ assertTrue(map.containsKey("paths"));
+ assertTrue(map.containsKey("components"));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testSpecJson() throws JsonProcessingException
+ {
+ HttpResponse response = Unirest.get(getBaseUrlAndPath() + "/v1/openapi.json").asString();
+ assertEquals(200, response.getStatus());
+ JSONObject map = JsonUtils.toJSONObject(response.getBody());
+ assertTrue(map.has("openapi"));
+ assertTrue(map.has("info"));
+ assertTrue(map.has("paths"));
+ assertTrue(map.has("components"));
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testServeResources()
+ {
+ HttpResponse response = Unirest.get("http://localhost:" + PORT + "/api/docs/js/rapidoc.min.js").asString();
+ assertEquals(200, response.getStatus());
+ }
+
+}
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/schemabuilder/ToSchemaTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/schemabuilder/ToSchemaTest.java
new file mode 100644
index 00000000..e9ec9a57
--- /dev/null
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/schemabuilder/ToSchemaTest.java
@@ -0,0 +1,59 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.middleware.javalin.schemabuilder;
+
+
+import com.kingsrook.qqq.middleware.javalin.schemabuilder.annotations.OpenAPIDescription;
+import com.kingsrook.qqq.openapi.model.Schema;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+
+/*******************************************************************************
+ ** Unit test for ToSchema
+ *******************************************************************************/
+class ToSchemaTest
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void test()
+ {
+ Schema schema = new TestsToSchema().toSchema();
+ Schema myFieldSchema = schema.getProperties().get("myField");
+ assertEquals("This is a field", myFieldSchema.getDescription());
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ public static class TestsToSchema implements ToSchema
+ {
+ @OpenAPIDescription("This is a field")
+ private String myField;
+ }
+
+}
\ No newline at end of file
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/SpecTestBase.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/SpecTestBase.java
index 9969827a..d658c101 100644
--- a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/SpecTestBase.java
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/SpecTestBase.java
@@ -22,6 +22,9 @@
package com.kingsrook.qqq.middleware.javalin.specs;
+import java.util.Collections;
+import java.util.List;
+import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
import com.kingsrook.qqq.backend.javalin.TestUtils;
import io.javalin.Javalin;
@@ -46,6 +49,18 @@ public abstract class SpecTestBase
***************************************************************************/
protected abstract AbstractEndpointSpec, ?, ?> getSpec();
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ protected List> getAdditionalSpecs()
+ {
+ return (Collections.emptyList());
+ }
+
+
+
/***************************************************************************
**
***************************************************************************/
@@ -98,9 +113,17 @@ public abstract class SpecTestBase
{
service = Javalin.create(config ->
{
+ QInstance qInstance = TestUtils.defineInstance();
+
AbstractEndpointSpec, ?, ?> spec = getSpec();
- spec.setQInstance(TestUtils.defineInstance());
+ spec.setQInstance(qInstance);
config.router.apiBuilder(() -> spec.defineRoute(getVersion()));
+
+ for(AbstractEndpointSpec, ?, ?> additionalSpec : getAdditionalSpecs())
+ {
+ additionalSpec.setQInstance(qInstance);
+ config.router.apiBuilder(() -> additionalSpec.defineRoute(getVersion()));
+ }
}
).start(PORT);
}
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/v1/ProcessStatusSpecV1Test.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/v1/ProcessStatusSpecV1Test.java
new file mode 100644
index 00000000..ed045652
--- /dev/null
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/specs/v1/ProcessStatusSpecV1Test.java
@@ -0,0 +1,323 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.middleware.javalin.specs.v1;
+
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import com.kingsrook.qqq.backend.core.logging.QLogger;
+import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
+import com.kingsrook.qqq.backend.core.utils.JsonUtils;
+import com.kingsrook.qqq.backend.core.utils.SleepUtils;
+import com.kingsrook.qqq.backend.javalin.TestUtils;
+import com.kingsrook.qqq.middleware.javalin.specs.AbstractEndpointSpec;
+import com.kingsrook.qqq.middleware.javalin.specs.SpecTestBase;
+import kong.unirest.HttpResponse;
+import kong.unirest.Unirest;
+import org.json.JSONObject;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/*******************************************************************************
+ ** Unit test for ProcessStatusSpecV1
+ *******************************************************************************/
+class ProcessStatusSpecV1Test extends SpecTestBase
+{
+ private static final int MORE_THAN_TIMEOUT = 500;
+ private static final int TIMEOUT = 300;
+ private static final int LESS_THAN_TIMEOUT = 50;
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ protected AbstractEndpointSpec, ?, ?> getSpec()
+ {
+ return new ProcessStatusSpecV1();
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ protected List> getAdditionalSpecs()
+ {
+ return List.of(new ProcessInitSpecV1(), new ProcessStepSpecV1());
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ @Override
+ protected String getVersion()
+ {
+ return "v1";
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @AfterEach
+ void afterEach()
+ {
+ QLogger.deactivateCollectingLoggerForClass(MockBackendStep.class);
+ }
+
+
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void testInitWentAsync()
+ {
+ /////////////////////////////////////////
+ // init process, which should go async //
+ /////////////////////////////////////////
+ String processBasePath = getBaseUrlAndPath() + "/processes/" + TestUtils.PROCESS_NAME_SIMPLE_SLEEP;
+ HttpResponse response = Unirest.post(processBasePath + "/init")
+ .multiPartContent()
+ .field("stepTimeoutMillis", String.valueOf(TIMEOUT))
+ .field("values", new JSONObject()
+ .put(TestUtils.SleeperStep.FIELD_SLEEP_MILLIS, MORE_THAN_TIMEOUT)
+ .toString())
+ .asString();
+
+ ///////////////////////////////////
+ // assert we got back job-status //
+ ///////////////////////////////////
+ assertEquals(200, response.getStatus());
+ JSONObject jsonObject = assertProcessStepWentAsyncResponse(response);
+ String processUUID = jsonObject.getString("processUUID");
+ String jobUUID = jsonObject.getString("jobUUID");
+ assertNotNull(processUUID, "Process UUID should not be null.");
+ assertNotNull(jobUUID, "Job UUID should not be null");
+
+ /////////////////////////////////////////////
+ // request job status before sleep is done //
+ /////////////////////////////////////////////
+ response = Unirest.get(processBasePath + "/" + processUUID + "/status/" + jobUUID).asString();
+ jsonObject = assertProcessStepRunningResponse(response);
+
+ ///////////////////////////////////
+ // sleep, to let that job finish //
+ ///////////////////////////////////
+ SleepUtils.sleep(MORE_THAN_TIMEOUT, TimeUnit.MILLISECONDS);
+
+ ////////////////////////////////////////////////////////
+ // request job status again, get back results instead //
+ ////////////////////////////////////////////////////////
+ response = Unirest.get(processBasePath + "/" + processUUID + "/status/" + jobUUID).asString();
+ jsonObject = assertProcessStepCompleteResponse(response);
+ }
+
+
+
+ /*******************************************************************************
+ ** test running a step a process that goes async
+ **
+ *******************************************************************************/
+ @Test
+ public void test_processStepGoingAsync() throws InterruptedException
+ {
+ ///////////////////////////////////////////////////////////
+ // first init the process, to get its UUID //
+ // note this process doesn't sleep until its second step //
+ ///////////////////////////////////////////////////////////
+ String processBasePath = getBaseUrlAndPath() + "/processes/" + TestUtils.PROCESS_NAME_SLEEP_INTERACTIVE;
+ HttpResponse response = Unirest.post(processBasePath + "/init")
+ .multiPartContent()
+ .field("values", new JSONObject()
+ .put(TestUtils.SleeperStep.FIELD_SLEEP_MILLIS, MORE_THAN_TIMEOUT)
+ .toString())
+ .asString();
+
+ JSONObject jsonObject = assertProcessStepCompleteResponse(response);
+ String processUUID = jsonObject.getString("processUUID");
+ String nextStep = jsonObject.getString("nextStep");
+ assertNotNull(processUUID, "Process UUID should not be null.");
+ assertNotNull(nextStep, "There should be a next step");
+ assertFalse(jsonObject.getJSONObject("values").has("didSleep"), "There should not (yet) be a value from the backend step");
+
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ // second, run the 'nextStep' (the backend step, that sleeps). run it with a long enough sleep so that it'll go async //
+ ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ response = Unirest.post(processBasePath + "/" + processUUID + "/step/" + nextStep)
+ .multiPartContent()
+ .field("stepTimeoutMillis", String.valueOf(TIMEOUT))
+ .asString();
+
+ jsonObject = assertProcessStepWentAsyncResponse(response);
+ String jobUUID = jsonObject.getString("jobUUID");
+
+ ///////////////////////////////////
+ // sleep, to let that job finish //
+ ///////////////////////////////////
+ Thread.sleep(MORE_THAN_TIMEOUT);
+
+ ///////////////////////////////
+ // third, request job status //
+ ///////////////////////////////
+ response = Unirest.get(processBasePath + "/" + processUUID + "/status/" + jobUUID).asString();
+
+ jsonObject = assertProcessStepCompleteResponse(response);
+ String nextStep2 = jsonObject.getString("nextStep");
+ assertNotNull(nextStep2, "There be one more next step");
+ assertNotEquals(nextStep, nextStep2, "The next step should be different this time.");
+ assertTrue(jsonObject.getJSONObject("values").has("didSleep"), "There should be a value from the backend step");
+ }
+
+
+
+ /*******************************************************************************
+ ** test init'ing a process that goes async and then throws
+ **
+ *******************************************************************************/
+ @Test
+ public void test_processInitGoingAsyncThenThrowing() throws InterruptedException
+ {
+ String processBasePath = getBaseUrlAndPath() + "/processes/" + TestUtils.PROCESS_NAME_SIMPLE_THROW;
+ HttpResponse response = Unirest.post(processBasePath + "/init")
+ .multiPartContent()
+ .field("stepTimeoutMillis", String.valueOf(TIMEOUT))
+ .field("values", new JSONObject()
+ .put(TestUtils.SleeperStep.FIELD_SLEEP_MILLIS, MORE_THAN_TIMEOUT)
+ .toString())
+ .asString();
+
+ JSONObject jsonObject = assertProcessStepWentAsyncResponse(response);
+ String processUUID = jsonObject.getString("processUUID");
+ String jobUUID = jsonObject.getString("jobUUID");
+
+ /////////////////////////////////////////////
+ // request job status before sleep is done //
+ /////////////////////////////////////////////
+ response = Unirest.get(processBasePath + "/" + processUUID + "/status/" + jobUUID).asString();
+ jsonObject = assertProcessStepRunningResponse(response);
+
+ ///////////////////////////////////
+ // sleep, to let that job finish //
+ ///////////////////////////////////
+ Thread.sleep(MORE_THAN_TIMEOUT);
+
+ /////////////////////////////////////////////////////////////
+ // request job status again, get back error status instead //
+ /////////////////////////////////////////////////////////////
+ response = Unirest.get(processBasePath + "/" + processUUID + "/status/" + jobUUID).asString();
+ jsonObject = assertProcessStepErrorResponse(response);
+ }
+
+
+
+ /*******************************************************************************
+ ** every time a process step (or init) has gone async, expect what the
+ ** response should look like
+ *******************************************************************************/
+ private JSONObject assertProcessStepWentAsyncResponse(HttpResponse response)
+ {
+ assertEquals(200, response.getStatus());
+ JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
+
+ assertEquals("JOB_STARTED", jsonObject.getString("type"));
+
+ assertTrue(jsonObject.has("processUUID"), "Async-started response should have a processUUID");
+ assertTrue(jsonObject.has("jobUUID"), "Async-started response should have a jobUUID");
+
+ assertFalse(jsonObject.has("values"), "Async-started response should NOT have values");
+ assertFalse(jsonObject.has("error"), "Async-started response should NOT have error");
+
+ return (jsonObject);
+ }
+
+
+
+ /***************************************************************************
+ **
+ ***************************************************************************/
+ private JSONObject assertProcessStepRunningResponse(HttpResponse response)
+ {
+ assertEquals(200, response.getStatus());
+ JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
+
+ assertEquals("RUNNING", jsonObject.getString("type"), "Step Running response should have type=RUNNING");
+
+ assertFalse(jsonObject.has("values"), "Step Running response should NOT have values");
+ assertFalse(jsonObject.has("error"), "Step Running response should NOT have error");
+
+ return (jsonObject);
+ }
+
+
+
+ /*******************************************************************************
+ ** every time a process step (sync or async) completes, expect certain things
+ ** to be (and not to be) in the json response.
+ *******************************************************************************/
+ private JSONObject assertProcessStepCompleteResponse(HttpResponse response)
+ {
+ assertEquals(200, response.getStatus());
+ JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
+
+ assertEquals("COMPLETE", jsonObject.getString("type"), "Step Running response should have type=COMPLETE");
+ assertTrue(jsonObject.has("values"), "Step Complete response should have values");
+
+ assertFalse(jsonObject.has("jobUUID"), "Step Complete response should not have a jobUUID");
+ assertFalse(jsonObject.has("error"), "Step Complete response should not have an error");
+
+ return (jsonObject);
+ }
+
+
+
+ /*******************************************************************************
+ ** every time a process step (sync or async) has an error, expect certain things
+ ** to be (and not to be) in the json response.
+ *******************************************************************************/
+ private JSONObject assertProcessStepErrorResponse(HttpResponse response)
+ {
+ assertEquals(200, response.getStatus());
+ JSONObject jsonObject = JsonUtils.toJSONObject(response.getBody());
+
+ assertEquals("ERROR", jsonObject.getString("type"), "Step Running response should have type=ERROR");
+ assertTrue(jsonObject.has("error"), "Step Error response should have an error");
+
+ assertFalse(jsonObject.has("jobUUID"), "Step Error response should not have a jobUUID");
+ assertFalse(jsonObject.has("values"), "Step Error response should not have values");
+
+ return (jsonObject);
+ }
+
+}
\ No newline at end of file
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGeneratorTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGeneratorTest.java
new file mode 100644
index 00000000..bded2d84
--- /dev/null
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/ExecutorCodeGeneratorTest.java
@@ -0,0 +1,56 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.middleware.javalin.tools.codegenerators;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/*******************************************************************************
+ ** Unit test for ExecutorCodeGenerator
+ *******************************************************************************/
+class ExecutorCodeGeneratorTest
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void test() throws IOException
+ {
+ String rootPath = "/tmp/" + UUID.randomUUID() + "/";
+ File dir = new File(rootPath + "/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/executors/io");
+ assertTrue(dir.mkdirs());
+ new ExecutorCodeGenerator().writeAllFiles(rootPath, "SomeTest");
+
+ File anExpectedFile = new File(dir.getAbsolutePath() + "/SomeTestOutputInterface.java");
+ assertTrue(anExpectedFile.exists());
+
+ FileUtils.deleteDirectory(new File(rootPath));
+ }
+
+}
\ No newline at end of file
diff --git a/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGeneratorTest.java b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGeneratorTest.java
new file mode 100644
index 00000000..38023197
--- /dev/null
+++ b/qqq-middleware-javalin/src/test/java/com/kingsrook/qqq/middleware/javalin/tools/codegenerators/SpecCodeGeneratorTest.java
@@ -0,0 +1,56 @@
+/*
+ * QQQ - Low-code Application Framework for Engineers.
+ * Copyright (C) 2021-2024. Kingsrook, LLC
+ * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
+ * contact@kingsrook.com
+ * https://github.com/Kingsrook/
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.kingsrook.qqq.middleware.javalin.tools.codegenerators;
+
+
+import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
+import org.apache.commons.io.FileUtils;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+
+/*******************************************************************************
+ ** Unit test for SpecCodeGenerator
+ *******************************************************************************/
+class SpecCodeGeneratorTest
+{
+
+ /*******************************************************************************
+ **
+ *******************************************************************************/
+ @Test
+ void test() throws IOException
+ {
+ String rootPath = "/tmp/" + UUID.randomUUID() + "/";
+ File dir = new File(rootPath + "/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/middleware/javalin/specs/v1/responses");
+ assertTrue(dir.mkdirs());
+ new SpecCodeGenerator().writeAllFiles(rootPath, "v1", "SomeTest");
+
+ File anExpectedFile = new File(dir.getAbsolutePath() + "/SomeTestResponseV1.java");
+ assertTrue(anExpectedFile.exists());
+
+ FileUtils.deleteDirectory(new File(rootPath));
+ }
+
+}
\ No newline at end of file