Pass optional request query param in gatling - gatling

I am using Gatling 3.6.1.
I would like to attach gatling param to the request in order to force getting the value NOT from the cache. I would like it to be configurable, in a way to attach it when I want to force thing not to use cache and in other times I would like to use cache so I wouldn't attach the param or at least the unique value to it.
This is what I tried:
http("ProductReferencesPDP")
.get(path + "products/${product_code}/references?currentPage=1&pageSize=25&lang=de").queryParam("gatling", session => System.currentTimeMillis().toString)
.check(jsonPath("$..numberOfPages").exists)
And it attaches the gatling param always. But how can I make it optional, meaning someting like this:
http("ProductReferencesPDP")
.get(path + "products/${product_code}/references?currentPage=1&pageSize=25&lang=de").queryParam("gatling", if withCache "" else session => System.currentTimeMillis().toString)
.check(jsonPath("$..numberOfPages").exists)
But it doesn't work like this. I cannot access the session in the if clause or in u custom function for that matter.
Thanks!

.queryParam("gatling", if withCache "" else session => System.currentTimeMillis().toString)
Your if statement returns either a String or a Function. This can't possibly work. Try
.queryParam("gatling", session => if (withCache) "" else System.currentTimeMillis().toString)

Related

Configuring additional properties within SnowflakeBasicDataSource

I'm using following class in Java. But this class has limited implementation of properties.
SnowflakeBasicDataSource basicDataSource = new SnowflakeBasicDataSource();
basicDataSource.setSsl(true);
basicDataSource.setUser(dbSnowFlakeUsername);
basicDataSource.setPassword(dbSnowFlakePassword);
basicDataSource.setUrl(dbSnowFlakeUrl);
basicDataSource.setLoginTimeout(dbSnowFlakeLoginTimeoutSeconds);
For example i want to indicate networkTimeout and queryTimeout. But this is not implemented
How do i pass it to SnowflakeBasicDataSource?
Tried to pass withing url like this
jdbc:snowflake://ni31094.eu-central-1.snowflakecomputing.com/?db=TEST_DB&schema=PUBLIC&role=SYSADMIN&warehouse=TEST_WAREHOUSE&tracing=FINE&networkTimeout=10&queryTimeout=10
But don't think it works.
Please need assistance.
The two parameters for Network and Query Timeout are:
Network Timeout: networkTimeout
Query Timeout: queryTimeout=<number>
Looking at your string: Yes, you are using the two parameters correctly but I see your user and password-parameters are missing.
You can also try to:
ensure your properties are set correctly
adjust your connection string and accountinformation-values
You can find some more detail information about troubleshooting here: https://docs.snowflake.com/en/user-guide/jdbc-configure.html#troubleshooting-tips
Setting the properties in the URL for the SnowflakeBasicDataSource does work. I tested the queryTimeout with a long-running query and the query correctly get cancelled after the specified time in the parameter.
Testing networkTimeout is a little more difficult since it's hard to tell how it is actually used. It is used by net.snowflake.client.jdbc.RestRequest and I've tested that the correct parameter gets passed through, and it does. The reason you're getting a timeout of 60 seconds is that the HTTP request for the initial login request gets set to that by default. The initial login request seems to ignore the networkTimeout. The request which contains the query to run gets correctly set to the networkTimeout parameter passed in through the query string. Since my java skills aren't great I was unable to test a situation where the networkTimeout causes an error, unfortunately.
Here is some scala code which shows you that the two params get correctly set in the session:
import net.snowflake.client.jdbc.{SnowflakeBasicDataSource, SnowflakeConnectionV1}
import java.sql.Statement
import java.io.FileReader
import java.util.Properties
object BasicConnector extends App{
val prop = new Properties
prop.load(new FileReader("~/snowflake_conn.properties"))
val username = prop.getProperty("username")
val password = prop.getProperty("password")
val url = prop.getProperty("url") + "?networkTimeout=54321&queryTimeout=1234"
val basicDataSource = new SnowflakeBasicDataSource()
basicDataSource.setSsl(true)
basicDataSource.setUser(username)
basicDataSource.setPassword(password)
basicDataSource.setUrl(url)
basicDataSource.setWarehouse("DEMO_WH")
val conn: SnowflakeConnectionV1 = basicDataSource.getConnection().asInstanceOf[SnowflakeConnectionV1]
val statement: Statement = conn.createStatement()
val queryTimeout = conn.getSfSession.getQueryTimeout
val networkTimeout = conn.getSfSession.getNetworkTimeoutInMilli
println(s"query timeout: $queryTimeout")
println(s"network timeout: $networkTimeout")
statement.close()
conn.close()
The above prints out:
query timeout: 1234
network timeout: 54321
As you can see, I had to cast the Connection object to a SnowflakeConnectionV1 and use the getSfSession method to inspect the params with .asInstanceOf[SnowflakeConnectionV1]. This is because the JDBC Connection type doesn't have this method. You shouldn't have to do this though if you don't care about inspecting the parameter, it'll still use them correctly.

How to use a CookieCollection in multiple functions

I'm setting up a web page using cookies to determine if the user already logged in, using a cookie containing his id. Problem is : The cookie is either not written or the cookie collection is not updated.
I've tried reading the documentation, but it does not define the usage of CookieCollection.
Here's the function where i write my cookie :
function displayData(){
$id = $this->getRequest()->getSession()->read('id');
$cookies = CookieCollection::createFromServerRequest($this->getRequest());
if(!$cookies->has('id')){
$cookie = (new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true);
$cookies = $cookies->add($cookie);
}
// Other stuff
}
And where I try reading it :
function index(){
$cookies = $this->getRequest()->getCookieCollection();
dd($cookies);
}
I expect having a cookie named "id", but I don't have it. Only CAKEPHP and pll_language are showing up.
First things first, CakePHP provides authentication functionality with cookie authentication, you may want to have a look at that instead of driving a custom solution.
Cookbook > Plugins > Authentication
That being said, what you're doing there will create a cookie collection object, which however is just that, a lone object somewhere in space, it won't affect the state of your application, in order for that to happen you have to actually modify the response object.
However what you're trying to do there doesn't require cookie collections in the first place, you can simply read and write cookies directly via the methods provided by the request and response objects, like:
// will be `null` in case the cookie doesn't exist
$cookie = $this->getRequest()->getCookie('id');
// responses are immutable, they need to be reassinged
this->setResponse(
$this->getResponse()->withCookie(
(new Cookie('id'))
->withValue($id)
->withExpiry(new DateTime('+999 year'))
->withPath('/')
->withDomain('break-first.eu')
->withSecure(true)
->withHttpOnly(true)
)
);
And if you where to use a cookie collection for whatever reason, then you'd use withCookieCollection() to pass it into the response:
$this->setResponse($this->getResponse()->withCookieCollection($cookies));
If you run into strict typing errors, you could for example create a custom reponse class with an overridden Response::convertCookieToArray() method and cast the string to an integer there (make sure that PHP_INT_MAX covers your target date timestamp, 32-Bit incompatibility is why the fix that landed in CakePHP 4.x, probably won't come to 3.x), something like:
src/Http/Response.php
namespace App\Http;
use Cake\Http\Cookie\CookieInterface;
use Cake\Http\Response as CakeResponse;
class Response extends CakeResponse
{
protected function convertCookieToArray(CookieInterface $cookie)
{
$data = parent::convertCookieToArray($cookie);
$data['expire'] = (int)$data['expire'];
return $data;
}
}
You can pass that into the app in your webroot/index.php file, as the second argument of the $server->run() call:
// ...
$server->emit($server->run(null, new \App\Http\Response()));
See also
Cookbook > Request & Response Objects > Request > Cookies
Cookbook > Request & Response Objects > Response > Setting Cookies

accessing session variable inside a response transformer in gatling

I have a basic question for which I don't seem to be able to find the answer. How can I access session variables inside a response transformer? Something like:
.transformResponse {
case response if response.isReceived =>
new ResponseWrapper(response) {
session??
override val body = new ByteArrayResponseBody("", UTF_8)
}
};
There is no Session object in context of that partial function, so you can't access it. What do you want to achieve? Maybe check(bodyString.transform()) would be better solution as it can take (String, Session) => T as parameter so you can access session attributes in that function.

How to use tryMax in Gatling for a blank attribute, logout and restart the iteration?

I know I can use tryMax to restart a scenario and use .exists() to validate if an attribute exists. How do I incorporate the two together?
If you have checks in an exec inside the tryMax and that check fails, it will try it again from the beginning of the tryMax block.
EX:
val TestScenarioThatRestartsOnFail: ScenarioBuilder = scenario("ScenarioNameHere")
.tryMax(2) {
exec(
http("zzzz")
.get("zzz")
.check(xxxx)
)
.exec(
http("zzzz")
.get("zzz")
.check(xxxx)
)
}
So basically any check that fails in the entire tryMax{} block, will make the entire block run again.
You can also chain multiple tryMax blocks if you need to retry different number of times different requests
val TestScenarioThatRestartsOnFail: ScenarioBuilder = scenario("ScenarioNameHere")
.tryMax(2) {
exec (
http ("zzzz")
.get ("zzz")
.check (xxxx)
)
}.tryMax(2) {
exec(
http("zzzz")
.get("zzz")
.check(xxxx)
)
}
If you encounter checks that are "optional" hence they might sometimes fail, but you don't want to trigger the tryMax for those checks, use the optional method like :
.check(regex("/admin-ng/login.[^\"]*.css").find.optional.saveAs("login_css"))
This will not fail the check if it is not fulfilled, in this example, the element we search for is not in the response.

Using userId with Gatling DSL

I am using session.userId to parametrize my test requests like this:
exec(http("get request")
.get((s:Session) => "somebaseUrl/" + s.userId.toString ))
Is it possible to get sesion userId differently, so my request addresses are more DRY, eg I won't have to use lambda?
I tried using gatling DSL:
exec(http("get request")
.get("somebaseUrl/${userId}")
but then I get: No attribute named 'userId' is defined error
I could just calculate some random id to my tests, but why when gatling already does this.
Only solution that I came up with is saving userId to some different attribute like this:
exec((s:Session) => s.set("user_id",s.userId)),
exec(http("test").get("${user_id}"))
but that doesn't seem right, and you have to make sure to call the set before all other requests.
The userId is some internal details. Gatling Expression language only exposes Session attributes, ie the attributes Map content.
Either use the first way you described, with a function, or don't use Gatling internals and set your own UUID attribute (feeder, exec(function), etc).
To avoid using gatling internals you could use a feeder:
var userId = 0
feed(Iterator.continually(Map("userId" -> {
userId += 1
userId}.toString)))

Resources