From c53e80a9063fbccb5ee6e2c8e569917305e34127 Mon Sep 17 00:00:00 2001 From: Valentin Zickner Date: Fri, 20 Sep 2024 21:53:15 +0200 Subject: [PATCH] add test for robocorp client --- .github/workflows/main.yml | 1 + robocorp/README.md | 4 +- robocorp/flowable/.gitignore | 1 + robocorp/setup.py | 2 +- robocorp/tests/basic_test.py | 20 + robocorp/tests/bpmn_utils.py | 55 ++ .../cassettes/test_with_custom_action.yml | 618 ++++++++++++++++++ .../fixtures/processes/robocorpExample.bpmn | 62 ++ robocorp/tests/robocorp_action_weather.py | 8 + robocorp/tests/test_robocorp_client.py | 57 ++ robocorp/tests/vcr.py | 12 + 11 files changed, 837 insertions(+), 3 deletions(-) create mode 100644 robocorp/flowable/.gitignore create mode 100644 robocorp/tests/basic_test.py create mode 100644 robocorp/tests/bpmn_utils.py create mode 100644 robocorp/tests/fixtures/cassettes/test_with_custom_action.yml create mode 100644 robocorp/tests/fixtures/processes/robocorpExample.bpmn create mode 100644 robocorp/tests/robocorp_action_weather.py create mode 100644 robocorp/tests/test_robocorp_client.py create mode 100644 robocorp/tests/vcr.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ce1ca83..98187ed 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,5 +47,6 @@ jobs: working-directory: ./external-worker/ - run: pip install -e ".[testing]" working-directory: ./robocorp/ + - run: cp -a external-worker/flowable/external_worker_client robocorp/flowable/ # we need to copy this module over, since it's only searching locally for modules - run: python -m unittest discover working-directory: ./robocorp/ diff --git a/robocorp/README.md b/robocorp/README.md index 1d37181..7cb219b 100644 --- a/robocorp/README.md +++ b/robocorp/README.md @@ -1,4 +1,4 @@ -# Flowable Robocorp Client +from typing import Dict# Flowable Robocorp Client [License: ![license](https://img.shields.io/hexpm/l/plug.svg)](https://github.com/flowable/flowable-external-client-python/blob/main/LICENSE) @@ -31,7 +31,7 @@ The following example `get-weather-forecast.py` can be used as a Robocorp action from robocorp.actions import action @action -def get_weather_forecast(city: str, days: int, scale: str = "celsius") -> str: +def get_weather_forecast(city: str, days: int, scale: str = "celsius") -> dict[str, str]: """ Returns weather conditions forecast for a given city. diff --git a/robocorp/flowable/.gitignore b/robocorp/flowable/.gitignore new file mode 100644 index 0000000..a5fedf3 --- /dev/null +++ b/robocorp/flowable/.gitignore @@ -0,0 +1 @@ +external_worker_client \ No newline at end of file diff --git a/robocorp/setup.py b/robocorp/setup.py index 291ff9e..2516080 100644 --- a/robocorp/setup.py +++ b/robocorp/setup.py @@ -17,6 +17,6 @@ license='Apache License, Version 2.0', install_requires=['flowable.external-worker-client>=1.0.1rc1', 'robocorp-actions>=0.2.1', 'robocorp-tasks>=3.1.1', 'robocorp-truststore>=0.9.1'], extras_require={ - 'testing': ['pytest', 'vcrpy'] + 'testing': ['pytest', 'vcrpy', 'flowable.external-worker-client>=1.0.1rc1'] }, ) diff --git a/robocorp/tests/basic_test.py b/robocorp/tests/basic_test.py new file mode 100644 index 0000000..1b414f4 --- /dev/null +++ b/robocorp/tests/basic_test.py @@ -0,0 +1,20 @@ +import unittest +from pathlib import Path + +from requests.auth import HTTPBasicAuth + +from tests.bpmn_utils import deploy_process, get_process_definition_id, delete_deployment + +base_url = "http://localhost:8090/flowable-work" +auth = HTTPBasicAuth("admin", "test") + + +class BasicTest(unittest.TestCase): + + def deploy_process(self, fileToDeploy='externalWorkerProcess.bpmn'): + self._process_deployment_id = deploy_process(base_url, auth, str(Path(__file__).parent.absolute()) + '/fixtures/processes/' + fileToDeploy) + self._process_definition_id = get_process_definition_id(base_url, auth, self._process_deployment_id) + + def remove_deployment(self): + if hasattr(self, "_process_deployment_id"): + delete_deployment(base_url, auth, self._process_deployment_id) diff --git a/robocorp/tests/bpmn_utils.py b/robocorp/tests/bpmn_utils.py new file mode 100644 index 0000000..9e855d3 --- /dev/null +++ b/robocorp/tests/bpmn_utils.py @@ -0,0 +1,55 @@ +import requests +from requests.auth import AuthBase + + +def deploy_process(base_url: str, auth: AuthBase, file: str) -> str: + files = {'file': open(file, 'rb')} + deployment_result = requests.post(base_url + '/process-api/repository/deployments', auth=auth, files=files) + assert deployment_result.status_code == 201 + return deployment_result.json().get("id") + + +def delete_deployment(base_url: str, auth: AuthBase, process_deployment_id: str) -> None: + delete_deployment_result = requests.delete(base_url + "/process-api/repository/deployments/" + process_deployment_id, auth=auth) + assert delete_deployment_result.status_code == 204 + + +def get_process_definition_id(base_url: str, auth: AuthBase, process_deployment_id: str) -> None: + definition_search = requests.get(base_url + "/process-api/repository/process-definitions?deploymentId=" + process_deployment_id, auth=auth) + assert definition_search.status_code == 200 + data = definition_search.json().get("data") + assert len(data) == 1 + return data[0].get("id") + + +def start_process(base_url: str, auth: AuthBase, process_definition_id: str, variables: list[object] = []) -> str: + start_result = requests.post( + base_url + "/process-api/runtime/process-instances", + auth=auth, + json={"processDefinitionId": process_definition_id, "variables": variables}, + headers={"Content-Type": "application/json"} + ) + assert start_result.status_code == 201 + return start_result.json().get("id") + + +def terminate_process(base_url: str, auth: AuthBase, process_instance_id: str) -> None: + delete_result = requests.delete(base_url + "/process-api/runtime/process-instances/" + process_instance_id, auth=auth) + assert delete_result.status_code == 204 or delete_result.status_code == 404 + + +def get_process_variable(base_url: str, auth: AuthBase, process_instance_id: str, variable_name: str) -> dict | None: + variable_result = requests.get(base_url + "/process-api/history/historic-variable-instances?processInstanceId=" + process_instance_id + "&variableName=" + variable_name, auth=auth) + assert variable_result.status_code == 200 + data = variable_result.json().get("data") + if len(data) == 1: + return data[0].get("variable") + else: + return None + + +def executed_activity_ids(base_url: str, auth: AuthBase, process_instance_id: str) -> list[str]: + activity_instances = requests.get(base_url + "/process-api/history/historic-activity-instances?processInstanceId=" + process_instance_id, auth=auth) + assert activity_instances.status_code == 200 + data = activity_instances.json().get("data") + return sorted(list(map(lambda o: o.get("activityId"), data))) diff --git a/robocorp/tests/fixtures/cassettes/test_with_custom_action.yml b/robocorp/tests/fixtures/cassettes/test_with_custom_action.yml new file mode 100644 index 0000000..80ed24a --- /dev/null +++ b/robocorp/tests/fixtures/cassettes/test_with_custom_action.yml @@ -0,0 +1,618 @@ +interactions: +- request: + body: "--7d6d74e28e95ea697b80fb2e866f3861\r\nContent-Disposition: form-data; name=\"file\"; + filename=\"robocorpExample.bpmn\"\r\n\r\n\n\n + \ \n \n + \ \n \n + \ \n + \ \n \n + \ \n \n \n \n + \ \n + \ \n \n + \ \n \n \n \n + \ \n + \ \n \n + \ \n \n \n + \ \n \n + \ \n \n \n \n + \ \n \n \n \n + \ \n \n + \ \n \n \n + \ \n + \ \n + \ \n + \ \n \n \n \n \n \n \n + \ \n \n + \ \n \n + \ \n \n + \ \n \n + \ \n \n\r\n--7d6d74e28e95ea697b80fb2e866f3861--\r\n" + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + Content-Length: + - '4594' + Content-Type: + - multipart/form-data; boundary=7d6d74e28e95ea697b80fb2e866f3861 + User-Agent: + - python-requests/2.32.3 + method: POST + uri: http://localhost:8090/flowable-work/process-api/repository/deployments + response: + body: + string: '{"id":"PRC-d7476565-7789-11ef-8757-fe6a9bab3681","name":"robocorpExample","deploymentTime":"2024-09-20T21:52:17.778+02:00","category":null,"parentDeploymentId":"PRC-d7476565-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/process-api/repository/deployments/PRC-d7476565-7789-11ef-8757-fe6a9bab3681","tenantId":""}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=62423fb1-697e-413c-9b3d-3f24f0e9780b; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '337' + status: + code: 201 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/process-api/repository/process-definitions?deploymentId=PRC-d7476565-7789-11ef-8757-fe6a9bab3681 + response: + body: + string: '{"data":[{"id":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/process-api/repository/process-definitions/PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","key":"robocorpExample","version":3,"name":"Robocorp + Example","description":null,"tenantId":"","deploymentId":"PRC-d7476565-7789-11ef-8757-fe6a9bab3681","deploymentUrl":"http://localhost:8090/flowable-work/process-api/repository/deployments/PRC-d7476565-7789-11ef-8757-fe6a9bab3681","resource":"http://localhost:8090/flowable-work/process-api/repository/deployments/PRC-d7476565-7789-11ef-8757-fe6a9bab3681/resources/robocorpExample.bpmn","diagramResource":null,"category":"http://flowable.org/test","graphicalNotationDefined":true,"suspended":false,"startFormDefined":false}],"total":1,"start":0,"sort":"name","order":"asc","size":1}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=73278ac4-c116-42b6-bee9-73ed4296f913; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '856' + status: + code: 200 + message: '' +- request: + body: '{"processDefinitionId": "PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681", + "variables": [{"name": "city", "type": "string", "value": "Zurich"}, {"name": + "days", "type": "integer", "value": 3}]}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + Content-Length: + - '204' + Content-Type: + - application/json + User-Agent: + - python-requests/2.32.3 + method: POST + uri: http://localhost:8090/flowable-work/process-api/runtime/process-instances + response: + body: + string: '{"id":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/process-api/runtime/process-instances/PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","name":null,"businessKey":null,"businessStatus":null,"suspended":false,"ended":false,"processDefinitionId":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","processDefinitionUrl":"http://localhost:8090/flowable-work/process-api/repository/process-definitions/PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","processDefinitionName":"Robocorp + Example","processDefinitionDescription":null,"activityId":null,"startUserId":"admin","startTime":"2024-09-20T21:52:18.044+02:00","superProcessInstanceId":null,"variables":[{"name":"city","type":"string","value":"Zurich","scope":"local"},{"name":"initiator","type":"string","value":"admin","scope":"local"},{"name":"days","type":"integer","value":3,"scope":"local"}],"callbackId":null,"callbackType":null,"referenceId":null,"referenceType":null,"propagatedStageInstanceId":null,"tenantId":"","completed":false}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=051033f2-6205-488e-84e9-aa4140c00722; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '1046' + status: + code: 201 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/external-job-api/jobs?processInstanceId=PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681 + response: + body: + string: '{"data":[{"id":"JOB-d7707145-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681","correlationId":"d7707144-7789-11ef-8757-fe6a9bab3681","processInstanceId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","processDefinitionId":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","executionId":"PRC-d770231e-7789-11ef-8757-fe6a9bab3681","scopeId":null,"subScopeId":null,"scopeDefinitionId":null,"scopeType":null,"elementId":"bpmnTask_1","elementName":"Get + Weather Forecast","retries":3,"exceptionMessage":null,"dueDate":null,"createTime":"2024-09-20T19:52:18.047Z","tenantId":"","lockOwner":null,"lockExpirationTime":null}],"total":1,"start":0,"sort":"id","order":"asc","size":1}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=8d8dec72-f0fc-4452-a82c-a18f107e41c9; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '765' + status: + code: 200 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681?i=0 + response: + body: + string: '{"id":"JOB-d7707145-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681","correlationId":"d7707144-7789-11ef-8757-fe6a9bab3681","processInstanceId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","processDefinitionId":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","executionId":"PRC-d770231e-7789-11ef-8757-fe6a9bab3681","scopeId":null,"subScopeId":null,"scopeDefinitionId":null,"scopeType":null,"elementId":"bpmnTask_1","elementName":"Get + Weather Forecast","retries":3,"exceptionMessage":null,"dueDate":null,"createTime":"2024-09-20T19:52:18.047Z","tenantId":"","lockOwner":null,"lockExpirationTime":null}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=29769a42-9c6e-476b-8e5c-924e7cdf37fd; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '699' + status: + code: 200 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681?i=1 + response: + body: + string: '{"id":"JOB-d7707145-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681","correlationId":"d7707144-7789-11ef-8757-fe6a9bab3681","processInstanceId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","processDefinitionId":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","executionId":"PRC-d770231e-7789-11ef-8757-fe6a9bab3681","scopeId":null,"subScopeId":null,"scopeDefinitionId":null,"scopeType":null,"elementId":"bpmnTask_1","elementName":"Get + Weather Forecast","retries":3,"exceptionMessage":null,"dueDate":null,"createTime":"2024-09-20T19:52:18.047Z","tenantId":"","lockOwner":"lelia.local-44317","lockExpirationTime":"2024-09-20T19:53:18.540Z"}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:17 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=d9451b5a-7346-4344-8983-65b324b94404; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '736' + status: + code: 200 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681?i=2 + response: + body: + string: '{"id":"JOB-d7707145-7789-11ef-8757-fe6a9bab3681","url":"http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681","correlationId":"d7707144-7789-11ef-8757-fe6a9bab3681","processInstanceId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","processDefinitionId":"PRC-robocorpExample:3:d749d667-7789-11ef-8757-fe6a9bab3681","executionId":"PRC-d770231e-7789-11ef-8757-fe6a9bab3681","scopeId":null,"subScopeId":null,"scopeDefinitionId":null,"scopeType":null,"elementId":"bpmnTask_1","elementName":"Get + Weather Forecast","retries":3,"exceptionMessage":null,"dueDate":null,"createTime":"2024-09-20T19:52:18.047Z","tenantId":"","lockOwner":"lelia.local-44317","lockExpirationTime":"2024-09-20T19:53:18.540Z"}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:18 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=3045f0c1-f8f9-4375-9400-d8cf7d519b57; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '736' + status: + code: 200 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/external-job-api/jobs/JOB-d7707145-7789-11ef-8757-fe6a9bab3681?i=3 + response: + body: + string: '{"message":"Not found","exception":"Could not find external worker + job with id ''JOB-d7707145-7789-11ef-8757-fe6a9bab3681''."}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:18 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=ebf40149-a726-467b-b666-a2ee794e55b5; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '124' + status: + code: 404 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: http://localhost:8090/flowable-work/process-api/history/historic-variable-instances?processInstanceId=PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681&variableName=temperature + response: + body: + string: '{"data":[{"id":"VAR-d81f9d09-7789-11ef-8757-fe6a9bab3681","processInstanceId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","processInstanceUrl":"http://localhost:8090/flowable-work/process-api/history/historic-process-instances/PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","taskId":null,"executionId":"PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681","variable":{"name":"temperature","type":"integer","value":18,"scope":"global"}}],"total":1,"start":0,"sort":"variableName","order":"asc","size":1}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:18 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=118c41ad-05fe-4c95-8282-b11ef9433cfc; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '485' + status: + code: 200 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - python-requests/2.32.3 + method: DELETE + uri: http://localhost:8090/flowable-work/process-api/runtime/process-instances/PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681 + response: + body: + string: '{"message":"Not found","exception":"Could not find a process instance + with id ''PRC-d76ffc09-7789-11ef-8757-fe6a9bab3681''."}' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Content-Type: + - application/json;charset=UTF-8 + Date: + - Fri, 20 Sep 2024 19:52:18 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=544145b7-2c6a-4f55-861d-1aecc58aea32; Path=/flowable-work + Transfer-Encoding: + - chunked + Vary: + - origin,access-control-request-method,access-control-request-headers,accept-encoding + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + content-length: + - '123' + status: + code: 404 + message: '' +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic YWRtaW46dGVzdA== + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - python-requests/2.32.3 + method: DELETE + uri: http://localhost:8090/flowable-work/process-api/repository/deployments/PRC-d7476565-7789-11ef-8757-fe6a9bab3681 + response: + body: + string: '' + headers: + Cache-Control: + - no-cache, no-store, max-age=0, must-revalidate + Connection: + - keep-alive + Date: + - Fri, 20 Sep 2024 19:52:18 GMT + Expires: + - '0' + Keep-Alive: + - timeout=60 + Pragma: + - no-cache + Set-Cookie: + - XSRF-TOKEN=f2d2e2e7-ad28-407c-9759-9d471b06d820; Path=/flowable-work + Vary: + - Origin + - Access-Control-Request-Method + - Access-Control-Request-Headers + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-XSS-Protection: + - '0' + status: + code: 204 + message: '' +version: 1 diff --git a/robocorp/tests/fixtures/processes/robocorpExample.bpmn b/robocorp/tests/fixtures/processes/robocorpExample.bpmn new file mode 100644 index 0000000..b9d47e1 --- /dev/null +++ b/robocorp/tests/fixtures/processes/robocorpExample.bpmn @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/robocorp/tests/robocorp_action_weather.py b/robocorp/tests/robocorp_action_weather.py new file mode 100644 index 0000000..3aa2835 --- /dev/null +++ b/robocorp/tests/robocorp_action_weather.py @@ -0,0 +1,8 @@ +from robocorp.actions import action + +@action +def get_weather_forecast(city: str, days: int, scale: str = "celsius") -> dict[str, int]: + # Just a random implementation which multiplies the length of the city by the days + return { + "temperature": days * len(city) + } \ No newline at end of file diff --git a/robocorp/tests/test_robocorp_client.py b/robocorp/tests/test_robocorp_client.py new file mode 100644 index 0000000..ff36d91 --- /dev/null +++ b/robocorp/tests/test_robocorp_client.py @@ -0,0 +1,57 @@ +import os +import subprocess +import sys +from time import sleep + +import requests + +from tests.basic_test import BasicTest, base_url, auth +from tests.bpmn_utils import start_process, terminate_process, get_process_variable + +from tests.vcr import my_vcr + + +class TestRestClient(BasicTest): + @my_vcr.use_cassette + def test_with_custom_action(self): + self.deploy_process('robocorpExample.bpmn') + process_instance_id = start_process(base_url, auth, self._process_definition_id, [ + {'name': 'city', 'type': 'string', 'value': 'Zurich'}, + {'name': 'days', 'type': 'integer', 'value': 3} + ]) + try: + call_args = [sys.executable, "-m", 'flowable.robocorp_client', + '--flowable-host', base_url, + '--flowable-username', auth.username, + '--flowable-password', auth.password, + 'myTopic', + 'action', + os.path.join(os.path.dirname(os.path.abspath(__file__)), 'robocorp_action_weather.py') + ] + print(" ".join(call_args)) + + r = requests.get(base_url + '/external-job-api/jobs?processInstanceId=' + process_instance_id, auth=auth) + result = r.json() + job = result.get('data')[0].get('id') + + print('start subprocess with parameters', call_args) + process = subprocess.Popen(call_args, text=True) + i = 0 + while i < 10 and job is not None: + # we are adding the i that vcr is not assuming it's the same request + r = requests.get(base_url + '/external-job-api/jobs/' + job + '?i=' + str(i), auth=auth) + if r.status_code == 200: + print('waiting for job to complete', job) + sleep(0.2) + else: + job = None + i += 1 + + process.terminate() + + temperature = get_process_variable(base_url, auth, process_instance_id, 'temperature') + self.assertIsNotNone(temperature) + self.assertEquals(temperature['value'], 18) + finally: + terminate_process(base_url, auth, process_instance_id) + self.remove_deployment() diff --git a/robocorp/tests/vcr.py b/robocorp/tests/vcr.py new file mode 100644 index 0000000..dbf8235 --- /dev/null +++ b/robocorp/tests/vcr.py @@ -0,0 +1,12 @@ +from pathlib import Path + +import vcr +from vcr import VCR + +my_vcr = vcr.VCR( + cassette_library_dir=str(Path(__file__).parent.absolute()) + '/fixtures/cassettes', + path_transformer=VCR.ensure_suffix('.yml'), + # see https://vcrpy.readthedocs.io/en/latest/usage.html for further details; e.g. 'once' (for just at the beginning) or 'all' (for always) + record_mode='new_episodes', + decode_compressed_response=True, +)