Add third work package for REST API implementation
authorEduardo Ramos Testillano (eramedu) <eduardo.ramos.testillano@ericsson.com>
Mon, 11 May 2020 03:10:31 +0000 (05:10 +0200)
committerEduardo Ramos Testillano (eramedu) <eduardo.ramos.testillano@ericsson.com>
Mon, 11 May 2020 03:10:31 +0000 (05:10 +0200)
Start with FSM Testing

19 files changed:
example/diameter/launcher/EventOperation.cpp
example/diameter/launcher/EventOperation.hpp
example/diameter/launcher/Launcher.hpp
example/diameter/launcher/MyHandler.cpp
example/diameter/launcher/resources/HELP.md
example/diameter/launcher/resources/rest_api/ct/fsm-testing/test-clear_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-delay_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-description_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-ip-limit_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2c_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2e_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sh-command_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-timeout_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-hex_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-msg_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-hex_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-msg_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe_test.py [new file with mode: 0644]
example/diameter/launcher/resources/rest_api/ct/resources/condition-request.json [new file with mode: 0644]

index 935940d..7709fb1 100644 (file)
@@ -36,7 +36,8 @@
 //#include <anna/http/Transport.hpp>
 //#include <anna/diameter/stack/Engine.hpp>
 #include <anna/diameter/helpers/base/functions.hpp>
-//#include <anna/time/functions.hpp>
+#include <anna/time/functions.hpp>
+#include <anna/core/functions.hpp>
 //#include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
 //#include <anna/testing/defines.hpp>
 #include <anna/xml/xml.hpp>
@@ -50,7 +51,7 @@
 //#include <EventOperation.hpp>
 #include <MyDiameterEngine.hpp>
 #include <MyLocalServer.hpp>
-//#include <anna/testing/TestManager.hpp>
+#include <anna/testing/TestManager.hpp>
 //#include <anna/testing/TestCase.hpp>
 
 
@@ -577,8 +578,17 @@ bool EventOperation::answermsg_action_2c(std::string &response, const std::strin
 bool EventOperation::test_id__description(std::string &response, unsigned int id, const std::string & description) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try {
+    testManager.getTestCase(id)->setDescription(description); // creates / reuses
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid ip-limit";
+    return false;
+  }
 
   return true; // OK
 }
@@ -586,8 +596,17 @@ bool EventOperation::test_id__description(std::string &response, unsigned int id
 bool EventOperation::test_id__ip_limit(std::string &response, unsigned int id, int amount) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try {
+    testManager.getTestCase(id)->addIpLimit(amount); // creates / reuses
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid ip-limit";
+    return false;
+  }
 
   return true; // OK
 }
@@ -595,26 +614,55 @@ bool EventOperation::test_id__ip_limit(std::string &response, unsigned int id, i
 bool EventOperation::test_id__timeout(std::string &response, unsigned int id, int msecs) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try {
+    anna::Millisecond timeout = my_app.checkTimeMeasure("Test case timeout", anna::functions::asString(msecs));
+    testManager.getTestCase(id)->addTimeout(timeout); // creates / reuses
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid timeout";
+    return false;
+  }
 
   return true; // OK
 }
 
-bool EventOperation::test_id__sendmsg2e(std::string &response, unsigned int id, const std::string & diameterJson, int stepNumber) {
+bool EventOperation::test_id__sendmsg2e_2c(std::string &response, unsigned int id, bool _2e_or_2c, const std::string & diameterJson, int stepNumber) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load message !";
+    return false;
+  }
+  anna::diameter::codec::Message codecMsg; // auxiliary codec message
+  codecMsg.loadXMLString(diameterXml);
 
+  LOGWARNING(
+     if (!codecMsg.isRequest() && (stepNumber == -1))
+      anna::Logger::warning("Step number has not been provided. Take into account that this answer message will be sent 'as is' and sequence information could be wrong at the remote peer", ANNA_FILE_LOCATION);
+  );
 
-  return true; // OK
-}
-
-bool EventOperation::test_id__sendmsg2c(std::string &response, unsigned int id, const std::string & diameterJson, int stepNumber) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
+  try {
+    my_app.updateOperatedOriginHostWithMessage(codecMsg);
+    if (_2e_or_2c)
+      testManager.getTestCase(id)->addSendDiameterXml2e(codecMsg.code(), my_app.getOperatedHost(), stepNumber); // creates / reuses
+    else
+      testManager.getTestCase(id)->addSendDiameterXml2c(codecMsg.code(), my_app.getOperatedHost(), stepNumber); // creates / reuses
 
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
@@ -622,8 +670,18 @@ bool EventOperation::test_id__sendmsg2c(std::string &response, unsigned int id,
 bool EventOperation::test_id__delay(std::string &response, unsigned int id, int msecs) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try {
+    anna::Millisecond delay = ((msecs == 0 /* special case */) ? (anna::Millisecond)0 : my_app.checkTimeMeasure("Test case delay step", anna::functions::asString(msecs)));
+    testManager.getTestCase(id)->addDelay(delay); // creates / reuses
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "invalid delay";
+    return false;
+  }
 
   return true; // OK
 }
@@ -631,62 +689,164 @@ bool EventOperation::test_id__delay(std::string &response, unsigned int id, int
 bool EventOperation::test_id__sh_command(std::string &response, unsigned int id, const std::string & script) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
-
-  return true; // OK
-}
-
-bool EventOperation::test_id__waitfe_hex(std::string &response, unsigned int id, const std::string & hex, bool strict) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
-
+  try {
+    testManager.getTestCase(id)->addCommand(script); // creates / reuses
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
 
-bool EventOperation::test_id__waitfc_hex(std::string &response, unsigned int id, const std::string & hex, bool strict) {
+bool EventOperation::test_id__waitfefc_hex(std::string &response, unsigned int id, bool fe_or_fc, const std::string & hex, bool strict) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
+  // Get DataBlock from hex content:
+  anna::DataBlock db_aux(true);
+  std::string hexString = hex;
+  hexString.erase(std::remove(hexString.begin(), hexString.end(), ':'), hexString.end());
+  LOGDEBUG(
+      std::string msg = "Hex string (remove colons if exists): ";
+  msg += hexString;
+  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+  );
+
+  std::string regexp;
+  try {
+    anna::functions::fromHexString(hexString, db_aux); // could launch exception
+    regexp = anna::functions::asHexString(db_aux);
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
+  // optional 'full':
+  if(!strict) {
+    //// If request, we will ignore sequence data:
+    //if (anna::diameter::codec::functions::requestBit(db_aux))
+    regexp.replace (24, 16, "[0-9A-Fa-f]{16}");
 
-  return true; // OK
-}
-
-bool EventOperation::test_id__waitfe_msg(std::string &response, unsigned int id, const std::string & diameterJson, bool strict) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
+    regexp.insert(0, "^");
+    regexp += "$";
+  }
 
+  try {
+    testManager.getTestCase(id)->addWaitDiameterRegexpHex(fe_or_fc, regexp);
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
 
-bool EventOperation::test_id__waitfc_msg(std::string &response, unsigned int id, const std::string & diameterJson, bool strict) {
+bool EventOperation::test_id__waitfefc_msg(std::string &response, unsigned int id, bool fe_or_fc, const std::string & diameterJson, bool strict) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
+  bool success;
+  std::string diameterXml = anna::json::functions::json2xml(diameterJson, success);
+  if (!success) {
+    response += "json to xml failed, unable to load message !";
+    return false;
+  }
 
+  try {
+    anna::diameter::codec::Message codecMsg; // auxiliary codec message
+    codecMsg.loadXMLString(diameterXml);
+    std::string regexp = codecMsg.asXMLString(true /* normalization */);
+
+    // Now we must insert regular expressions in hop-by-hop, end-to-end and Origin-State-Id:
+
+    // optional 'full':
+    if(!strict) {
+      std::string::size_type pos, pos_1, pos_2;
+
+      pos = regexp.find("end-to-end-id=", 0u);
+      pos = regexp.find("\"", pos);
+      pos_1 = pos;
+      pos = regexp.find("\"", pos+1);
+      pos_2 = pos;
+      regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+
+      pos = regexp.find("hop-by-hop-id=", 0u);
+      pos = regexp.find("\"", pos);
+      pos_1 = pos;
+      pos = regexp.find("\"", pos+1);
+      pos_2 = pos;
+      regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+
+      // For this representation: <avp name="Origin-State-Id" data="1428633668"/>
+      //pos = regexp.find("Origin-State-Id", 0u);
+      //pos = regexp.find("\"", pos);
+      //pos = regexp.find("\"", pos+1);
+      //pos_1 = pos;
+      //pos = regexp.find("\"", pos+1);
+      //pos_2 = pos;
+      //regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+      // But we have this one: <avp data="1428633668" name="Origin-State-Id"/>
+      pos = regexp.find("Origin-State-Id", 0u);
+      pos = regexp.rfind("\"", pos);
+      pos = regexp.rfind("\"", pos-1);
+      pos = regexp.rfind("\"", pos-1);
+      pos_1 = pos;
+      pos = regexp.find("\"", pos+1);
+      pos_2 = pos;
+      regexp.replace(pos_1 + 1, pos_2 - pos_1 - 1, "[0-9]+");
+
+      //regexp.insert(0, "^");
+      //regexp += "$";
+    }
 
-  return true; // OK
-}
-
-bool EventOperation::test_id__waitfe(std::string &response, unsigned int id, const std::string & condition) {
-
-  Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
-
-
+    testManager.getTestCase(id)->addWaitDiameterRegexpXml(fe_or_fc, regexp);
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
 
-bool EventOperation::test_id__waitfc(std::string &response, unsigned int id, const std::string & condition) {
+bool EventOperation::test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
+                         const std::string & code,
+                         const std::string & bitR,
+                         const std::string & hopByHop,
+                         const std::string & applicationId,
+                         const std::string & sessionId,
+                         const std::string & resultCode,
+                         const std::string & msisdn,
+                         const std::string & imsi,
+                         const std::string & serviceContextId) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try { // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
+    testManager.getTestCase(id)->addWaitDiameter(fe_or_fc, code, bitR, hopByHop, applicationId, sessionId, resultCode, msisdn, imsi, serviceContextId);
+    response = "Done";
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
@@ -814,8 +974,21 @@ bool EventOperation::test__finished(std::string &response) {
 bool EventOperation::test__clear(std::string &response) {
 
   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
+  anna::testing::TestManager &testManager = anna::testing::TestManager::instantiate();
 
-
+  try {
+    if (testManager.clearPool()) {
+      response = "All the programmed test cases have been dropped";
+    }
+    else {
+      response = "There are not programmed test cases to be removed";
+    }
+  }
+  catch(anna::RuntimeException &ex) {
+    ex.trace();
+    response += "failed";
+    return false;
+  }
 
   return true; // OK
 }
index be117cf..6fceb85 100644 (file)
@@ -110,16 +110,21 @@ public:
   bool test_id__description(std::string &response, unsigned int id, const std::string & description);
   bool test_id__ip_limit(std::string &response, unsigned int id, int amount = 1);
   bool test_id__timeout(std::string &response, unsigned int id, int msecs);
-  bool test_id__sendmsg2e(std::string &response, unsigned int id, const std::string & diameterJson, int stepNumber = -1);
-  bool test_id__sendmsg2c(std::string &response, unsigned int id, const std::string & diameterJson, int stepNumber = -1);
+  bool test_id__sendmsg2e_2c(std::string &response, unsigned int id, bool _2e_or_2c, const std::string & diameterJson, int stepNumber = -1);
   bool test_id__delay(std::string &response, unsigned int id, int msecs);
   bool test_id__sh_command(std::string &response, unsigned int id, const std::string & script);
-  bool test_id__waitfe_hex(std::string &response, unsigned int id, const std::string & hex, bool strict = false);
-  bool test_id__waitfc_hex(std::string &response, unsigned int id, const std::string & hex, bool strict = false);
-  bool test_id__waitfe_msg(std::string &response, unsigned int id, const std::string & diameterJson, bool strict = false);
-  bool test_id__waitfc_msg(std::string &response, unsigned int id, const std::string & diameterJson, bool strict = false);
-  bool test_id__waitfe(std::string &response, unsigned int id, const std::string & condition);
-  bool test_id__waitfc(std::string &response, unsigned int id, const std::string & condition);
+  bool test_id__waitfefc_hex(std::string &response, unsigned int id, bool fe_or_fc, const std::string & hex, bool strict = false);
+  bool test_id__waitfefc_msg(std::string &response, unsigned int id, bool fe_or_fc, const std::string & diameterJson, bool strict = false);
+  bool test_id__waitfefc(std::string &response, unsigned int id, bool fe_or_fc,
+                         const std::string & code,
+                         const std::string & bitR,
+                         const std::string & hopByHop,
+                         const std::string & applicationId,
+                         const std::string & sessionId,
+                         const std::string & resultCode,
+                         const std::string & msisdn,
+                         const std::string & imsi,
+                         const std::string & serviceContextId);
 
   // Testcases execution
   // test__<command>
index c8f34e6..4a7f392 100644 (file)
@@ -75,7 +75,6 @@ class Launcher : public anna::comm::Application {
   std::string getSignalUSR2OutputFile() const throw();
 
   void servicesFromXML(const anna::xml::Node* servicesNode, bool bindResources) throw(anna::RuntimeException);
-  anna::Millisecond checkTimeMeasure(const std::string &parameter, const std::string &value) throw(anna::RuntimeException);
   void initialize() throw(anna::RuntimeException); // HTTP
   void run() throw(anna::RuntimeException);
 
@@ -85,6 +84,8 @@ public:
   Launcher();
   //~Launcher(); TODO
 
+  anna::Millisecond checkTimeMeasure(const std::string &parameter, const std::string &value) throw(anna::RuntimeException);
+
   void loadServicesFromFile(const std::string & xmlPathFile, bool bindResources) throw(anna::RuntimeException);
   void loadServicesFromXMLString(const std::string & xmlString, bool bindResources) throw(anna::RuntimeException);
   void startServices() throw(anna::RuntimeException);
index 49205ed..7d312ef 100644 (file)
@@ -158,7 +158,7 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   // Node management
   if (opType == "/node") {
     auto it = j.find("name");
-    if (it != j.end())
+    if (it != j.end() && it->is_string())
       result = eop.node(response, *it);
     else
       response += "missing 'name' string field";
@@ -170,21 +170,21 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   // Parsing operations
   else if (opType == "/code") {
     auto it = j.find("diameterJson");
-    if (it != j.end())
+    if (it != j.end() && it->is_object())
       result = eop.code(response, it->dump(4)); // get the object as string (always indentation = 4)
     else
       response += "missing 'diameterJson' object field";
   }
   else if (opType == "/decode") {
     auto it = j.find("diameterHex");
-    if (it != j.end())
+    if (it != j.end() && it->is_string())
       result = eop.decode(response, *it);
     else
       response += "missing 'diameterHex' string field";
   }
   else if (opType == "/loadmsg") {
     auto it = j.find("diameterJson");
-    if (it != j.end())
+    if (it != j.end() && it->is_object())
       result = eop.loadmsg(response, it->dump(4)); // get the object as string (always indentation = 4)
     else
       response += "missing 'diameterJson' object field";
@@ -193,7 +193,7 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   // Hot changes
   else if (opType == "/services") {
     auto it = j.find("servicesJson");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_object()) {
       result = eop.services(response, it->dump(4)); // get the object as string (always indentation = 4)
     }
     else
@@ -201,28 +201,28 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/diameterServerSessions") {
     auto it = j.find("sessions");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.diameterServerSessions(response, it->get<int>());
     else
       response += "missing 'session' integer field";
   }
   else if (opType == "/change-dir") {
     auto it = j.find("directory");
-    std::string directory = (it != j.end()) ? *it : ""; // default is: restore initial directory
+    std::string directory = (it != j.end() && it->is_string()) ? *it : ""; // default is: restore initial directory
     result = eop.change_dir(response, directory);
   }
 
   // Client sessions visibility
   else if (opType == "/visibility") {
     auto it = j.find("action");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_string()) {
       std::string action = *it;
 
       it = j.find("addressPort");
-      std::string addressPort = (it != j.end()) ? *it : "";
+      std::string addressPort = (it != j.end() && it->is_string()) ? *it : "";
 
       it = j.find("socket");
-      int socket = (it != j.end()) ? it->get<int>() : -1;
+      int socket = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -1;
 
       result = eop.visibility(response, action, addressPort, socket);
     }
@@ -236,7 +236,7 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/context") {
     auto it = j.find("targetFile");
-    std::string targetFile = (it != j.end()) ? *it : "";
+    std::string targetFile = (it != j.end() && it->is_string()) ? *it : "";
     result = eop.context(response, targetFile);
   }
   else if (opType == "/forceCountersRecord") {
@@ -244,16 +244,14 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/log-statistics-samples") {
     auto it = j.find("list");
-    if (it != j.end())
-      result = eop.log_statistics_samples(response, *it);
-    else
-      response += "missing 'list' string field";
+    std::string list = (it != j.end() && it->is_string()) ? *it : "list";
+    result = eop.log_statistics_samples(response, list);
   }
 
   // Flow operations
   else if ((opType == "/sendmsg2e")||(opType == "/sendmsg2c")) {
     auto itJ = j.find("diameterJson");
-    if (itJ != j.end()) {
+    if (itJ != j.end() && itJ->is_object()) {
       if (opType == "/sendmsg2e")
         result = eop.sendmsg_hex_2e(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
       else
@@ -264,7 +262,7 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if ((opType == "/sendhex2e")||(opType == "/sendhex2c")) {
     auto itH = j.find("diameterHex");
-    if (itH != j.end())
+    if (itH != j.end() && itH->is_string())
       if (opType == "/sendhex2e")
         result = eop.sendmsg_hex_2e(response, *itH, false);
       else
@@ -275,8 +273,8 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   else if ((opType == "/answermsg2e")||(opType == "/answermsg2c")) {
     auto itJ = j.find("diameterJson");
     auto itA = j.find("action");
-    bool hasJ = (itJ != j.end());
-    bool hasA = (itA != j.end());
+    bool hasJ = (itJ != j.end() && itJ->is_object());
+    bool hasA = (itA != j.end() && itA->is_string());
 
     if (hasJ != hasA) { // XOR
       std::string action;
@@ -298,85 +296,103 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   // test_id__<command>
   else if (opType == "/testid-description") {
     auto it = j.find("description");
-    if (it != j.end())
+    if (it != j.end() && it->is_string())
       result = eop.test_id__description(response, atoi(param1.c_str()), *it);
     else
       response += "missing 'description' string field";
   }
   else if (opType == "/testid-ip-limit") {
     auto it = j.find("amount");
-    int amount = (it != j.end()) ? it->get<int>() : 1;
+    int amount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : 1;
     result = eop.test_id__ip_limit(response, atoi(param1.c_str()), amount);
   }
   else if (opType == "/testid-timeout") {
     auto it = j.find("msecs");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test_id__timeout(response, atoi(param1.c_str()), it->get<int>());
     else
       response += "missing 'msecs' integer field";
   }
   else if ((opType == "/testid-sendmsg2e")||(opType == "/testid-sendmsg2c")) {
     auto it = j.find("diameterJson");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_object()) {
 
       auto itS = j.find("stepNumber");
-      int stepNumber = (itS != j.end()) ? itS->get<int>() : -1;
+      int stepNumber = (itS != j.end() && itS->is_number_integer()) ? itS->get<int>() : -1;
 
-      if (opType == "/testid-sendmsg2e")
-        result = eop.test_id__sendmsg2e(response, atoi(param1.c_str()), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
-      else
-        result = eop.test_id__sendmsg2c(response, atoi(param1.c_str()), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
+      result = eop.test_id__sendmsg2e_2c(response, atoi(param1.c_str()),
+                                        (opType == "/testid-sendmsg2e"), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
     }
     else
       response += "missing 'diameterJson' object field";
   }
   else if (opType == "/testid-delay") {
     auto it = j.find("msecs");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test_id__delay(response, atoi(param1.c_str()), it->get<int>());
     else
       response += "missing 'msecs' integer field";
   }
   else if (opType == "/testid-sh-command") {
     auto it = j.find("script");
-    if (it != j.end())
+    if (it != j.end() && it->is_string())
       result = eop.test_id__sh_command(response, atoi(param1.c_str()), *it);
     else
       response += "missing 'script' string field";
   }
   else if ((opType == "/testid-waitfe-hex")||(opType == "/testid-waitfc-hex")) {
     auto it = j.find("hex");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_string()) {
 
       auto itS = j.find("strict");
-      bool strict = (itS != j.end()) ? (*itS == "true") : false;
+      bool strict = (itS != j.end() && itS->is_string()) ? (*itS == "true") : false;
 
-      if (opType == "/testid-waitfe-hex")
-        result = eop.test_id__waitfe_hex(response, atoi(param1.c_str()), *it, strict);
-      else
-        result = eop.test_id__waitfc_hex(response, atoi(param1.c_str()), *it, strict);
+      result = eop.test_id__waitfefc_hex(response, atoi(param1.c_str()), (opType == "/testid-waitfe-hex"), *it, strict);
     }
     else
       response += "missing 'hex' string field";
   }
   else if ((opType == "/testid-waitfe-msg")||(opType == "/testid-waitfc-msg")) {
     auto it = j.find("diameterJson");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_object()) {
 
       auto itS = j.find("strict");
-      bool strict = (itS != j.end()) ? (*itS == "true") : false;
+      bool strict = (itS != j.end() && itS->is_string()) ? (*itS == "true") : false;
 
-      if (opType == "/testid-waitfe-msg")
-        result = eop.test_id__waitfe_msg(response, atoi(param1.c_str()), it->dump(4), strict); // get the object as string (always indentation = 4)
-      else
-        result = eop.test_id__waitfc_msg(response, atoi(param1.c_str()), it->dump(4), strict); // get the object as string (always indentation = 4)
+      result = eop.test_id__waitfefc_msg(response, atoi(param1.c_str()), (opType == "/testid-waitfe-msg"), it->dump(4), strict); // get the object as string (always indentation = 4)
     }
     else
       response += "missing 'diameterJson' object field";
   }
   else if ((opType == "/testid-waitfe")||(opType == "/testid-waitfc")) {
     auto it = j.find("condition");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_object()) {
+
+/*
+      auto j2 = it->get<nlohmann::json::object_t>();
+
+      // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
+      auto it_code = j2.find("code");
+      auto it_bitR = j2.find("bitR");
+      auto it_hopByHop = j2.find("hopByHop");
+      auto it_applicationId = j2.find("applicationId");
+      auto it_sessionId = j2.find("sessionId");
+      auto it_resultCode = j2.find("resultCode");
+      auto it_msisdn = j2.find("msisdn");
+      auto it_imsi = j2.find("imsi");
+      auto it_serviceContextId = j2.find("serviceContextId");
+
+      std::string p1 = (it_code != j2.end() && it_code->is_string()) ? *it_code : "";
+      std::string p2 = (it_bitR != j2.end() && it_bitR->is_string()) ? *it_bitR : "";
+      std::string p3 = (it_hopByHop != it->end() && it_hopByHop->is_string()) ? *it_hopByHop : "";
+      std::string p4 = (it_applicationId != it->end() && it_applicationId->is_string()) ? *it_applicationId : "";
+      std::string p5 = (it_sessionId != it->end() && it_sessionId->is_string()) ? *it_sessionId : "";
+      std::string p6 = (it_resultCode != it->end() && it_resultCode->is_string()) ? *it_resultCode : "";
+      std::string p7 = (it_msisdn != it->end() && it_msisdn->is_string()) ? *it_msisdn : "";
+      std::string p8 = (it_imsi != it->end() && it_imsi->is_string()) ? *it_imsi : "";
+      std::string p9 = (it_serviceContextId != it->end() && it_serviceContextId->is_string()) ? *it_serviceContextId : "";
+*/
+
       // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
       auto it_code = it->find("code");
       auto it_bitR = it->find("bitR");
@@ -388,21 +404,17 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
       auto it_imsi = it->find("imsi");
       auto it_serviceContextId = it->find("serviceContextId");
 
-      std::string condition;
-      condition += (it_code != it->end()) ? *it_code : ""; condition += "|";
-      condition += (it_bitR != it->end()) ? *it_bitR : ""; condition += "|";
-      condition += (it_hopByHop != it->end()) ? *it_hopByHop : ""; condition += "|";
-      condition += (it_applicationId != it->end()) ? *it_applicationId : ""; condition += "|";
-      condition += (it_sessionId != it->end()) ? *it_sessionId : ""; condition += "|";
-      condition += (it_resultCode != it->end()) ? *it_resultCode : ""; condition += "|";
-      condition += (it_msisdn != it->end()) ? *it_msisdn : ""; condition += "|";
-      condition += (it_imsi != it->end()) ? *it_imsi : ""; condition += "|";
-      condition += (it_serviceContextId != it->end()) ? *it_serviceContextId : "";
-
-      if (opType == "/testid-waitfe")
-        result = eop.test_id__waitfe(response, atoi(param1.c_str()), condition);
-      else
-        result = eop.test_id__waitfc(response, atoi(param1.c_str()), condition);
+      std::string p1 = (it_code != it->end() && it_code->is_string()) ? *it_code : "";
+      std::string p2 = (it_bitR != it->end() && it_bitR->is_string()) ? *it_bitR : "";
+      std::string p3 = (it_hopByHop != it->end() && it_hopByHop->is_string()) ? *it_hopByHop : "";
+      std::string p4 = (it_applicationId != it->end() && it_applicationId->is_string()) ? *it_applicationId : "";
+      std::string p5 = (it_sessionId != it->end() && it_sessionId->is_string()) ? *it_sessionId : "";
+      std::string p6 = (it_resultCode != it->end() && it_resultCode->is_string()) ? *it_resultCode : "";
+      std::string p7 = (it_msisdn != it->end() && it_msisdn->is_string()) ? *it_msisdn : "";
+      std::string p8 = (it_imsi != it->end() && it_imsi->is_string()) ? *it_imsi : "";
+      std::string p9 = (it_serviceContextId != it->end() && it_serviceContextId->is_string()) ? *it_serviceContextId : "";
+
+      result = eop.test_id__waitfefc(response, atoi(param1.c_str()), (opType == "/testid-waitfe"), p1, p2, p3, p4, p5, p6, p7, p8, p9);
     }
     else
       response += "missing 'condition' object field";
@@ -412,51 +424,51 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   // test__<command>
   else if (opType == "/test-ttps") {
     auto it = j.find("amount");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test__ttps(response, it->get<int>());
     else
       response += "missing 'amount' integer field";
   }
   else if (opType == "/test-next") {
     auto it = j.find("syncAmount");
-    int syncAmount = (it != j.end()) ? it->get<int>() : 1;
+    int syncAmount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : 1;
     result = eop.test__next(response, syncAmount);
   }
   else if (opType == "/test-ip-limit") {
     auto it = j.find("amount");
-    int amount = (it != j.end()) ? it->get<int>() : -2; // default is: show current ip-limit and in-progress test cases amount
+    int amount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -2; // default is: show current ip-limit and in-progress test cases amount
     result = eop.test__ip_limit(response, amount);
   }
   else if (opType == "/test-goto") {
     auto it = j.find("id");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test__goto(response, it->get<int>());
     else
       response += "missing 'id' integer field";
   }
   else if (opType == "/test-run") {
     auto it = j.find("id");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test__run(response, it->get<int>());
     else
       response += "missing 'id' integer field";
   }
   else if (opType == "/test-look") {
     auto it = j.find("id");
-    int id = (it != j.end()) ? it->get<int>() : -1; // default is: current
+    int id = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -1; // default is: current
     result = eop.test__look(response, id);
   }
   else if (opType == "/test-state") {
     auto it = j.find("id");
-    int id = (it != j.end()) ? it->get<int>() : -1; // default is: current
+    int id = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -1; // default is: current
     result = eop.test__state(response, id);
   }
   else if (opType == "/test-interact") {
     auto it = j.find("amount");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_number_integer()) {
 
       auto itI = j.find("id");
-      int id = (itI != j.end()) ? itI->get<int>() : -1; // default is: current
+      int id = (itI != j.end() && itI->is_number_integer()) ? itI->get<int>() : -1; // default is: current
 
       result = eop.test__interact(response, it->get<int>(), id);
     }
@@ -465,10 +477,10 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/test-reset") {
     auto it = j.find("type");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_string()) {
 
       auto itI = j.find("id");
-      int id = (itI != j.end()) ? itI->get<int>() : -2; // default is: apply to all the tests
+      int id = (itI != j.end() && itI->is_number_integer()) ? itI->get<int>() : -2; // default is: apply to all the tests
 
       if ((*it == "soft") || (*it == "hard")) {
         result = eop.test__reset(response, (*it == "soft"), id);
@@ -481,14 +493,14 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/test-repeats") {
     auto it = j.find("amount");
-    if (it != j.end())
+    if (it != j.end() && it->is_number_integer())
       result = eop.test__repeats(response, it->get<int>());
     else
       response += "missing 'amount' integer field";
   }
   else if (opType == "/test-auto-reset") {
     auto it = j.find("type");
-    if (it != j.end()) {
+    if (it != j.end() && it->is_string()) {
 
       if ((*it == "soft") || (*it == "hard")) {
         result = eop.test__auto_reset(response, (*it == "soft"));
@@ -510,8 +522,10 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/test-junit") {
     auto it = j.find("targetFile");
-    std::string targetFile = (it != j.end()) ? *it : ""; // default is: get junit on response instead dumping on file
-    result = eop.test__junit(response, targetFile);
+    if (it != j.end() && it->is_string())
+      result = eop.test__junit(response, *it);
+    else
+      response += "missing 'targetFile' string field";
   }
   else if (opType == "/test-summary-counts") {
     result = eop.test__summary_counts(response);
@@ -524,10 +538,10 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   }
   else if (opType == "/test-report") {
     auto it = j.find("state");
-    std::string state = (it != j.end()) ? *it : "all"; // initialized|in-progress|failed|success|[all]|none
+    std::string state = (it != j.end() && it->is_string()) ? *it : "all"; // initialized|in-progress|failed|success|[all]|none
 
     auto itA = j.find("action");
-    std::string action = (itA != j.end()) ? *itA : "enable"; // default is: enable
+    std::string action = (itA != j.end() && itA->is_string()) ? *itA : "enable"; // default is: enable
 
     if ((action == "enable") || (action == "disable")) {
       result = eop.test__report(response, state, (action == "enable"));
@@ -538,7 +552,7 @@ bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::str
   else if ((opType == "/test-report-hex")||(opType == "/test-dump_stdout")) {
 
     auto itA = j.find("action");
-    std::string action = (itA != j.end()) ? *itA : "enable"; // default is: enable
+    std::string action = (itA != j.end() && itA->is_string()) ? *itA : "enable"; // default is: enable
 
     if ((action == "enable") || (action == "disable")) {
       bool enable = (action == "enable");
index fec3b72..bef7e3f 100644 (file)
@@ -960,7 +960,7 @@ Updates diameter server sessions to be accepted.
 
 ```
 {
-    "sessions":<integer value>
+    "sessions":<sessions (integer)>
 }
 ```
 
@@ -1004,7 +1004,7 @@ Updates ADML working directory.
 {
     "action":"<hide|show|hidden|shown>"
     [, "addressPort":"<address:port>"]
-    [, "socket":<socket>]
+    [, "socket":<socket id (integer)>]
 }
 ```
 
@@ -1036,13 +1036,13 @@ Reset statistics and counters.
 
 #### POST /context
 
-Dump ADML context at file path provided. If empty, default path is selected. Context information is not retrieved in the response, so, file is related to ADML execution context.
+Dump ADML context at file path provided. If empty (or field missing), default path is selected. Context information is not retrieved in the response, so, file is related to ADML execution context.
 
 **Request body**:
 
 ```
 {
-    "targetFile":"[file path]"
+    ["targetFile":"[file path]"]
 }
 ```
 
@@ -1078,7 +1078,7 @@ Set the statistics concepts to be logged. To know the concept indentifiers regis
 
 ```
 {
-    "list":"<comma-separated list|all|none>"
+    ["list":"<comma-separated list|[all]|none>"]
 }
 ```
 
@@ -1153,7 +1153,7 @@ or
 
 ```
 {
-    "action":"<action: list|dump|clear|exhaust|rotate>"
+    "action":"[action: <[list]|dump|clear|exhaust|rotate>]"
 }
 ```
 
@@ -1184,7 +1184,7 @@ or
 
 ```
 {
-    "action":"<action: list|dump|clear|exhaust|rotate>"
+    "action":"[action: <[list]|dump|clear|exhaust|rotate>]"
 }
 ```
 
@@ -1243,21 +1243,651 @@ Sends diameter expressed in hexadecimal string (no spaces, no colons, i.e.: `010
 
 ADML implements a bulting *Finite State Machine* to plan testing flows with a great flexibility.
 
-#### POST /xxxxxxxx
+#### POST /testid-description
 
-Referred files (*dictionaries, cer, cea, etc.*) shall be accesible for ADML and are not provided in this operation.
+**Request body**:
+
+```
+{
+    "description":"<description (string)>"
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-ip-limit
+
+In-Progress limit is the maximum number of tests which can be executed in parallel.
+This operation allows a specific test to set this global pool behaviour.
+
+**Request body**:
+
+```
+{
+    "amount":[amount (integer, 1 by default: execution in sequence)]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-timeout
+
+**Request body**:
+
+```
+{
+    "msecs":<milliseconds (integer)>
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-sendmsg2e
+
+**Request body**:
+
+```
+{
+    "diameterJson":<diameter message json object>
+    [,"stepNumber":[amount (integer, -1 no step associated)]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-sendmsg2c
+
+**Request body**:
+
+```
+{
+    "diameterJson":<diameter message json object>
+    [,"stepNumber":[amount (integer, -1 no step associated)]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-delay
+
+**Request body**:
+
+```
+{
+    "msecs":<milliseconds (integer)>
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-sh-command
+
+**Request body**:
+
+```
+{
+    "script":"<script>"
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfe-hex
+
+**Request body**:
+
+```
+{
+    "hex":"<hex string>"
+    [,"strict":"[<true|[false]>]"]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfc-hex
+
+**Request body**:
+
+```
+{
+    "hex":"<hex string>"
+    [,"strict":"[<true|[false]>]"]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfe-msg
+
+**Request body**:
+
+```
+{
+    "diameterJson":<diameter message json object>
+    [,"strict":"[<true|[false]>]"]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfc-msg
+
+**Request body**:
+
+```
+{
+    "diameterJson":<diameter message json object>
+    [,"strict":"[<true|[false]>]"]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfe
+
+**Request body**:
+
+```
+{
+    "condition": {
+      "code":"[(integer)]",
+      "bitR":"[(1|0)]",
+      "hopByHop":"[(integer)]",
+      "applicationId":"[(integer)]",
+      "sessionId":"[session id]",
+      "resultCode":"[(integer)]",
+      "msisdn":"[msisdn]",
+      "imsi":"[imsi]",
+      "serviceContextId":"[service context id]"
+    }
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /testid-waitfc
+
+**Request body**:
+
+```
+{
+    "condition": {
+      "code":"[(integer)]",
+      "bitR":"[(1|0)]",
+      "hopByHop":"[(integer)]",
+      "applicationId":"[(integer)]",
+      "sessionId":"[session id]",
+      "resultCode":"[(integer)]",
+      "msisdn":"[msisdn]",
+      "imsi":"[imsi]",
+      "serviceContextId":"[service context id]"
+    }
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+### EXECUTION ACTIONS
+
+#### POST /test-ttps
+
+**Request body**:
+
+```
+{
+    "amount":<amount (integer)>
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-next
+
+**Request body**:
+
+```
+{
+    ["syncAmount":[amount (integer: 1 by default)]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-ip-limit
+
+Global operation (also at test identifier level is accessible).
+
+**Request body**:
+
+```
+{
+    ["amount":[amount (integer, shows current ip-limit and in-progress test cases amount, by default (-2))]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-goto
+
+**Request body**:
+
+```
+{
+    "id":<test identifier>
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
 
-**Request body**: { "servicesJson":<services json object>}
+#### POST /test-run
 
-**Response body**: { "result":"<true or false>", "response":"<response>"}
+**Request body**:
 
-#### POST /xxxxxxxx
+```
+{
+    "id":<test identifier>
+}
+```
 
-Referred files (*dictionaries, cer, cea, etc.*) shall be accesible for ADML and are not provided in this operation.
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-look
+
+**Request body**:
+
+```
+{
+     ["id":[test identifier (integer: current by default (-1))]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-state
+
+**Request body**:
+
+```
+{
+     ["id":[test identifier (integer: current by default (-1))]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-interact
+
+**Request body**:
+
+```
+{
+     "amount":<amount (integer)>
+     [,"id":[test identifier (integer: current by default (-1))]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-reset
+
+**Request body**:
+
+```
+{
+     "type":"<soft|hard>"
+     [,"id":[test identifier (integer: apply to all the tests (-2))]]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-repeats
+
+**Request body**:
+
+```
+{
+     "amount":<amount (integer)>
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-auto-reset
+
+**Request body**:
+
+```
+{
+     "type":"<soft|hard>"
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-initialized
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-finished
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-clear
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-junit
+
+**Request body**:
+
+```
+{
+    "targetFile":"<file path>"
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-summary-counts
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-summary-states
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-summary
+
+**Request body**: none
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-report
+
+**Request body**:
+
+```
+{
+    ["state":"[<initialized|in-progress|failed|success|[all]|none>]"]
+    [,"action":"[<[enable]|disable>]"]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
 
-**Request body**: { "servicesJson":<services json object>}
+#### POST /test-report-hex
 
-**Response body**: { "result":"<true or false>", "response":"<response>"}
+**Request body**:
+
+```
+{
+    ["action":"[<[enable]|disable>]"]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
+
+#### POST /test-dump_stdout
+
+**Request body**:
+
+```
+{
+    ["action":"[<[enable]|disable>]"]
+}
+```
+
+**Response body**:
+
+```
+{
+    "result":"<true or false>",
+    "response":"<response>"
+}
+```
 
 
 
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/test-clear_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/test-clear_test.py
new file mode 100644 (file)
index 0000000..2c07810
--- /dev/null
@@ -0,0 +1,14 @@
+import pytest
+
+
+@pytest.mark.run(order=1)
+def test_001_i_want_to_test_pool_when_it_is_already_empty(admlc):
+
+  responseBodyRef = { "success":"true", "response":"There are not programmed test cases to be removed" }
+
+  # Send POST
+  response = admlc.post("/test-clear")
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-delay_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-delay_test.py
new file mode 100644 (file)
index 0000000..8978c7e
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_i_want_to_set_test_id_delay(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "msecs":500 }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-delay/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-description_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-description_test.py
new file mode 100644 (file)
index 0000000..d44cb83
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_i_want_to_set_test_id_description(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "description":"testid-description.test_001.flow{}".format(flow) }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-description/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-ip-limit_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-ip-limit_test.py
new file mode 100644 (file)
index 0000000..ed4e330
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_i_want_to_set_test_id_ip_limit(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "amount":5 }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-ip-limit/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2c_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2c_test.py
new file mode 100644 (file)
index 0000000..19affa3
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_diameter_json_representation_i_want_to_add_test_id_sendmsg2c_step(resources, admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = resources("aar-diameterJsonFromOwnToAF-request.json")
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.post("/testid-sendmsg2c/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2e_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sendmsg2e_test.py
new file mode 100644 (file)
index 0000000..e25c2ea
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_diameter_json_representation_i_want_to_add_test_id_sendmsg2e_step(resources, admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = resources("aar-diameterJsonFromOwnToAF-request.json")
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.post("/testid-sendmsg2e/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sh-command_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-sh-command_test.py
new file mode 100644 (file)
index 0000000..15c811d
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_i_want_to_set_test_id_sh_command(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "script":"date" }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-sh-command/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-timeout_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-timeout_test.py
new file mode 100644 (file)
index 0000000..025b236
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_i_want_to_set_test_id_timeout(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "msecs":3000 }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-timeout/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-hex_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-hex_test.py
new file mode 100644 (file)
index 0000000..ad0e052
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_hex_representation_i_want_to_add_test_id_waitfc_hex_step(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "hex":"01000150c000010901000014" }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-waitfc-hex/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-msg_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfc-msg_test.py
new file mode 100644 (file)
index 0000000..611cb0e
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_diameter_json_representation_i_want_to_add_test_id_waitfc_msg_step(resources, admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = resources("aar-diameterJsonFromOwnToAF-request.json")
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.post("/testid-waitfc-msg/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-hex_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-hex_test.py
new file mode 100644 (file)
index 0000000..fb579b3
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_hex_representation_i_want_to_add_test_id_waitfe_hex_step(admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = { "hex":"01000150c000010901000014" }
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.postDict("/testid-waitfe-hex/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-msg_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe-msg_test.py
new file mode 100644 (file)
index 0000000..18d3b4f
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_diameter_json_representation_i_want_to_add_test_id_waitfe_msg_step(resources, admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = resources("aar-diameterJsonFromOwnToAF-request.json")
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.post("/testid-waitfe-msg/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe_test.py b/example/diameter/launcher/resources/rest_api/ct/fsm-testing/testid-waitfe_test.py
new file mode 100644 (file)
index 0000000..e5628de
--- /dev/null
@@ -0,0 +1,19 @@
+import pytest
+
+
+def test_001_given_test_indentifier_and_diameter_json_condition_i_want_to_add_test_id_waitfe_step(resources, admlc, admlf):
+
+  flow = admlf.getId()
+
+  requestBody = resources("condition-request.json")
+  responseBodyRef = { "success":"true", "response":"Done" }
+
+  # Send POST
+  response = admlc.post("/testid-waitfe/{}".format(flow), requestBody)
+
+  # Verify response
+  admlc.assert_response__status_body_headers(response, 200, responseBodyRef)
+
+  # Cleanup
+  response = admlc.post("/test-clear")
+
diff --git a/example/diameter/launcher/resources/rest_api/ct/resources/condition-request.json b/example/diameter/launcher/resources/rest_api/ct/resources/condition-request.json
new file mode 100644 (file)
index 0000000..f256c7d
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "condition": {
+    "code":"272",
+    "bitR":"1",
+    "applicationId":"16777236",
+    "sessionId":"ocs3;1332774430;1;1332774430"
+  }
+}