How do I provide the correct headers for Basic Auth on a GET in OpenTest? - opentest

I'm attempting to make a GET call to a test management system that exposes an API. I want to provide Basic Auth in the header of the HTTPRequest provided as an action in OT like so.
includes: login.js
actors:
- actor: WEB
segments:
- segment: 1
actions:
- script: var xUsername = $env("X_USERNAME");
- script: var xPassword = $env("X_PASSWORD");
- script: $log("This is the username " + xUsername);
- script: $log("This is the password " + xPassword);
- description: Sample Reading testid
action: org.getopentest.actions.HttpRequest
args:
$checkpoint: true
$localData:
testRailCaseInfo: $output.body
url: https://sub.domain.io/api/v2/get_results/1234
headers:
Content-Type: application/json
Authorization: Basic xUsername xPassword
verb: GET
Is this correct?

Here are two ways to do it (please note I didn't test this code). You can either build the Authorization header value using a JavaScript expression,
- description: Read test ID
action: org.getopentest.actions.HttpRequest
args:
url: https://sub.domain.io/api/v2/get_results/1234
verb: GET
headers:
Content-Type: application/json
Authorization: |
$script
"Basic " + $base64($env("X_USERNAME") + ":" + $env("X_PASSWORD"))
or build the header value in a script action, ahead of time:
- script: |
// Build the authentication header
var authHeader =
"Basic " + $base64($env("X_USERNAME") + ":" + $env("X_PASSWORD"));
- description: Read test ID
action: org.getopentest.actions.HttpRequest
args:
url: https://sub.domain.io/api/v2/get_results/1234
verb: GET
headers:
Content-Type: application/json
Authorization: $script authHeader
I should probably explain what is the role or $script prefix in the two examples. When the value of an action argument starts with a dollar-prefixed symbol (like $json, $data, $format, etc.), the test actor understands that the expression is JavaScript code, evaluates the expression and uses the result as the value for the argument. When a JS expression doesn't start with a dollar-prefixed symbol (e.g. our expressions start with "Basic" and authHeader, respectively) we must prefix the expression with $script followed by one or more whitespace characters, to let the test actor know that the string that follows is JavaScript code and not just an ordinary string literal.
As for the format of the basic authentication scheme, you can find more information here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization.

Related

Access HTTP response data saved in a variable with colon

I'm using Google Cloud Workflow to call via http.get a CloudRun app that returns a XML document that has been converted to json, the below json gets successfully returned to Workflow in Step 2 which contains the converted XML to json in the body.
{
"body": {
"ResponseMessage": {
"#xmlns": "http://someurl.com/services",
"Response": {
"#xmlns:a": "http://someurl.com/data",
"#xmlns:i": "http://www.w3.org/2001/XMLSchema-instance",
"a:ReferenceNumber": {
"#i:nil": "true"
},
"a:DateTime": "2023-01-01T00:17:38+0000",
"a:TransactionId": "154200432",
"a:Environment": "Development",
"a:RequestDateTime": "2023-01-01T11:17:39",
}
},
"code": 200,
"headers": {
"Alt-Svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"",
"Content-Length": "1601",
"Content-Type": "application/json",
"Date": "Sun, 01 Jan 2023 00:17:39 GMT",
"Server": "Google Frontend",
"X-Cloud-Trace-Context": "931754ab82102397eb07775171485850"
}
}
}
The full yaml of the workflow is below and without step3/step4 it works. In step3 I try to access an element in the json which is returned from step2 as per https://cloud.google.com/workflows/docs/http-requests#access-data
main:
params: [args]
steps:
- step1:
assign:
- searchURL: ${"https://myfunction.a.run.app/search/" + args.type + "/" + args.serial}
- step2:
call: http.get
args:
url: ${searchURL}
result: s2result
- step3:
assign:
- resubmitURL: '${https://myfunction.a.run.app/resubmit/" + ${s2result.body.ResponseMessage.Response.a:TransactionId} }'
- step4:
call: http.get
args:
url: ${resubmitURL}
result: s4result
- returnOutput:
return: ${s4result}
However due to the colon : in the field I'm trying to access there are yaml parsing errors when I attempt to save another variable assignment. How can I access a HTTP response data saved in a variable when there are colons in the property field.
The errors in the console are similar too
Could not deploy workflow: main.yaml:14:25: parse error: in workflow 'main', step 'step3': token recognition error at: ':'
- resubmitURL: '${"https://myfunction.a.run.app/resubmit/" + ${s2result.body.ResponseMessage.Response.a:TransactionId}'
^
main.yaml:14:25: parse error: in workflow 'main', step 'step3': mismatched input '+' expecting {'[', LOGICAL_NOT, '(', '-', TRUE, FALSE, NULL, NUM_FLOAT, NUM_INT, STRING, IDENTIFIER}
- resubmitURL: '${"https://myfunction.a.run.app/resubmit/" + ${s2result.body.ResponseMessage.Response.a:TransactionId}'
Two techniques are required to reference map keys with special characters like this:
As recommended in the documentation, all expressions should be wrapped in single quotes to avoid YAML parsing errors (i.e. '${...}').
When referencing keys with special characters, you can use array notation to wrap the key name in quotes (i.e. var["KEY"]).
Together, it looks like this:
main:
steps:
- init:
assign:
- var:
key:
"co:lon": bar
- returnOutput:
return: '${"foo" + var.key["co:lon"]}'
In your code you are using an expression inside an expression:
- resubmitURL: '**${**"https://myfunction.a.run.app/resubmit/" + **${**s2result.body.ResponseMessage.Response.a:TransactionId**}**'
In this sample from your error message your not even closing the expressions right.
If you pack everything into one expression and use the hint from Kris with the key, it should deploy:
- resubmitURL: '${"https://myfunction.a.run.app/resubmit/" + s2result.body.ResponseMessage.Response["a:TransactionId"]}'
Here is my full test case:
main:
params: [args]
steps:
- init_assign:
assign:
- input: ${args}
- s2result:
body:
ResponseMessage:
Response:
"a:TransactionId": "Test"
- resubmitURL: '${"https://myfunction.a.run.app/resubmit/" + s2result.body.ResponseMessage.Response["a:TransactionId"]}'
- log1:
call: sys.log
args:
text: ${resubmitURL}
severity: INFO
With the log: 'https://myfunction.a.run.app/resubmit/Test'

Getting error in accessing Jmeter variables (variable_MarchNr) in yaml in beanshell script

I am trying to bulk edit one entity called issues, I call GetIssues API and via json extractor, get all issueId's in variable "issueIds"
json extractor to extract issueIds
Now I want to pass these Ids in other api bulk edit issues, If I directly use these array in next API, I get below error:
{"details":"Unexpected token $ in JSON at position 19","metadata":{}}
So I used to below code in Beanshell Post processor:
var mylist;
props.put("mylist", new ArrayList());
log.info("Detected " + vars.get("issueIds_matchNr") + " issueIds");
for (int i=1; i<= Integer.parseInt(vars.get("issueIds_matchNr")); i++) {
log.info("IDs # " + i + ": " + vars.get("issueIds_" + i));
props.get("mylist").add('"' + vars.get("issueIds_" + i) + '"' );
}
log.info(props.get("mylist").toString());
var issueIdList;
vars.put("issueIdList", props.get("mylist").toString());
log.info(vars.get("issueIdList"));
In my next api call if I pass issueIdList variable, then this works fine in jmeter.
sample variable values in debug sampler are like:
issueIdList=["555bcfc2", "33974d2c", "e58db1d6"]
issueIds_1=555bcfc2
issueIds_2=33974d2c
issueIds_3=e58db1d6
issueIds_matchNr=3
Problem I am facing if I convert my jmx2yaml and tried to run this file with
bzt issues.yml
then while executing above shell script, these issueIds_matchNr, issueIds_3 are not detected, I get below error;
2022-05-29 08:26:10,785 INFO o.a.j.e.J.JSR223PostProcessor: Detected null issueIds
2022-05-29 08:26:10,795 ERROR o.a.j.e.JSR223PostProcessor: Problem in JSR223 script, JSR223PostProcessor
javax.script.ScriptException: Sourced file: eval stream : Method Invocation Integer.parseInt : at Line: 4 : in file: eval stream : Integer .parseInt ( vars .get ( "issueIds_matchNr" ) )
Target exception: java.lang.NumberFormatException: null
in eval stream at line number 4
at bsh.engine.BshScriptEngine.evalSource(BshScriptEngine.java:87) ~[bsh-2.0b6.jar:2.0b6 2016-02-05 05:16:19]
My Yaml script is:
- extract-jsonpath:
issueIds:
default: NotFound
jsonpath: $..id
follow-redirects: true
jsr223:
- compile-cache: true
execute: after
language: beanshell
parameters: issueIds
script-file: script.bsh
label: Get Issue Id's
method: GET
url: https://${BASE_URL1}/${PROJECT_ID}/issues?limit=5&sortBy=-displayId&filter%5Bvalid%5D=true
You're missing one important bit: setting the Match Nr in your Taurus YAML
The correct definition of the JSON Extractor would be something like:
extract-jsonpath:
issueIds:
jsonpath: $..id
match-no: -1 #this is what you need to add
Also be informed that starting from JMeter 3.1 it's recommended to use Groovy as the scripting language so consider migrating, it will be as simple as removing the first line from your script.bsh

Taurus pre-request authentication

I've faced a problem using Taurus. Could someone help me, please? I'm trying to simulate 300 users but before sending those 300 users POST requests, I need to generate a token. The token is attached to the request in that way:
- url: http://url?user_token=${auth_token}
Now I have the following scenario:
load_api:
requests:
- once:
- url: https://endpoint/authenticateUser
method: POST
headers:
Content-Type: application/json
body:
username: username
password: pass
generateToken: true
extract-jsonpath:
auth_token:
jsonpath: $.token
label: get_token
- url: http://url/user_token=${auth_token}
method: POST
headers:
Content-Type: application/json
body-file: test_data/body.json
label: sending_300
As you can see, a token is generated for each thread. And I need that it is generated before the script and then the token is attached to the URL as a parameter. I've tried to separate those for two scenarios but in that way variables from one script can't be used in the other. I was also looking at global variables but it seems like that kind of variable can be created only before executing. So, if someone could help me, I'll appreciate your time spent.
EDIT (thank you so much Dmitri T):
Here is a workable script:
execution:
concurrency: 300
scenario: load_test
scenarios:
load_test:
requests:
- if: ${__groovy(ctx.getThreadNum() == 0 && vars.getIteration() == 1,)}
then:
- url: https://url/authenticateUser
method: POST
headers:
Content-Type: application/json
body:
username: username
password: pass
generateToken: true
extract-jsonpath:
auth_token:
jsonpath: $.token
label: get_token
jsr223: props.put('auth_token', vars.get('auth_token'))
else:
- url: http://endpoint?user_token=${__P(auth_token,)}
method: POST
headers:
Content-Type: application/json
body-file: test_data/body.json
label: sending_300_reqs
think-time: 10s # waiter for processing auth request
If you want to generate a token once and share it across 300 threads:
Generate token for 1st thread during the first iteration using If block and convert it to JMeter Property in JSR223 block. The condition for the If block would be:
${__groovy(ctx.getThreadNum() == 0 && vars.getIteration() == 1,)}
and the code for JSR223 block:
props.put('auth_token', vars.get('auth_token'))
check out Top 8 JMeter Java Classes You Should Be Using with Groovy article to learn what these ctx, props and vars words mean
In your 2nd request refer the property using __P() function
http://url/user_token=${__P(auth_token,)}

Dredd passing trailing square bracket to API

I'm using Dredd to test an API I have written. It works fine until I try to vary the action uri within a resource. When I have an action of the form
## Retrieve Task [GET /task/{id}]
it sends a request to Drakov with the ] appended. This Drakov server is running the blueprint document.
Drakov 0.1.16 Listening on port 8090
[LOG] GET /task/myid]
[LOG] DELETE /task/myid]
[LOG] GET /task/myid]
You can see this request has an extra ] on the end.
This is my blueprint. It is a subset of the example from the Api Blueprint examples:
FORMAT: 1A
# Advanced Action API
A resource action is – in fact – a state transition. This API example demonstrates an action - state transition - to another resource.
## API Blueprint
# Tasks [/tasks/tasks{?status,priority}]
+ Parameters
+ status `test` (string)
+ priority `1` (number)
## Retrieve Task [GET /task/{id}]
This is a state transition to another resource
+ Parameters
+ id: `myid` (string)
+ Response 200 (application/json)
{
"id": 123,
"name": "Go to gym",
"done": false,
"type": "task"
}
What am I doing wrong?
Your API Blueprint has multiple errors. For instance,
+ Parameters
+ status `test` (string)
+ priority `1` (number)
...should be:
+ Parameters
+ status: `test` (string)
+ priority: `1` (number)
Also, you are defining a resource Tasks with URI Template /tasks/tasks{?status,priority} and then you are trying to define a GET action for the resource with a different URI Template. That is confusing.
I tried to create a sample API Blueprint (saved as sample-blueprint.md) like this:
FORMAT: 1A
# My API
## Task [/task/{id}]
### Retrieve Task [GET]
+ Parameters
+ id: `123` (string)
+ Response 200 (application/json)
{
"id": 123,
"name": "Go to gym",
"done": false,
"type": "task"
}
Then I launched a Drakov server in one terminal like this:
drakov -f *.md
Then I tried to run Dredd:
dredd sample-blueprint.md http://localhost:3000
Everything passed correctly:
$ dredd sample-blueprint.md http://localhost:3000
info: Beginning Dredd testing...
pass: GET /task/123 duration: 42ms
complete: 1 passing, 0 failing, 0 errors, 0 skipped, 1 total
complete: Tests took 50ms
Is this something you originally wanted to achieve?

Different request on same action with parameters

I want to search (say) "accounts" based on "name" or "status".
So I would like to have two actions :
GET /persons/?q=name==Jea*
GET /persons/?q=status==locked
How can I document that ?
I tried an Action with multiples transactions :
### GET /accounts{?q}
+ Request by name
+Parameters
+q (required, FIQLQuery)
**Only name is supported**
+ Request by status
+Parameters
+q (required, FIQLQuery)
**Only status is supported**
But Apiary editor complains because :
I must provide a message-body for my GET requests:
Message-body asset is expected to be a pre-formatted code block, every of its line indented by exactly 8 spaces or 2 tabs.
The + Parameters block is not recognized :
Ignoring unrecognized block
Thanks a lot
i can make solution that works for me.
Try this API Blueprint:
FORMAT: 1A
# requestByname
## Accounts [/accounts/?{q,type}]
### GET
+ Parameters
+ q (required, FIQLQuery)
+ type (string, `name` or `status`)
+ Request Name (application/json)
+ Response 200
{"name": "test"}
+ Request Status (application/json)
+ Response 200
{"status": 200}

Resources