@@ -211,7 +211,7 @@ To Integrate Vansah Binding Java functions, you need to add the below dependenci
* Sample JUnit test class demonstrating how to send test execution results to Vansah.
*
* This class uses VansahNode to:
- * - Connect to Vansah with a project key and token
+ * - Connect to Vansah with a Space Key (Jira project key) and token
* - Send test results using:
* 1. Test Folder path
* 2. Advanced Test Plan (ATP)
@@ -231,7 +231,7 @@ class Tests {
// Test Case key from Jira
private final String testCaseKey = "KAN-C17";
- // Jira Project key
+ // Space Key (the Jira project key)
private final String projectKey = "KAN";
// Advanced Test Plan key used for ATP-based test executions
@@ -245,8 +245,8 @@ class Tests {
/**
* Setup method that runs before each test.
- * It initializes VansahNode with URL, API token, project key, folder path,
- * and test plan keys for both ATP and STP.
+ * It initializes VansahNode with URL, API token, Space Key (Jira project key),
+ * folder path, and test plan keys for both ATP and STP.
*/
@SuppressWarnings("static-access")
@BeforeEach
@@ -313,7 +313,7 @@ Initiates a new test run within a specified test folder. Use this method to orga
Logs the result of a specific test step, optionally including a comment and a screenshot. This method provides detailed tracking of test execution outcomes.
- **Parameters**:
- - `result`: The outcome of the test step (e.g., PASSED, FAILED)|| (e.g 0 = N/A, 1 = FAIL, 2 = PASS, 3 = Not tested).
+ - `result`: The outcome of the test step. Accepts either a **String** (`NA`, `FAILED`, `PASSED`, `UNTESTED` — case-insensitive) or an **int** (0 = N/A, 1 = FAIL, 2 = PASS, 3 = Not tested). Note the string is `NA`, not `N/A`.
- `comment`: An optional comment describing the test step outcome.
- `testStepRow`: The index of the test step within the test case.
- `screenshotFile`: (Optional) The File Object of the screenshot taken to upload : Provide file object or Path of the screenshot.
@@ -324,7 +324,7 @@ Quickly logs the overall result of a test case associated with either a JIRA iss
- **Parameters**:
- `testcase`: The test case identifier.
- - `result`: The overall test result (e.g., PASS, FAIL).
+ - `result`: The overall test result as an **int** — 0 = N/A, 1 = FAIL, 2 = PASS, 3 = Not tested.
### `addTestRunFromAdvancedTestPlan(String testPlanAssetType, String testCaseKey)`
@@ -335,7 +335,9 @@ Adds a new test run in Vansah under an Advanced Test Plan (ATP). The method link
- `folder` – If the ATP is structured by test folder.
- `issue` – If the ATP is linked to a specific Jira issue.
- `testCaseKey`: The key of the test case to be executed (e.g., "KAN-C17").
-
+
+The run targets **iteration 1** by default. To target a different iteration, call `setTestPlanIteration(int)` (range 1–5) before this method.
+
### `addTestRunFromStandardTestPlan(String testCaseKey)`
Adds a new test run in Vansah under a Standard Test Plan (STP). This method links the specified test case to the configured Standard Test Plan, enabling execution tracking and reporting without the need for a specific folder or issue reference.
@@ -343,6 +345,8 @@ Adds a new test run in Vansah under a Standard Test Plan (STP). This method link
- **Parameters**:
- `testCaseKey`: The key of the test case to be executed under the Standard Test Plan (e.g., "KAN-C17").
+The run targets **iteration 1** by default. To target a different iteration, call `setTestPlanIteration(int)` (range 1–5) before this method.
+
### `removeTestRun()` and `removeTestLog()`
Deletes a previously created test run or log. These methods are useful for cleaning up data in Vansah that is no longer relevant or was created in error.
@@ -358,12 +362,21 @@ Updates an existing test log with new information, such as a revised result or a
The `VansahNode` class provides a set of setter methods to configure your test management context before performing operations such as creating test runs, adding test logs, and more. Here's a detailed overview of each setter method:
-### `setTESTFOLDER_PATH(String TESTFOLDER_PATH)`
+### `setProjectKey(String projectKey)`
+
+Sets the **Space Key** (the Jira project key) that scopes your test runs and logs. This is **required for the Vansah API v2** — it is sent as the top-level `project` object on every request.
+
+- **Parameters**:
+ - `projectKey`: The Space Key / Jira project key (e.g., `"KAN"`).
+
+> **Note:** A null or empty value prints a warning (`⚠️ Warning: Provided Space Key is null or empty. Value not updated.`) and is ignored.
+
+### `setFOLDERPATH(String FOLDERPATH)`
Configures the **Test Folder Path** for the VansahNode instance. This path is essential for associating your test runs and logs with the correct test folder structure in Vansah.
- **Parameters**:
- - `TESTFOLDER_PATH`: The folder path for the test folder in Vansah. The path must contain at least one `/` and must not start with `/`.
+ - `FOLDERPATH`: The folder path for the test folder in Vansah. The path must contain at least one `/` and must not start with `/`.
> **Note:** Invalid paths (e.g., those starting with `/` or lacking `/`) will print a warning and be ignored.
@@ -408,8 +421,26 @@ Sets the key for the Standard Test Plan (STP). This key is used to associate tes
- **Parameters**:
- `testPlanKey`: The unique key of the Standard Test Plan (e.g., "KAN-P17"), used to organize and manage test executions in Vansah.
-
-
+
+### `setTestPlanIteration(int iteration)`
+
+Sets the **iteration** to target when creating a run from a Standard or Advanced Test Plan. This is **optional** — if you never call it, runs default to **iteration 1**. Only call this when you need to record results against a specific iteration of the test plan.
+
+- **Parameters**:
+ - `iteration`: The test plan iteration to target. Valid range is **1–5**. Values outside this range are ignored (a warning is printed and the default of 1 is kept).
+
+> **Note:** The iteration applies only to `addTestRunFromStandardTestPlan(...)` and `addTestRunFromAdvancedTestPlan(...)`. It has no effect on Jira-issue or test-folder runs.
+
+### `setDebug(boolean debug)`
+
+Enables or disables **request-payload logging**. When enabled, the exact JSON body sent to Vansah is printed before each API call — useful for diagnosing issues such as a malformed folder path or a missing Space Key. Screenshot attachments are logged after the payload so base64 data does not flood the output.
+
+Debug logging is also enabled automatically when the `VANSAH_DEBUG` environment variable is set to `true` or `1`.
+
+- **Parameters**:
+ - `debug`: `true` to log outgoing request payloads, `false` to disable.
+
+
### Usage
To use these setter methods in your application, create an instance of `VansahNode` and call the relevant setter methods with the appropriate values before proceeding with any test management operations. For example:
@@ -417,13 +448,14 @@ To use these setter methods in your application, create an instance of `VansahNo
```java
VansahNode vansahNode = new VansahNode();
vansahNode.setVansahToken("Add your Token here");
-vansahNode.setTESTFOLDER_PATH("feature-tests/login");
+vansahNode.setProjectKey("KAN"); // Space Key (the Jira project key) — required for API v2
+vansahNode.setFOLDERPATH("feature-tests/login");
vansahNode.setJIRA_ISSUE_KEY("your-jira-issue-key");
vansahNode.setSPRINT_NAME("your-sprint-name");
vansahNode.setRELEASE_NAME("your-release-name");
vansahNode.setENVIRONMENT_NAME("your-environment-name");
-vansahNode.setAdvancedTestPlanKey("KAN-P18");
-vansahNode.setStandardTestPlanKey("KAN-P17");
+vansahNode.setAdvancedTestPlanKey("KAN-P17");
+vansahNode.setStandardTestPlanKey("KAN-P18");
```
## Developed By
diff --git a/license.md b/license.md
index b79c981..30cfc00 100644
--- a/license.md
+++ b/license.md
@@ -1,5 +1,5 @@
-

+
diff --git a/src/main/java/com/vansah/VansahNode.java b/src/main/java/com/vansah/VansahNode.java
index 80a021f..a6a4f48 100644
--- a/src/main/java/com/vansah/VansahNode.java
+++ b/src/main/java/com/vansah/VansahNode.java
@@ -9,6 +9,7 @@
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
+import org.json.JSONArray;
import org.json.JSONObject;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
@@ -57,6 +58,14 @@ public class VansahNode {
*/
private static String STANDARD_TEST_PLAN_KEY = null;
+ /**
+ * The iteration number for a Standard or Advanced Test Plan run.
+ * Defaults to {@code 1} (Vansah's first iteration). Only sent to the API when the
+ * caller explicitly overrides it via {@link #setTestPlanIteration(int)}; otherwise
+ * the default iteration of 1 is used. Valid range is 1–5.
+ */
+ private static Integer TEST_PLAN_ITERATION = null;
+
/**
* Returns the endpoint URL for adding a test run.
* This dynamically constructs the URL using the current Vansah base URL and API version.
@@ -110,16 +119,6 @@ private static String getRemoveTestRunUrl(String identifier) {
return VANSAH_URL + "/api/" + API_VERSION + "/run/" + identifier;
}
- /**
- * Returns the endpoint URL for retrieving test scripts associated with a test case.
- * This dynamically builds the URL to query test scripts by case key.
- *
- * @return The complete URL for the "get test script list" API call.
- */
- private static String getTestScriptUrl() {
- return VANSAH_URL + "/api/" + API_VERSION + "/testCase/list/testScripts";
- }
-
/**
* Sets a custom base URL for the Vansah API.
*
@@ -148,7 +147,7 @@ public static void setProjectKey(String projectKey) {
if (projectKey != null && !projectKey.trim().isEmpty()) {
PROJECT_KEY = projectKey.trim();
} else {
- System.out.println("⚠️ Warning: Provided project key is null or empty. Value not updated.");
+ System.out.println("⚠️ Warning: Provided Space Key is null or empty. Value not updated.");
}
}
/**
@@ -169,6 +168,20 @@ public void setAdvancedTestPlanKey(String ADVANCED_TEST_PLAN_KEY) {
public void setStandardTestPlanKey(String STANDARD_TEST_PLAN_KEY) {
this.STANDARD_TEST_PLAN_KEY = STANDARD_TEST_PLAN_KEY;
}
+ /**
+ * Sets the iteration number for a Standard or Advanced Test Plan run.
+ * If this is never called, the run defaults to iteration 1. Only call this when you
+ * need to target a specific iteration of the test plan.
+ *
+ * @param iteration The test plan iteration to target. Valid range is 1–5.
+ */
+ public void setTestPlanIteration(int iteration) {
+ if (iteration >= 1 && iteration <= 5) {
+ this.TEST_PLAN_ITERATION = iteration;
+ } else {
+ System.out.println("⚠️ Warning: Test plan iteration must be between 1 and 5. Value not updated (default is 1).");
+ }
+ }
/**
* The authentication token required for making requests to the Vansah API. This token
* authenticates the client to the Vansah system, ensuring secure access to API functions.
@@ -188,6 +201,38 @@ public void setStandardTestPlanKey(String STANDARD_TEST_PLAN_KEY) {
public static void setVansahToken(String vansahToken) {
VANSAH_TOKEN = vansahToken;
}
+
+ /**
+ * Controls whether outgoing request payloads are logged before being sent to Vansah.
+ * Useful for diagnosing integration issues (e.g. malformed folder paths, missing
+ * project keys) without inspecting network traffic. Defaults to enabled when the
+ * VANSAH_DEBUG environment variable is set to "true" or "1".
+ */
+ private static boolean DEBUG = "true".equalsIgnoreCase(System.getenv("VANSAH_DEBUG"))
+ || "1".equals(System.getenv("VANSAH_DEBUG"));
+
+ /**
+ * Enables or disables debug payload logging for outgoing Vansah API requests.
+ *
+ * @param debug true to log request payloads before they are sent, false to disable.
+ */
+ public static void setDebug(boolean debug) {
+ DEBUG = debug;
+ }
+
+ /**
+ * Logs the outgoing request payload for a given endpoint when debug mode is enabled.
+ * Called before any base64 attachment is added to the payload so the log isn't
+ * flooded with encoded file data.
+ *
+ * @param endpoint The Vansah API endpoint being called.
+ * @param payload The JSON request body about to be sent.
+ */
+ private static void emitPayload(String endpoint, JSONObject payload) {
+ if (DEBUG) {
+ System.out.println("🐛 [DEBUG] Request to " + endpoint + ": " + payload.toString());
+ }
+ }
/**
* The hostname or IP address of the proxy server used when the Vansah API binding operates behind a proxy.
* Specify the address of the proxy to enable communication with the Vansah API through it. Leave blank if
@@ -269,6 +314,20 @@ public static void setVansahToken(String vansahToken) {
/** A mapping from result names to their corresponding numeric codes. */
private HashMap resultAsName = new HashMap<>();
+ /**
+ * The result id sent by default when a test run is created. Creating a run as UNTESTED (id 3)
+ * makes Vansah pre-create one UNTESTED test log per step of the test case; those per-step logs
+ * are then updated (rather than newly created) by {@link #addTestLog}.
+ */
+ private static final int UNTESTED_RESULT_ID = 3;
+
+ /**
+ * Maps a test step number to the identifier of the (UNTESTED) test log Vansah pre-creates for
+ * that step when a run is created. Populated from the "logs" array of the add-test-run response
+ * and used by {@link #addTestLog} to update the correct step log.
+ */
+ private Map stepLogIdentifiers = new HashMap<>();
+
/** The JSON object representing the body of the API request. */
private JSONObject requestBody = null;
@@ -594,72 +653,6 @@ public void updateTestLog(String result, String comment, File image) throws Exce
connectToVansahRest("updateTestLog");
}
- /**
- * Retrieves the count of test steps for a given test case from Vansah. This method sends a GET request
- * to the Vansah API to fetch the test script associated with the specified case key and calculates the
- * number of steps in the test script. It also handles proxy settings if specified.
- *
- * @param case_key The unique identifier for the test case whose test script step count is to be retrieved.
- * @return The number of test steps contained within the test script for the specified case key. Returns 0
- * if there's an error in fetching the test script, the response from Vansah is unexpected, or the
- * specified test case does not contain any steps.
- * @throws Exception if there's an issue with network connectivity, parsing the response, or if the Vansah
- * API endpoint is not reachable. In such cases, the exception is caught and printed to the
- * console, and the method returns 0.
- *
- * Note: This method assumes that the Vansah API token and possibly proxy settings have been correctly set
- * prior to its invocation. It utilizes the `headers` and `clientBuilder` fields of the enclosing class
- * to configure the HTTP request.
- */
- public int testStepCount(String case_key) {
-
-
- try {
- headers.put("Authorization",VANSAH_TOKEN);
- headers.put("Content-Type","application/json");
-
- clientBuilder = HttpClientBuilder.create();
- // Detecting if the system using any proxy setting.
-
-
- if (hostAddr.equals("") && portNo.equals("")) {
- Unirest.setHttpClient(clientBuilder.build());
- } else {
- System.out.println("Proxy Server");
- credsProvider = new BasicCredentialsProvider();
- clientBuilder.useSystemProperties();
- clientBuilder.setProxy(new HttpHost(hostAddr, Integer.parseInt(portNo)));
- clientBuilder.setDefaultCredentialsProvider(credsProvider);
- clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
- Unirest.setHttpClient(clientBuilder.build());
- }
- HttpResponse get;
- get = Unirest.get(getTestScriptUrl()).headers(headers).queryString("caseKey", case_key).asJson();
- if (get.getBody().toString().equals("[]")) {
- System.out.println("Unexpected Response From Server: " + get.getBody().toString());
- } else {
- JSONObject jsonobjInit = new JSONObject(get.getBody().toString());
- boolean success = jsonobjInit.getBoolean("success");
- String vansah_message = jsonobjInit.getString("message");
-
- if (success) {
-
- int testRows = jsonobjInit.getJSONObject("data").getJSONArray("steps").length();
- System.out.println("NUMBER OF STEPS: " + testRows);
- return testRows;
-
- } else {
- System.out.println("Error - Response From Vansah: " + vansah_message);
- return 0;
- }
- }
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- return 0;
-
- }
-
/**
* Central method for interacting with the Vansah REST API. Depending on the specified type, it constructs
* and executes different API requests to add, update, or remove test runs and logs. This method also handles
@@ -716,8 +709,11 @@ private void connectToVansahRest(String type) {
requestBody.accumulate("properties", properties());
}
+ // Create the run as UNTESTED so Vansah pre-creates a log for each step.
+ requestBody.accumulate("result", resultObj(UNTESTED_RESULT_ID));
requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
}
@@ -728,9 +724,12 @@ private void connectToVansahRest(String type) {
if(properties().length()!=0) {
requestBody.accumulate("properties", properties());
}
-
+
+ // Create the run as UNTESTED so Vansah pre-creates a log for each step.
+ requestBody.accumulate("result", resultObj(UNTESTED_RESULT_ID));
requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
}
@@ -742,32 +741,62 @@ private void connectToVansahRest(String type) {
if(properties().length()!=0) {
requestBody.accumulate("properties", properties());
}
-
+
+ // Create the run as UNTESTED so Vansah pre-creates a log for each step.
+ requestBody.accumulate("result", resultObj(UNTESTED_RESULT_ID));
requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
- }
+ }
if(type == "addTestRunFromStandardTestPlan") {
requestBody = new JSONObject();
requestBody.accumulate("case", testCase());
- requestBody.accumulate("asset", standardTestPlanAsset());
+ requestBody.accumulate("asset", standardTestPlanAsset());
if(properties().length()!=0) {
requestBody.accumulate("properties", properties());
}
-
- requestBody.accumulate("project", jiraProjectAsset());
+
+ // Create the run as UNTESTED so Vansah pre-creates a log for each step.
+ requestBody.accumulate("result", resultObj(UNTESTED_RESULT_ID));
+ requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
- }
+ }
if(type == "addTestLog") {
- requestBody = addTestLogProp();
- if(SEND_SCREENSHOT) {
+ String stepLogIdentifier = stepLogIdentifiers.get(STEP_ORDER);
+ if (stepLogIdentifier != null) {
+ // A log for this step was pre-created (UNTESTED) when the run was created.
+ // Update it in place rather than posting a new one (which Vansah rejects
+ // with "A Test Log already exists for provided Step.").
+ requestBody = new JSONObject();
+ requestBody.accumulate("result", resultObj(RESULT_KEY));
+ requestBody.accumulate("actualResult", COMMENT);
+ requestBody.accumulate("project", jiraProjectAsset());
+
+ emitPayload(getUpdateTestLogUrl(stepLogIdentifier), requestBody);
+
+ if(SEND_SCREENSHOT) {
+ requestBody.append("attachments", addAttachment(FILE));
+ }
- requestBody.append("attachments", addAttachment(FILE));
+ jsonRequestBody = Unirest.put(getUpdateTestLogUrl(stepLogIdentifier)).headers(headers).body(requestBody).asJson();
+ TEST_LOG_IDENTIFIER = stepLogIdentifier;
+ } else {
+ // Fallback: no pre-created log for this step (e.g. the case has no steps) -- create one.
+ requestBody = addTestLogProp();
+ requestBody.accumulate("project", jiraProjectAsset());
- }
- requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestLogUrl(), requestBody);
+
+ if(SEND_SCREENSHOT) {
- jsonRequestBody = Unirest.post( getAddTestLogUrl()).headers(headers).body(requestBody).asJson();
+ requestBody.append("attachments", addAttachment(FILE));
+
+ }
+
+ jsonRequestBody = Unirest.post( getAddTestLogUrl()).headers(headers).body(requestBody).asJson();
+ }
}
@@ -780,9 +809,10 @@ private void connectToVansahRest(String type) {
requestBody.accumulate("properties", properties());
}
requestBody.accumulate("result", resultObj(RESULT_KEY));
-
+
requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
}
if(type == "addQuickTestFromTestFolders") {
@@ -793,9 +823,10 @@ private void connectToVansahRest(String type) {
requestBody.accumulate("properties", properties());
}
requestBody.accumulate("result", resultObj(RESULT_KEY));
-
+
requestBody.accumulate("project", jiraProjectAsset());
+ emitPayload(getAddTestRunUrl(), requestBody);
jsonRequestBody = Unirest.post(getAddTestRunUrl()).headers(headers).body(requestBody).asJson();
}
@@ -806,7 +837,7 @@ private void connectToVansahRest(String type) {
if(type == "removeTestLog") {
- jsonRequestBody = Unirest.delete(getRemoveTestRunUrl(TEST_LOG_IDENTIFIER)).headers(headers).asJson();
+ jsonRequestBody = Unirest.delete(getRemoveTestLogUrl(TEST_LOG_IDENTIFIER)).headers(headers).asJson();
}
@@ -814,10 +845,13 @@ private void connectToVansahRest(String type) {
requestBody = new JSONObject();
requestBody.accumulate("result", resultObj(RESULT_KEY));
requestBody.accumulate("actualResult", COMMENT);
+ requestBody.accumulate("project", jiraProjectAsset());
+
+ emitPayload(getUpdateTestLogUrl(TEST_LOG_IDENTIFIER), requestBody);
+
if(SEND_SCREENSHOT) {
requestBody.append("attachments", addAttachment(FILE));
}
- requestBody.accumulate("project", jiraProjectAsset());
jsonRequestBody = Unirest.put(getUpdateTestLogUrl(TEST_LOG_IDENTIFIER)).headers(headers).body(requestBody).asJson();
}
@@ -837,25 +871,49 @@ private void connectToVansahRest(String type) {
if (success){
if(type == "addTestRunFromJIRAIssue") {
- TEST_RUN_IDENTIFIER = fullBody.getJSONObject("data").getJSONObject("run").get("identifier").toString();
+ JSONObject runObject = fullBody.getJSONObject("data").getJSONObject("run");
+ TEST_RUN_IDENTIFIER = runObject.get("identifier").toString();
System.out.println("Test Run Identifier: " + TEST_RUN_IDENTIFIER);
+ storeStepLogs(runObject);
}
if(type == "addTestRunFromTestFolder") {
- TEST_RUN_IDENTIFIER = fullBody.getJSONObject("data").getJSONObject("run").get("identifier").toString();
+ JSONObject runObject = fullBody.getJSONObject("data").getJSONObject("run");
+ TEST_RUN_IDENTIFIER = runObject.get("identifier").toString();
System.out.println("Test Run Identifier: " + TEST_RUN_IDENTIFIER);
+ storeStepLogs(runObject);
}
if(type == "addTestRunFromAdvancedTestPlan") {
- TEST_RUN_IDENTIFIER = fullBody.getJSONObject("data").getJSONObject("run").get("identifier").toString();
+ JSONObject runObject = fullBody.getJSONObject("data").getJSONObject("run");
+ TEST_RUN_IDENTIFIER = runObject.get("identifier").toString();
System.out.println("Test Run Identifier: " + TEST_RUN_IDENTIFIER);
+ storeStepLogs(runObject);
}
if(type == "addTestRunFromStandardTestPlan") {
- TEST_RUN_IDENTIFIER = fullBody.getJSONObject("data").getJSONObject("run").get("identifier").toString();
+ JSONObject runObject = fullBody.getJSONObject("data").getJSONObject("run");
+ TEST_RUN_IDENTIFIER = runObject.get("identifier").toString();
System.out.println("Test Run Identifier: " + TEST_RUN_IDENTIFIER);
+ storeStepLogs(runObject);
}
if(type == "addTestLog") {
TEST_LOG_IDENTIFIER = fullBody.getJSONObject("data").getJSONObject("log").get("identifier").toString();
System.out.println("Test Log Identifier: " + TEST_LOG_IDENTIFIER);
+ // Keep the step -> log map current for both the update and fallback-create paths.
+ stepLogIdentifiers.put(STEP_ORDER, TEST_LOG_IDENTIFIER);
+ }
+
+ if(type == "removeTestLog") {
+ // The deleted log leaves its step without a log. Recreate an empty UNTESTED
+ // placeholder for that step so a later addTestLog updates the correct log and
+ // the stored step -> log map never holds a stale (deleted) identifier.
+ Integer removedStep = stepForLog(TEST_LOG_IDENTIFIER);
+ if (removedStep != null) {
+ stepLogIdentifiers.remove(removedStep);
+ }
+ TEST_LOG_IDENTIFIER = null;
+ if (removedStep != null) {
+ recreateUntestedStepLog(removedStep);
+ }
}
}else{
@@ -1068,7 +1126,7 @@ private JSONObject jiraProjectAsset() {
} else {
// Print warning if key is invalid
- System.out.println("⚠️ Warning: Please provide a valid JIRA Project Key.");
+ System.out.println("⚠️ Warning: Please provide a valid Space Key.");
}
return asset;
@@ -1118,7 +1176,7 @@ private JSONObject advancedTestPlanAsset() {
if (ADVANCED_TEST_PLAN_KEY != null && !ADVANCED_TEST_PLAN_KEY.trim().isEmpty()) {
asset.accumulate("type", "plannedRun");
asset.accumulate("key", ADVANCED_TEST_PLAN_KEY);
- asset.accumulate("iteration", 1); // Optionally, use a variable like ATP_ITERATION
+ asset.accumulate("iteration", TEST_PLAN_ITERATION != null ? TEST_PLAN_ITERATION : 1);
} else {
System.out.println("⚠️ Warning: Please provide a valid Advanced Test Plan Key.");
}
@@ -1141,7 +1199,7 @@ private JSONObject standardTestPlanAsset() {
if (STANDARD_TEST_PLAN_KEY != null && !STANDARD_TEST_PLAN_KEY.trim().isEmpty()) {
asset.accumulate("type", "plannedRun");
asset.accumulate("key", STANDARD_TEST_PLAN_KEY);
- asset.accumulate("iteration", 1);
+ asset.accumulate("iteration", TEST_PLAN_ITERATION != null ? TEST_PLAN_ITERATION : 1);
} else {
System.out.println("⚠️ Warning: Please provide a valid Standard Test Plan Key.");
}
@@ -1229,6 +1287,85 @@ public boolean isValidFolderPath(String folderPath) {
* step number, result ID, and actual result comment. This object can be directly used as the body
* of an API request to add or update test logs.
*/
+ /**
+ * Records the per-step test logs that Vansah pre-creates when a run is created as UNTESTED.
+ * Reads the "logs" array from the add-test-run response and maps each step number to its log
+ * identifier so {@link #addTestLog} can update the correct step log instead of creating a new one.
+ * Any previously stored mapping is cleared first, so each new run starts fresh.
+ *
+ * @param runObject The "run" object from the add-test-run response body (data.run).
+ */
+ private void storeStepLogs(JSONObject runObject) {
+ stepLogIdentifiers.clear();
+ if (runObject == null || !runObject.has("logs")) {
+ return;
+ }
+ JSONArray logs = runObject.getJSONArray("logs");
+ for (int i = 0; i < logs.length(); i++) {
+ JSONObject log = logs.getJSONObject(i);
+ if (log.has("identifier") && log.has("step") && log.getJSONObject("step").has("number")) {
+ stepLogIdentifiers.put(log.getJSONObject("step").getInt("number"),
+ log.get("identifier").toString());
+ }
+ }
+ }
+
+ /**
+ * Reverse lookup: returns the step number currently mapped to the given log identifier, or
+ * null if no step maps to it.
+ *
+ * @param logIdentifier The test log identifier to look up.
+ * @return The step number whose log is {@code logIdentifier}, or null if not found.
+ */
+ private Integer stepForLog(String logIdentifier) {
+ if (logIdentifier == null) {
+ return null;
+ }
+ for (Map.Entry entry : stepLogIdentifiers.entrySet()) {
+ if (logIdentifier.equals(entry.getValue())) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Recreates an empty UNTESTED test log for the given step on the current run and stores its
+ * identifier in the step -> log map. Called after {@link #removeTestLog()} so the step keeps a
+ * placeholder log that a later {@link #addTestLog} can update, and the map never holds a stale id.
+ *
+ * @param stepNumber The step number whose log placeholder should be recreated.
+ */
+ private void recreateUntestedStepLog(int stepNumber) {
+ try {
+ JSONObject run = new JSONObject();
+ run.accumulate("identifier", TEST_RUN_IDENTIFIER);
+
+ JSONObject step = new JSONObject();
+ step.accumulate("number", stepNumber);
+
+ JSONObject body = new JSONObject();
+ body.accumulate("run", run);
+ body.accumulate("step", step);
+ body.accumulate("result", resultObj(UNTESTED_RESULT_ID));
+ body.accumulate("project", jiraProjectAsset());
+
+ emitPayload(getAddTestLogUrl(), body);
+ HttpResponse response = Unirest.post(getAddTestLogUrl()).headers(headers).body(body).asJson();
+ JSONObject responseObject = new JSONObject(response.getBody().toString());
+
+ if (responseObject.getBoolean("success")) {
+ String newLogIdentifier = responseObject.getJSONObject("data").getJSONObject("log").get("identifier").toString();
+ stepLogIdentifiers.put(stepNumber, newLogIdentifier);
+ System.out.println("Recreated UNTESTED log for step " + stepNumber + ": " + newLogIdentifier);
+ } else {
+ System.out.println("Could not recreate a log for step " + stepNumber + ": " + responseObject.getString("message"));
+ }
+ } catch (Exception e) {
+ System.out.println("Error recreating a log for step " + stepNumber + ": " + e.toString());
+ }
+ }
+
private JSONObject addTestLogProp() {
JSONObject testRun = new JSONObject();
diff --git a/src/test/java/com/vansah/Tests.java b/src/test/java/com/vansah/Tests.java
deleted file mode 100644
index b2abbb7..0000000
--- a/src/test/java/com/vansah/Tests.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package com.vansah;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-class Tests {
-
- private final VansahNode sendResults = new VansahNode();
-
- private final String vansahURL = "https://prodau.vansah.com";
-
- private final String testfolderPath = "vansah test automation/regression 2025/";
-
- private final String testCaseKey = "KAN-C17";
-
- private final String projectKey = "KAN";
-
- private final String testPlanKeyforATP = "KAN-P17";
-
- private final String testPlanAssetType = "folder"; //or issue only for ATP - Advanced Test Plan
-
- private final String testPlanKeyforSTP = "KAN-P18"; //required for Standard Test Plan
-
-
- @SuppressWarnings("static-access")
- @BeforeEach
- void setup() {
-
- sendResults.setVansahURL(vansahURL);
-
- sendResults.setVansahToken(System.getenv("CONNECT_DEMO_TOKEN"));
-
- sendResults.setProjectKey(projectKey);
-
- sendResults.setFOLDERPATH(testfolderPath);
-
- sendResults.setAdvancedTestPlanKey(testPlanKeyforATP);
-
- sendResults.setStandardTestPlanKey(testPlanKeyforSTP);
-
-
- }
-
- @Test
- void sendingResultstoVansah_usingTestFolderPath() throws Exception {
-
- sendResults.addTestRunFromTestFolder(testCaseKey);
-
- sendResults.addTestLog("passed", "Actual result for the Test Step", 1);
-
-
- }
-
- @Test
- void sendingResultstoVansahforATP() throws Exception {
-
- sendResults.addTestRunFromAdvancedTestPlan(testPlanAssetType,testCaseKey);
-
- sendResults.addTestLog("passed", "Actual result for the Test Step", 1);
-
-
- }
-
- @Test
- void sendingResultstoVansahforSTP() throws Exception {
-
- sendResults.addTestRunFromStandardTestPlan(testCaseKey);
-
- sendResults.addTestLog("passed", "Actual result for the Test Step", 1);
-
-
- }
-
-}
diff --git a/src/test/java/com/vansah/VansahNodeFullTests.java b/src/test/java/com/vansah/VansahNodeFullTests.java
new file mode 100644
index 0000000..97a6906
--- /dev/null
+++ b/src/test/java/com/vansah/VansahNodeFullTests.java
@@ -0,0 +1,381 @@
+package com.vansah;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * ============================================================================
+ * FOR TESTING THE VANSAH JAVA BINDING (VansahNode) ONLY.
+ * ============================================================================
+ *
+ * This is a single, self-contained live test suite that exercises every public
+ * VansahNode operation (create runs, add/update/remove step logs, attachments,
+ * quick tests, Standard/Advanced Test Plans and the config/validation helpers)
+ * against a REAL Vansah instance. It is shipped as a reference/example so you
+ * can confirm the binding works end-to-end in your own Vansah workspace.
+ *
+ * How to run:
+ * 1. Copy .env.example to .env at the project root and fill in real values.
+ * (.env is gitignored - never commit real tokens.) You may instead export
+ * the same keys as environment variables (handy for CI).
+ * 2. mvn test
+ *
+ * Required keys (see .env.example): VANSAH_TOKEN, VANSAH_PROJECT_KEY,
+ * VANSAH_JIRA_ISSUE_KEY, VANSAH_TEST_CASE_KEY, VANSAH_FOLDER_PATH,
+ * VANSAH_ATP_KEY, VANSAH_STP_KEY. Optional: VANSAH_URL, VANSAH_ATP_ASSET_TYPE,
+ * VANSAH_SPRINT_NAME, VANSAH_RELEASE_NAME, VANSAH_ENVIRONMENT_NAME,
+ * VANSAH_SCREENSHOT_PATH, VANSAH_DEBUG.
+ *
+ * Any test whose required configuration is missing is SKIPPED (not failed), so
+ * this suite is safe to run before .env is fully filled in - with no config it
+ * simply skips every network test.
+ *
+ * Note on assertions: VansahNode catches all API/network errors internally and
+ * only prints outcomes to stdout - it does not expose success/failure or the
+ * created run/log ids. These tests therefore assert that each call completes
+ * without throwing; they cannot tell a real Vansah success from a silently
+ * logged API error. Enable VANSAH_DEBUG=true to inspect the exact JSON sent.
+ * Every network test cleans up after itself (removeTestLog / removeTestRun).
+ */
+class VansahNodeFullTests {
+
+ private static String vansahUrl;
+ private static String token;
+ private static String projectKey;
+ private static String folderPath;
+ private static String jiraIssueKey;
+ private static String testCaseKey;
+ private static String atpKey;
+ private static String atpAssetType;
+ private static String stpKey;
+ private static String sprintName;
+ private static String releaseName;
+ private static String environmentName;
+ private static File screenshotFile;
+
+ private VansahNode node;
+
+ @BeforeAll
+ static void loadConfig() throws IOException {
+ vansahUrl = Env.get("VANSAH_URL", "https://prod.vansah.com");
+ token = Env.get("VANSAH_TOKEN");
+ projectKey = Env.get("VANSAH_PROJECT_KEY");
+ folderPath = Env.get("VANSAH_FOLDER_PATH");
+ jiraIssueKey = Env.get("VANSAH_JIRA_ISSUE_KEY");
+ testCaseKey = Env.get("VANSAH_TEST_CASE_KEY");
+ atpKey = Env.get("VANSAH_ATP_KEY");
+ atpAssetType = Env.get("VANSAH_ATP_ASSET_TYPE", "folder");
+ stpKey = Env.get("VANSAH_STP_KEY");
+ sprintName = Env.get("VANSAH_SPRINT_NAME");
+ releaseName = Env.get("VANSAH_RELEASE_NAME");
+ environmentName = Env.get("VANSAH_ENVIRONMENT_NAME");
+ screenshotFile = resolveScreenshotFile();
+ }
+
+ private static File resolveScreenshotFile() throws IOException {
+ String configuredPath = Env.get("VANSAH_SCREENSHOT_PATH");
+ if (configuredPath != null) {
+ File configured = new File(configuredPath);
+ if (configured.isFile()) {
+ return configured;
+ }
+ System.out.println("⚠️ VANSAH_SCREENSHOT_PATH does not point to a file, using a generated placeholder instead.");
+ }
+ // 1x1 transparent PNG so attachment tests don't require an externally supplied image.
+ byte[] onePixelPng = Base64.getDecoder().decode(
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=");
+ Path tempFile = Files.createTempFile("vansah-test-screenshot", ".png");
+ Files.write(tempFile, onePixelPng);
+ tempFile.toFile().deleteOnExit();
+ return tempFile.toFile();
+ }
+
+ @BeforeEach
+ void setUp() {
+ assumeTrue(Env.isSet("VANSAH_TOKEN"),
+ "Skipping: VANSAH_TOKEN not set. Copy .env.example to .env and fill in real Vansah credentials.");
+
+ node = new VansahNode(folderPath, jiraIssueKey);
+ VansahNode.setVansahURL(vansahUrl);
+ VansahNode.setVansahToken(token);
+ VansahNode.setDebug(Env.getBoolean("VANSAH_DEBUG", false));
+ // Reset the shared Test Plan iteration to its default (1) so a value set by an
+ // earlier test cannot leak into a later one (the field is process-global).
+ node.setTestPlanIteration(1);
+ if (projectKey != null) {
+ VansahNode.setProjectKey(projectKey);
+ }
+ if (atpKey != null) {
+ node.setAdvancedTestPlanKey(atpKey);
+ }
+ if (stpKey != null) {
+ node.setStandardTestPlanKey(stpKey);
+ }
+ node.setFOLDERPATH(folderPath);
+ node.setJIRA_ISSUE_KEY(jiraIssueKey);
+ node.setSPRINT_NAME(sprintName);
+ node.setRELEASE_NAME(releaseName);
+ node.setENVIRONMENT_NAME(environmentName);
+ }
+
+ // ---- Config / validation methods (no network call) ----
+
+ @Test
+ void isValidFolderPath_acceptsWellFormedPath() {
+ assertTrue(node.isValidFolderPath("regression/2025/smoke"));
+ }
+
+ @Test
+ void isValidFolderPath_rejectsLeadingSlash() {
+ assertFalse(node.isValidFolderPath("/regression/2025"));
+ }
+
+ @Test
+ void isValidFolderPath_rejectsMissingSeparator() {
+ assertFalse(node.isValidFolderPath("regression"));
+ }
+
+ @Test
+ void isValidFolderPath_rejectsNullOrEmpty() {
+ assertFalse(node.isValidFolderPath(null));
+ assertFalse(node.isValidFolderPath(""));
+ }
+
+ @Test
+ void noArgConstructor_initialisesWithoutThrowing() {
+ VansahNode plain = assertDoesNotThrow(() -> new VansahNode());
+ // The no-arg constructor leaves folder/issue unset; validation helpers must still work.
+ assertFalse(plain.isValidFolderPath(null));
+ assertTrue(plain.isValidFolderPath("regression/2025/smoke"));
+ }
+
+ @Test
+ void setDebug_togglesWithoutThrowing() {
+ assertDoesNotThrow(() -> VansahNode.setDebug(true));
+ assertDoesNotThrow(() -> VansahNode.setDebug(false));
+ }
+
+ @Test
+ void setVansahURL_resetsToDefaultOnBlankInput() {
+ assertDoesNotThrow(() -> VansahNode.setVansahURL(""));
+ VansahNode.setVansahURL(vansahUrl); // restore for any tests sharing the JVM
+ }
+
+ @Test
+ void setVansahToken_acceptsArbitraryValue() {
+ assertDoesNotThrow(() -> VansahNode.setVansahToken("arbitrary-non-empty-token"));
+ // real token is restored by setUp() before every subsequent test
+ }
+
+ @Test
+ void setProjectKey_warnsButDoesNotThrowOnBlankInput() {
+ assertDoesNotThrow(() -> VansahNode.setProjectKey(""));
+ if (projectKey != null) {
+ VansahNode.setProjectKey(projectKey); // restore
+ }
+ }
+
+ @Test
+ void setTestPlanIteration_ignoresOutOfRangeButAcceptsValid() {
+ // Out-of-range values are ignored with a warning (default of 1 kept); valid ones are accepted.
+ assertDoesNotThrow(() -> node.setTestPlanIteration(0));
+ assertDoesNotThrow(() -> node.setTestPlanIteration(6));
+ assertDoesNotThrow(() -> node.setTestPlanIteration(2));
+ }
+
+ // ---- JIRA issue lifecycle: run -> log (all 4 addTestLog overloads) -> update (all 4 overloads) -> remove ----
+
+ @Test
+ void jiraIssueLifecycle_addLogUpdateRemove() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_JIRA_ISSUE_KEY"), "Skipping: VANSAH_JIRA_ISSUE_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addTestRunFromJIRAIssue(testCaseKey));
+
+ // Runs are created UNTESTED with one pre-created log per step; step 1 always exists.
+ final int step = 1;
+
+ assertDoesNotThrow(() -> node.addTestLog(2, "Automated: addTestLog(int, comment, step)", step));
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: addTestLog(String, comment, step)", step));
+ assertDoesNotThrow(() -> node.addTestLog(2, "Automated: addTestLog(int, comment, step, image)", step, screenshotFile));
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: addTestLog(String, comment, step, image)", step, screenshotFile));
+
+ assertDoesNotThrow(() -> node.updateTestLog(1, "Automated: updateTestLog(int, comment)"));
+ assertDoesNotThrow(() -> node.updateTestLog("failed", "Automated: updateTestLog(String, comment)"));
+ assertDoesNotThrow(() -> node.updateTestLog(1, "Automated: updateTestLog(int, comment, image)", screenshotFile));
+ assertDoesNotThrow(() -> node.updateTestLog("failed", "Automated: updateTestLog(String, comment, image)", screenshotFile));
+
+ assertDoesNotThrow(() -> node.removeTestLog());
+ assertDoesNotThrow(() -> node.removeTestRun());
+ }
+
+ // ---- Test folder lifecycle ----
+
+ @Test
+ void testFolderLifecycle_addLogRemove() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_FOLDER_PATH"), "Skipping: VANSAH_FOLDER_PATH not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addTestRunFromTestFolder(testCaseKey));
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: test folder log", 1));
+ assertDoesNotThrow(() -> node.removeTestLog());
+ assertDoesNotThrow(() -> node.removeTestRun());
+ }
+
+ // ---- Attachments (screenshot upload) ----
+
+ @Test
+ void attachmentLifecycle_addLogWithImage_thenUpdateWithImage() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_JIRA_ISSUE_KEY"), "Skipping: VANSAH_JIRA_ISSUE_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+ assumeTrue(screenshotFile != null && screenshotFile.isFile(),
+ "Skipping: no usable screenshot file (generated placeholder failed).");
+
+ assertDoesNotThrow(() -> node.addTestRunFromJIRAIssue(testCaseKey));
+
+ // addTestLog(result, comment, step, image) -- uploads the base64 screenshot as an attachment.
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: log with screenshot attachment", 1, screenshotFile));
+
+ // updateTestLog(result, comment, image) -- updates the same log with an attachment.
+ assertDoesNotThrow(() -> node.updateTestLog("failed", "Automated: updated log with screenshot attachment", screenshotFile));
+
+ assertDoesNotThrow(() -> node.removeTestLog());
+ assertDoesNotThrow(() -> node.removeTestRun());
+ }
+
+ // ---- Advanced Test Plan ----
+
+ @Test
+ void advancedTestPlanLifecycle_addLogRemove() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_ATP_KEY"), "Skipping: VANSAH_ATP_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addTestRunFromAdvancedTestPlan(atpAssetType, testCaseKey));
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: ATP log", 1));
+ assertDoesNotThrow(() -> node.removeTestLog());
+ assertDoesNotThrow(() -> node.removeTestRun());
+ }
+
+ // ---- Standard Test Plan ----
+
+ @Test
+ void standardTestPlanLifecycle_addLogRemove() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_STP_KEY"), "Skipping: VANSAH_STP_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addTestRunFromStandardTestPlan(testCaseKey));
+ assertDoesNotThrow(() -> node.addTestLog("passed", "Automated: STP log", 1));
+ assertDoesNotThrow(() -> node.removeTestLog());
+ assertDoesNotThrow(() -> node.removeTestRun());
+ }
+
+ // ---- Quick Test (single overall result, no steps) ----
+
+ @Test
+ void quickTestFromJiraIssue() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_JIRA_ISSUE_KEY"), "Skipping: VANSAH_JIRA_ISSUE_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addQuickTestFromJiraIssue(testCaseKey, 2));
+ }
+
+ @Test
+ void quickTestFromTestFolders() throws Exception {
+ assumeTrue(Env.isSet("VANSAH_PROJECT_KEY"), "Skipping: VANSAH_PROJECT_KEY not set.");
+ assumeTrue(Env.isSet("VANSAH_FOLDER_PATH"), "Skipping: VANSAH_FOLDER_PATH not set.");
+ assumeTrue(Env.isSet("VANSAH_TEST_CASE_KEY"), "Skipping: VANSAH_TEST_CASE_KEY not set.");
+
+ assertDoesNotThrow(() -> node.addQuickTestFromTestFolders(testCaseKey, 2));
+ }
+
+ /**
+ * Tiny config reader: loads a .env file at the project root (KEY=VALUE per line,
+ * '#' comments and blank lines ignored) and falls back to real OS environment
+ * variables so the same tests work unmodified in CI.
+ */
+ private static final class Env {
+ private static final Map VALUES = load();
+
+ private Env() {
+ }
+
+ private static Map load() {
+ Map values = new HashMap<>();
+ Path envFile = Paths.get(".env");
+ if (Files.isReadable(envFile)) {
+ try {
+ for (String line : Files.readAllLines(envFile)) {
+ String trimmed = line.trim();
+ if (trimmed.isEmpty() || trimmed.startsWith("#")) {
+ continue;
+ }
+ int eq = trimmed.indexOf('=');
+ if (eq <= 0) {
+ continue;
+ }
+ String key = trimmed.substring(0, eq).trim();
+ String value = trimmed.substring(eq + 1).trim();
+ if (value.length() >= 2
+ && ((value.startsWith("\"") && value.endsWith("\""))
+ || (value.startsWith("'") && value.endsWith("'")))) {
+ value = value.substring(1, value.length() - 1);
+ }
+ values.put(key, value);
+ }
+ } catch (IOException e) {
+ System.out.println("⚠️ Warning: Failed to read .env file: " + e.getMessage());
+ }
+ }
+ return values;
+ }
+
+ static String get(String key) {
+ return get(key, null);
+ }
+
+ static String get(String key, String defaultValue) {
+ String value = VALUES.get(key);
+ if (value == null || value.isEmpty()) {
+ value = System.getenv(key);
+ }
+ if (value == null || value.isEmpty()) {
+ value = defaultValue;
+ }
+ return value;
+ }
+
+ static boolean getBoolean(String key, boolean defaultValue) {
+ String value = get(key);
+ if (value == null) {
+ return defaultValue;
+ }
+ return "true".equalsIgnoreCase(value) || "1".equals(value);
+ }
+
+ static boolean isSet(String key) {
+ String value = get(key);
+ return value != null && !value.trim().isEmpty();
+ }
+ }
+}
diff --git a/src/test/java/com/vansah/VansahNodeTests.java b/src/test/java/com/vansah/VansahNodeTests.java
deleted file mode 100644
index 2761a9d..0000000
--- a/src/test/java/com/vansah/VansahNodeTests.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.vansah;
-
-public class VansahNodeTests {
-
- //Optional if IssueKey is provided
- private static String testFolderID = "1ba1372f-54ed-11ed-8e52-5658ef8eadd5"; //TestFolder ID to which test Execution is to be perform
-
- //Optional if TestFolder ID is provided
- private static String issueKey = "VAN-1"; //IssueKey to which test Execution is to be perform
-
- //Optional
- private static String sprintName = "VAN Sprint 1"; //Sprint Name for current sprint for which test execution is to be perform
-
- //Optional
- private static String releaseName = "v1"; //Release Name linked with the current sprint and to the test case.
-
- //Optional
- private static String environment = "SYS"; //Environment Name to which test execution of a test case is to be perform
-
- //Required
- private static String testCase = "VAN-C3";
-
- public static void main(String[] args) throws Exception {
-
-
- //Provide TestFolder ID , JIRA Issue, Sprint Key, Sprint Release and Environment
- VansahNode testExecute = new VansahNode(testFolderID,issueKey);
-
- //From Jira Issue Screen
- testExecute.addTestRunFromJIRAIssue(testCase);
-
- for(int i = 1;i<=testExecute.testStepCount(testCase);i++) {
-
- //Add logs for each step function(ResultID, AcutalResultComment, TestStepID, screenshot file);
- testExecute.addTestLog(2, "This is From Java Binder Add test log", i, null);
-
- //Will update the current test log
- testExecute.updateTestLog(1, "This is From Java Binder Update Test log", null);
-
- //To remove the current test log
- testExecute.removeTestLog();
-
-
- }
-
- testExecute.removeTestRun(); //Removing current test run
-
- //From Test folder screen
- testExecute.addTestRunFromTestFolder(testCase);
-
- for(int i = 1;i<=testExecute.testStepCount(testCase);i++) {
-
- //Add logs for each step function(ResultID, AcutalResultComment, TestStepID, screenshot file);
-
- testExecute.addTestLog(2, "This is From Java Binder Add test log", i, null);
-
- //Will update the current test log
- testExecute.updateTestLog(1, "This is From Java Binder Update Test log", null);
-
- //To remove the current test log
- //testExecute.remove_test_log();
-
- }
-
- //Add Quick test for Jira issue function(testCaseKey, ResultID, AcutalResultComment, screenshot file);
-
- testExecute.addQuickTestFromJiraIssue(testCase, 0);
-
- //Add Quick test for Test folders
- testExecute.addQuickTestFromTestFolders(testCase, 0);
- }
-
-
-}
-