Harpi is a cli tool that executes *.harpi.yml files which are simple text based http request scripts. The file format of the *.harpi.yml files support sending http requests and asserting response data. Example:
variables:
baseAddress: "https://test.com"
itemId: "1"
itemTitle: "item 1"
requests:
- name: "create data"
method: "post"
url: "$(baseAddress)/api/data/$(itemId)"
jsonBody:
title: "$(itemTitle)"
- name: "verify data now available"
method: "get"
url: "$(baseAddress)/api/data/$(itemId)"
asserts:
statusCodeEquals: 200
codeAsserts:
- name: "has expected title"
code: "response.title == '$(itemTitle)'"The file can then be executed by using the "run" command
harpi run mytestfile.harpi.yml
npm install harpi_cli -g
Download the example script to your current directory
curl -O https://raw.githubusercontent.com/teslae1/harpi/main/example.harpi.yml
List the requests of the script
harpi ls example.harpi.yml
Run the script
harpi run example.harpi.yml
It is possible to assign response data to a variable for later use:
variables:
myResponseValue: #Assigned by first request
baseAddress: "https://test.com"
requests:
- name: "create data with auto generated id"
method: "post"
url: "$(baseAddress)/api/data"
variableAssignments:
- variableName: myResponseValue
code: "response.id"
- name: "verify data now available at id"
method: "get"
url: "$(baseAddress)/api/data/$(myResponseValue)"
asserts:
statusCodeEquals: 200It is possible to execute a single request - using the "ls" command all the http requests are listed with an id:
$> harpi ls myfile.harpi.yml
$> - request
$> - id: 1
$> - url: $(baseAddress)/api/data
$> - method: post
$> - request
$> - id: 2
$> - url: $(baseAddress)/api/data/$(myResponseValue)
$> - method: get
It is then possible to only execute request 2:
$> harpi run myfile.harpi.yml 2
It is possible to take parameters from the command line - which then will be assigned to a variable. This is achieved by using the "required" keyword:
variables:
token: required
apiKey: required
headers:
Authorization: "Bearer $(token)"
ApiKey: "$(apiKey)"
requests:
- name: "verify data now available at id"
method: "get"
url: "https://test.com/api/data/2"
asserts:
statusCodeEquals: 200This will now make it required to provide the defined variables when running the file:
$> harpi run myfile.harpi.yml --variables token=MYTOKEN,apiKey=MYAPIKEY
Example of setting headers for all requests
variables:
token: required
apiKey: required
headers:
Authorization: "Bearer $(token)"
ApiKey: "$(apiKey)"
requests:
- name: "This request will have both the headers defined in header"
method: "get"
url: "https://test.com/api/data/2"
asserts:
statusCodeEquals: 200
- name: "This request will also have both the headers defined in header"
method: "get"
url: "https://test.com/api/data/2"
asserts:
statusCodeEquals: 200It is possible to insert a wait between two requests:
variables:
myResponseValue: #Assigned by first request
baseAddress: "https://test.com"
requests:
- name: "create data with auto generated id"
method: "post"
url: "https://test.com/api/data/1"
#Waiting 500 milliseconds before executing next request
waitBeforeNextRequest:
name: "Waiting for data to be prepared"
milliseconds: 500
- name: "verify data now available at id"
method: "get"
url: "https://test.com/api/data/1"
asserts:
statusCodeEquals: 200It is possible to auto generate values like guids and dates. The values will be regenerated whenever the first request is executed
variables:
currentDate: $(date)
dateFiveMinutesInFuture: $(date.addMinutes(5))
autoGeneratedId: $(guid)
requests:
- url: "https://test.com/api/data/$(autoGeneratedId)"
method: "post"
jsonBody:
someDateProperty: "$(currentDate)"
anotherDateProperty: "$(dateFiveMinutesInFuture)"Harpi supports a set of asserts and also supports you defining custom asserts as 'codeAsserts". codeAsserts have replaced "javascriptAsserts" since they are safer and evaluated using a minimal intepreter that does not allowed access to environment. The 'code' part of codeAsserts uses javascript-like syntax that makes it possible to define custom expressions that are expected to evaluate to a boolean value. The 'response' is the object representing the current response. if the response was json then the available 'response' object is an object deserialized from that json. If it was not json then the object is just the response body represented as a string.
variables:
baseAddress: "https://test.com"
requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
statusCodeEquals: 200
responseContains: "testTitle"
codeAsserts:
- name: "assert first element has expected title"
code: "response[0].title == 'testTitle'"
The following is a list of examples that are supported by as code asserts
requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert text response includes value"
code: "Object.values(response).includes('val')"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert element was inactive"
code: "response.isActive == false"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert value had length more than zero"
code: "response.value.length > 0"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "Assert first response had expected content inside of description"
code: "response.value[0].Description.includes('desc')"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "Assert response first element description is either null or had expected content in description"
code: "(response.value[0].Description == null) || (response.value[0].Description.includes('desc'))"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "Assert first element had a time generated that was older than 2025-10-02"
code: "new Date(response[0].timeGenerated).getTime() < new Date('2025-10-02T00:00:00.9625552+00:00').getTime()"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "Assert start of incomingText has the expected value"
code: "response.incomingText.substring(0,8) == 'expsubtext'.substring(0,8)"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert array response has atleast one element that is active"
code: "response.some(r => r.isActive)"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert array response has atleast one element that is active"
code: "response.some(r => r.isActive)"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert array response has exactly two elements that are active"
code: "response.filter(r => r.isActive).length == 2"requests:
- name: "assert on response"
method: "get"
url: "$(baseAddress)/api/data/1"
asserts:
codeAsserts:
- name: "assert array response first active element has expected id"
code: "response.find(r => r.isActive).id == 1"