Collect errors with Gatling? - gatling

In my long, but simple awesome Gatling simulation, I have few responses that ended with error 500. Is it possible to tell gatling to collect these error responses messages in a file during the simulation?

No in production mode. You only have them when debug logging is enabled.

It is possible to collect what ever you want and save it into simulation.log file. Use extraInfoExtractor method when you define protocol:
val httpProtocol = http
.baseURL(url)
.check(status.is(successStatus))
.extraInfoExtractor { extraInfo => List(getExtraInfo(extraInfo)) }
Then define in your getExtraInfo(extraInfo: ExtraInfo) method whatever criteria you want. Example bellow outputs request and response in case of debug is enables via Java System Property OR response code is not 200 OR status of request is KO (it can be KO if you have setup some max time and this max time gets increased)
private val successStatus: Int = 200
private val isDebug = System.getProperty("debug").toBoolean
private def getExtraInfo(extraInfo: ExtraInfo): String = {
if (isDebug
|| extraInfo.response.statusCode.get != successStatus
|| extraInfo.status.eq(Status.valueOf("KO"))) {
",URL:" + extraInfo.request.getUrl +
" Request: " + extraInfo.request.getStringData +
" Response: " + extraInfo.response.body.string
} else {
""
}
}

Related

Gatling/Scala - how to substring value from html response and use it in the next request?

I need to get CSRF token from this example page element
data-address-editor-options="{"changeBilling":true,"csrfToken":"07566cd8c8ef33a86b45b03e5f0a.2zBP36-ojP7OWTw5nvRINKjzldwKLzAQbkyA5grriJQ.iEQHsOzJ9La7Gl177IV6cveZ3o5wQHRcLwPMrnCR66CYRyeK5uX-iog9eQ"}"
and then in the next request in json body this token need to be used
def createBody(csrfToken: String, changeBilling: Boolean, changeShipping: Boolean): String = {
"""
|{
|"id": false,
|"_csrf_token": """ + csrfToken +
""",
|"changeableAddresses":
|{
|"changeBilling": """ + changeBilling +
""",
|"changeShipping": """ + changeShipping +
"""
|}
|}
""".stripMargin
}
So the question is how to substring csrf token from 'string' value html response web element
data-address-editor-options
and then used it as a parameter to the function which create json body of the next POST request?
Gatling version 3.4.2
Please let me know if more information is required to answer.
First, Gatling 3.4.2 is really outdated.
As of now, the latest version is 3.7.4 and it also supports Java and Kotlin for writing your tests.
Considering the Scala errors in your code (no offense meant), you're not a Scala developer, so one of those other options would definitely be a better choice for you.
I need to get CSRF token from this example page element
Not sure how to understand it. Have you already gone through a first extraction to get this, or is it your full payload?
Anyway, the best solution for you is probably to use a regex check.
And then, for crafting the body of your next request (assuming Java 17):
private static String createBody(String csrfToken, boolean changeBilling, boolean changeShipping) {
return "{\"id\": false, \"_csrf_token\": \"" + csrfToken + "\", \"changeableAddresses\": { \"changeBilling\": " + changeBilling + "\", \"changeShipping\": " + changeShipping + "}}";
}
StringBody(session -> createBody(session.getString("token"), true, true))
You should do something like this:
/** getting token */
val getToken: HttpRequestBuilder = http("getToken")
.post(myHost + "/auth/main/token")
.header("Content-Type", "application/x-www-form-urlencoded")
.formParam("username", "${username}")
.formParam("password", "${password}")
.formParam("client", "partners")
.check(status.is(200))
.check(regex("\"access_token\":\"(.+?)\"").saveAs("token"))
/** sending token */
val getApplications: HttpRequestBuilder = http("getApplications")
.get(myHost + "/api/partners/applications")
.header("Authorization", "Bearer ${token}")
.check(status.is(200))

Gatling Test in Blazemeter creates ClassNotFoundException

I used the Taurus Gatling guide to create a simple performance test and uploaded the yaml and scala file to blazemeter. When i start the test in blazemeter there is no test result and the bzt.log contains a ClassNotFoundException.
The validator for the yaml says its fine and i can't find anything so I'm lost...
My blazemleter.yaml:
execution:
- executor: gatling
scenario: products
iterations: 15
concurrency: 3
ramp-up: 2
scenarios:
products:
script: productSimulation.scala
simulation: test.productSimulation
My productSimulation.scala is mostly copied from their documentation:
package test
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class productSimulation extends Simulation {
// parse load profile from Taurus
val t_iterations = Integer.getInteger("iterations", 100).toInt
val t_concurrency = Integer.getInteger("concurrency", 10).toInt
val t_rampUp = Integer.getInteger("ramp-up", 1).toInt
val t_holdFor = Integer.getInteger("hold-for", 60).toInt
val t_throughput = Integer.getInteger("throughput", 100).toInt
val httpConf = http.baseURL("https://mydomain/")
val header = Map(
"Content-Type" -> """application/x-www-form-urlencoded""")
val sessionHeaders = Map("Authorization" -> "Bearer ${access_token}",
"Content-Type" -> "application/json")
// 'forever' means each thread will execute scenario until
// duration limit is reached
val loopScenario = scenario("products").forever() {
// auth
exec(http("POST OAuth Req")
.post("https://oauth-provider")
.formParam("client_secret", "...")
.formParam("client_id", "...")
.formParam("grant_type", "client_credentials")
.headers(header)
.check(status.is(200))
.check(jsonPath("$.access_token").exists
.saveAs("access_token")))
// read products
.exec(http("products")
.get("/products")
.queryParam("limit", 200)
.headers(sessionHeaders))
}
val execution = loopScenario
.inject(rampUsers(concurrency) over rampUp) // during for gatling 3.x
.protocols(httpConf)
setUp(execution).maxDuration(rampUp + holdFor)
}
After learning that i can execute the scala file as a test directly if i click the file directly and not the yaml i got better exceptions.
Basicly i made two mistakes:
my variables are named t_concurrency, ... while the execution definition uses a different name. ups.
since gatling 3.x the keyword for the inject is during, so the correct code is: rampUsers(t_concurrency) during t_rampUp
Now everything works.

are Steps in gatling cached and only executed once?

I have a simulation with a step that allows me to publish to different endpoints.
class MySimulation extends Simulation {
// some init code
var testTitle = this.getClass.getSimpleName
val myscenario = scenario("Scn Description")
.exec(PublishMessageRandom(pConfigTest, testTitle + "-" + numProducers, numProducers))
if (testMode == "debug") {
setUp(
myscenario.inject(
atOnceUsers(1)
)
).protocols(httpConf)
} else if (testMode == "open") {
setUp(
myscenario.inject(
rampConcurrentUsers(concurrentUserMin) to (concurrentUserMax) during (durationInMinutes minutes),
)
).protocols(httpConf)
}
}
Now here is my PublishMessageRandom definition
def PublishMessageRandom(producerConfig : ProducerConfig, testTitle : String, numberOfProducers : Int ) = {
val jsonBody = producerConfig.asJson
val valuedJsonBody = Printer.noSpaces.copy(dropNullValues = true).print(jsonBody)
println(valuedJsonBody)
val nodes : Array[String] = endpoints.split(endpointDelimiter)
val rnd = scala.util.Random
val rndIndex = rnd.nextInt(numberOfProducers)
var endpoint = "http://" + nodes(rndIndex) + perfEndpoint
println("endpoint:" + endpoint)
exec(http(testTitle)
.post(endpoint)
.header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationJson)
.body(StringBody(valuedJsonBody))
.check(status.is(200))
.check(bodyString.saveAs("serverResponse"))
)
// the below is only useful in debug mode. Comment it out for longer tests
/*.exec { session =>
println("server_response: " + session("serverResponse").as[String])
println("endpoint:" + endpoint)
session */
}
}
as you can see it simply round-robin of endpoints. Unfortunately I see the above println("endpoint:" + endpoint) once and it looks like it picks one endpoint randomly and keeps hitting that instead of desired purpose of hitting endpoints randomly.
Can someone explain that behavior? Is Gatling caching the Step or and how do I go around that?
Quoting the official documentation:
Warning
Gatling DSL components are immutable ActionBuilder(s) that have to be
chained altogether and are only built once on startup. The results is
a workflow chain of Action(s). These builders don’t do anything by
themselves, they don’t trigger any side effect, they are just
definitions. As a result, creating such DSL components at runtime in
functions is completely meaningless.
I had to use feeder to solve the problem where the feeder takes the random endpoint.
// feeder is random endpoint as per number of producers
val endpointFeeder = GetEndpoints(numProducers).random
val myscenario = scenario("Vary number of producers hitting Kafka cluster")
.feed(endpointFeeder)
.exec(PublishMessageRandom(pConfigTest, testTitle + "-" + numProducers))
and Publish message random looks like this:
def PublishMessageRandom(producerConfig : ProducerConfig, testTitle : String ) = {
val jsonBody = producerConfig.asJson
val valuedJsonBody = Printer.noSpaces.copy(dropNullValues = true).print(jsonBody)
println(valuedJsonBody)
exec(http(testTitle)
.post("${endpoint}")
.header(HttpHeaderNames.ContentType, HttpHeaderValues.ApplicationJson)
.body(StringBody(valuedJsonBody))
.check(status.is(200))
.check(bodyString.saveAs("serverResponse"))
)
}
you see the line above .post("${endpoint}") will end up hitting the endpoint coming from the feeder.
The feeder function GetEndpoints is defined as follows
where we create an array of maps with one value each "endpoint" is the key.
def GetEndpoints(numberOfProducers : Int ) : Array[Map[String,String]] = {
val nodes : Array[String] = endpoints.split(endpointDelimiter)
var result : Array[Map[String,String]] = Array()
for( elt <- 1 to numberOfProducers ) {
var endpoint = "http://" + nodes(elt-1) + perfEndpoint
var m : Map[String, String] = Map()
m += ("endpoint" -> endpoint )
result = result :+ m
println("map:" + m)
}
result
}

Error in JSON.parse() (when called from API Gateway)

I'm working on AWS lambda + API Gateway, and I need to pass an array of numbers in the url (GET method) for a REST call. It seems a good way is to pass the numbers as string (comma separated) and then use JSON.parse for the conversion to an array of numbers.
Following is the AWS lambda code I'm using;
exports.handler = (event, context, callback) => {
var arr = JSON.parse('[' + event.numbers + ']');
console.log("array: " + arr);
// TODO implement
callback(null, 'Hello from Lambda');
};
I'm testing this function in AWS Lambda using this Input test event;
{
"numbers": "1,5"
}
And everything works as expected; no errors.
However, when I test it via API Gateway, and passing the numbers as string in the query, I get following error (observed via CloudWatch);
*19:19:02
START RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Version: $LATEST
19:19:02
2017-08-29T19:19:02.688Z eabab882-8cee-11e7-8e2f-79d3086e061f SyntaxError: Unexpected token u in JSON at position 1 at Object.parse (native) at exports.handler (/var/task/index.js:4:20)
19:19:02
END RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f
19:19:02
REPORT RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Duration: 215.25 ms Billed Duration: 300 ms Memory Size: 128 MB Max Memory Used: 18 MB
19:19:02
RequestId: eabab882-8cee-11e7-8e2f-79d3086e061f Process exited before completing request*
This is the request passed to lambda as shown in the log;
"body-json" : {},
"params" : {
"path" : {
}
,"querystring" : {
"numbers" : "1,6"
}
,"header" : {
}
},
"stage-variables" : {
},
I can't figure out what the problem is, since I'm passing same string in both cases.
I would appreciate any help.
Thanks
Gus
With this input json informed, you need to get it like this:
var arr = JSON.parse('[' + event.params.querystring.numbers + ']');
rather than:
var arr = JSON.parse('[' + event.numbers + ']');
Or make a body mapping template to stay the way you want:
{ "number": "$input.params('number')" }
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
I hope I have helped!

Gmail API .NET: Get full message

How do I get the full message and not just the metadata using gmail api?
I have a service account and I am able to retrieve a message but only in the metadata, raw and minimal formats. How do I retrieve the full message in the full format? The following code works fine
var request = service.Users.Messages.Get(userId, messageId);
request.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Metadata;
Message message = request.Execute();
However, when I omit the format (hence I use the default format which is FULL) or I change the format to UsersResource.MessagesResource.GetRequest.FormatEnum.Full
I get the error: Metadata scope doesn't allow format FULL
I have included the following scopes:
https://www.googleapis.com/auth/gmail.readonly,
https://www.googleapis.com/auth/gmail.metadata,
https://www.googleapis.com/auth/gmail.modify,
https://mail.google.com/
How do I get the full message?
I had to remove the scope for the metadata to be able to get the full message format.
The user from the SO post have the same error.
Try this out first.
Go to https://security.google.com/settings/security/permissions
Choose the app you are working with.
Click Remove > OK
Next time, just request exactly which permissions you need.
Another thing, try to use gmailMessage.payload.parts[0].body.dataand to decode it into readable text, do the following from the SO post:
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
System.out.println(StringUtils.newStringUtf8(Base64.decodeBase64(gmailMessage.payload.parts[0].body.data)));
You can also check this for further reference.
try something like this
public String getMessage(string user_id, string message_id)
{
Message temp =service.Users.Messages.Get(user_id,message_id).Execute();
var parts = temp.Payload.Parts;
string s = "";
foreach (var part in parts) {
byte[] data = FromBase64ForUrlString(part.Body.Data);
s += Encoding.UTF8.GetString(data);
}
return s
}
public static byte[] FromBase64ForUrlString(string base64ForUrlInput)
{
int padChars = (base64ForUrlInput.Length % 4) == 0 ? 0 : (4 - (base64ForUrlInput.Length % 4));
StringBuilder result = new StringBuilder(base64ForUrlInput, base64ForUrlInput.Length + padChars);
result.Append(String.Empty.PadRight(padChars, '='));
result.Replace('-', '+');
result.Replace('_', '/');
return Convert.FromBase64String(result.ToString());
}

Resources