Gatling Error when running multiple users - 'httpRequest-2' failed to execute: No attribute named 'access_token' is defined - scala

im new to Gatling and have been trying to setup a test where my users login, get an access token, then perform some simple get requests using that token. Having 1-2 users works fine, however once i start ramping up the users i start getting spammed with this error:
[ERROR] i.g.h.a.HttpRequestAction - 'httpRequest-2' failed to execute: No attribute named 'access_token' is defined
Im thinking it could have something to do with the way I am saving and using the access token ?
class GatlingTest extends Simulation {
val httpProtocol = http
.baseUrl("https://myurl.com/api/v1")
.inferHtmlResources(BlackList(""".*\.js""", """.*\.css""", """.*\.gif""", """.*\.jpeg""", """.*\.jpg""", """.*\.ico""", """.*\.woff""", """.*\.woff2""", """.*\.(t|o)tf""", """.*\.png""", """.*detectportal\.firefox\.com.*"""), WhiteList())
.acceptLanguageHeader("en-GB,en;q=0.5")
.upgradeInsecureRequestsHeader("1")
object GetUserData {
val userData = exec(http("Get_User_Data")
.get("/user")
.header("Authorization", "Bearer ${access_token}"))
.pause(1)
}
object GetUserInfo {
val userInfo = exec(http("Get_User_Info")
.get("/userInfo")
.header("Authorization", "Bearer ${access_token}")
.header("Accept", "application/json"))
.pause(1)
}
object Login {
val sentHeaders = Map("api_key" -> "nnxzv336wt2374h6zw5x24qd", "Content-Type" -> "application/x-www-form-urlencoded", "Accept" -> "application/json")
val login = exec(http("Login_User")
.post("/login")
.basicAuth("username", "password")
.headers(sentHeaders)
.body(StringBody("grant_type=password&username=username#username.local&password=12345"))
.check(jsonPath("$.access_token").saveAs("access_token"))
)
}
val user = scenario("User").exec(Login.login).exec(GetUserData.userData, GetUserInfo.userInfo)
setUp(
user.inject(
rampUsers(5).during(2.seconds),
).protocols(httpProtocol)
)
}
I have added Authorization Bearer to the get requests, like i mentioned it does work, but as soon as 3+ users are involved i get the error.

It means the login request failed and hence, the user wasn't able to capture the access_token there.

Related

Auth token handling for Gatling

Doing performance testing of API using Gatling.
Scenario:
Login (authToken will be generated in header)
For GET, POST, PUT request, need to pass that generated authToken in header
Here's my code snippet:
package apitest
import scala.concurrent.duration.*
import io.gatling.core.Predef.*
import io.gatling.http.Predef.*
import io.gatling.jdbc.Predef.*
import scala.language.postfixOps
class TestEnv4trial extends Simulation {
var e1: String = "https://testenv1-dev.net"
var e2: String = "https://testenv2-dev.net"
var BaseUrl: String = e1
var pwd: String = "pass123"
// Users
var admin: String = "admin123"
val httpProtocol = http
.baseUrl(BaseUrl)
.inferHtmlResources()
val login_headers = Map(
"Accept" -> """*/*""",
"Connection" -> "keep-alive",
"Content-Type" -> "application/x-www-form-urlencoded;charset=UTF-8"
)
val scn1 = scenario("Admin Login")
.exec(http("Login Admin")
.post({BaseUrl} + "/api/user/login")
.formParam("username", admin123)
.formParam("password", pass123)
.check(jsonPath("$.authToken").saveAs("tokenId")))
.exec { session => println(session("tokenId").as[String]); session } //authToken getting printed
val common_headers = Map(
"Accept" -> """*/*""",
"Accept-Encoding" -> "gzip, deflate, br",
"Accept-Language" -> "en-GB,en;q=0.9,en-US;q=0.8",
"Authorization" -> "Bearer " + $tokenId, //With hardcoded authToken works. Need to pass generated authToken in prev scenario here.
"Connection" -> "keep-alive",
)
val scn2 = scenario("All Employees")
.exec(http("All Employees")
.post("/api/employee/lists/")
.headers(common_headers)
.body(RawFileBody("test/TestEnv4trial/employees_request.json")).asJson)
setUp(
scn1.inject(atOnceUsers(1)).protocols(httpProtocol),
scn2.inject(atOnceUsers(1)).protocols(httpProtocol))
}
When I hardcode authToken generated in scn1 in common_headers, scn2 works.
But when I use tokenId, its not able to identify tokenId.
How do I pass saved key tokenId in common_headers?
Thanks.
"Authorization" -> "Bearer " + $tokenId
This doesn't compile.
Currently, you're using Session attributes, meaning tokenId is scoped for the single user executing scn1.
There's no way for a user executing scn2 to be able to reach it as is.

Get token id from Login request in Gatling

I am using Gatling for performance testing, so I want know that how we extract token id from the login request here is code
val scn = scenario("Navigation")
.exec(http("request_6")
.post("/WEBAUTO03/aurora/login/security_check")
.headers(headers_6)
.formParam("j_username", "TONY")
.formParam("j_password", "1234")
.formParam("doLogin", "")
Above request provide token and I need apply the token in following request
val headers_9 = Map(
"Content-type" -> "text/plain",
"Origin" -> "https://resource.com",
"X-XSRF-TOKEN" -> ""4c81ed9c-e509-4830-b724-62e489c918e2"") -----here i need to replace token
.exec(http("request_9")
.post("/WEBAUTO03/aurora/JSON-RPC")
.headers(headers_9)
.body(RawFileBody("webview/navigation/0009_request.txt")))
anyone have any idea
Without seeing the response from the "above request" we cannot suggest the exact steps, approximate would be something like:
http("request_6")
.post("/WEBAUTO03/aurora/login/security_check")
.check(css("input[name='csrf_token']", "value").saveAs("Correlation1"))
val headers_9 = Map(
"Content-type" -> "text/plain",
"Origin" -> "https://resource.com",
"X-CSRF-Token" -> "${Correlation1}")
More information:
Gatling HTTP Checks
How to Run a Simple Load Test with Gatling

Pass specific value from feeder to script in gatling

I am working on performance test, for that I have below Gatling script -
val getUserById: ChainBuilder = feed(userEmailFeeder).exec(http("User By Id")
.get("url")
.headers(getHeaders)
.check(status is 200)
)
private val getHeaders = Map.apply(
"Content-Type" -> "application/json",
"Accept" -> "application/json",
"token" -> {tokenValue}
)
object BearerToken {
//Generating token here
}
In userEmailFeeder I have user emails and passwords. I have to generate a token for every email present in feeder and add to header in getHeader.
Can someone guide me how I can pass same email & associated password to BearerToken for which getUserById is referring from feeder so it will genearte token and add into header?
You can create method which will get your email and password from session, generate token and then write this values to session.
val generateTokenByEmailAndPassword: Expression[Session] = (session: Session) => {
val email = session("email").as[String]
val password = session("password").as[String]
// your logic for generate token
val token = email + password
session.set("tokenValue", token)
}
And then add to scenarion
...
.feed(userEmailFeeder)
.exec(generateTokenByEmailAndPassword)
.exec(http("User By Id")
...
A little remark - for get session value need add $
Wrong: {tokenValue}
Right way: ${tokenValue}

Groovy script for Jenkins: execute HTTP request without 3rd party libraries

I need to create a Groovy post build script in Jenkins and I need to make a request without using any 3rd party libraries as those can't be referenced from Jenkins.
I tried something like this:
def connection = new URL( "https://query.yahooapis.com/v1/public/yql?q=" +
URLEncoder.encode(
"select wind from weather.forecast where woeid in " + "(select woeid from geo.places(1) where text='chicago, il')",
'UTF-8' ) )
.openConnection() as HttpURLConnection
// set some headers
connection.setRequestProperty( 'User-Agent', 'groovy-2.4.4' )
connection.setRequestProperty( 'Accept', 'application/json' )
// get the response code - automatically sends the request
println connection.responseCode + ": " + connection.inputStream.text
but I also need to pass a JSON in the POST request and I'm not sure how I can do that. Any suggestion appreciated.
Executing POST request is pretty similar to a GET one, for example:
import groovy.json.JsonSlurper
// POST example
try {
def body = '{"id": 120}'
def http = new URL("http://localhost:8080/your/target/url").openConnection() as HttpURLConnection
http.setRequestMethod('POST')
http.setDoOutput(true)
http.setRequestProperty("Accept", 'application/json')
http.setRequestProperty("Content-Type", 'application/json')
http.outputStream.write(body.getBytes("UTF-8"))
http.connect()
def response = [:]
if (http.responseCode == 200) {
response = new JsonSlurper().parseText(http.inputStream.getText('UTF-8'))
} else {
response = new JsonSlurper().parseText(http.errorStream.getText('UTF-8'))
}
println "response: ${response}"
} catch (Exception e) {
// handle exception, e.g. Host unreachable, timeout etc.
}
There are two main differences comparing to GET request example:
You have to set HTTP method to POST
http.setRequestMethod('POST')
You write your POST body to outputStream:
http.outputStream.write(body.getBytes("UTF-8"))
where body might be a JSON represented as string:
def body = '{"id": 120}'
Eventually it's good practice to check what HTTP status code returned: in case of e.g. HTTP 200 OK you will get your response from inputStream while in case of any error like 404, 500 etc. you will get your error response body from errorStream.

Sending auth cookie using WS and ScalaTest

I have a web app which uses a form to login, this returns a session cookie to the user which is used to authorize requests to the rest of the app. I'm having trouble sending this cookie value with my requests. My test harness is below:
val loginResponse = await(WS.url(s"http://localhost:$port/authenticate")
.withHeaders("Content-Type" -> "application/x-www-form-urlencoded")
.post(Map("email" -> Seq("admin#example.com"), "password" -> Seq("genivirocks!"))))
loginResponse.status mustBe (OK)
val cookies = loginResponse.cookies(0).toString
val vehiclesResponse = await(WS.url(s"http://localhost:$port/api/v1/vehicles/" + testVin)
.withHeaders("Cookie" -> cookies)
.put(""))
vehiclesResponse.status mustBe (OK)
val vehiclesFilterResponse = await(WS.url(s"http://localhost:$port/api/v1/vehicles?regex=" + testVin)
.withHeaders("Cookie" -> cookies)
.get())
vehiclesFilterResponse.status mustBe (OK)
The request fails, as the second request gets a 204 instead of a 200, as it gets redirected to the login page because the cookie is interpreted as invalid. The web server gives the following error, when the second request is made:
2015-10-06 14:56:15,991
[sota-core-service-akka.actor.default-dispatcher-42] WARN
akka.actor.ActorSystemImpl - Illegal request header: Illegal 'cookie'
header: Invalid input 'EOI', expected tchar, '\r', WSP or '=' (line 1,
column 178):
PLAY2AUTH_SESS_ID=ee84a2d5a0a422a3e5446f82f9f3c6f8eda9db1cr~jz7ei0asg0hk.ebd8j.h4cpjj~~9c0(yxt8p*jqvgf)_t1.5b(7i~tly21(*id;
path=/; expires=1444139775000; maxAge=3600s; HTTPOnly
I've tried building the cookie string myself and making sure there are no extra '\r' characters at the end and so on, with no luck. Google also doesn't seem to have any hints. Is there a better way of sending cookie values using WS?
EDIT
Got it working with the following code:
import play.api.mvc.Cookies
val loginResponse = ...
loginResponse.status mustBe (OK)
val cookies = loginResponse.cookies
val cookie = Cookies.decodeCookieHeader(loginResponse.cookies(0).toString)
val vehiclesResponse = await(WS.url(s"http://localhost:$port/api/v1/vehicles/" + testVin)
.withHeaders("Cookie" -> Cookies.encodeCookieHeader(cookie))
.put(""))
vehiclesResponse.status mustBe (OK)
...
Why don't you use the existing Cookies.encode function to do the cookie encoding for you?
import play.api.mvc.Cookies
val loginResponse = ...
loginResponse.status mustBe (OK)
val cookies = loginResponse.cookies
val vehiclesResponse = await(WS.url(s"http://localhost:$port/api/v1/vehicles/" + testVin)
.withHeaders("Cookie" -> Cookies.encode(cookies))
.put(""))
vehiclesResponse.status mustBe (OK)
...