Gatling doIf is always executed - kotlin

I'm trying to create a login scenario in Gatling but only if environment needs it. My problem is, the then block is being always executed.
val environment = object Local : Environment(
url = "http://localhost:8080/",
loginUrl = null,
needsLogin =false
)
fun login() = doIf { session -> return#doIf !environment.needsLogin && session.getString(TOKEN_KEY).isNullOrEmpty() }
.then(authenticationLogin())
private val loginBody = LoginBody(
System.getenv("CI_PERFORMANCE_TEST_EMAIL") ?: "email",
System.getenv("CI_PERFORMANCE_TEST_PASSWORD") ?: "pass"
)
fun authenticationLogin() = exec(
http("POST Authentication")
.post(environment.loginUrl!!)
.formParam(EMAIL, loginBody.email)
.formParam(PASSWORD, loginBody.password)
.check(bodyString().saveAs("authResponse"))
)
)
I can't see what is wrong with my code.
I've tried with: Put logs I cant see them. Put everything as false. Return false...Is always executed.

This code doesn’t set TOKEN_KEY in the Session anywhere so the condition session.getString(TOKEN_KEY).isNullOrEmpty() is always false.

Related

WorkManger doesn't trigger after manually stopped- Kotlin

I want to use workMager to do some work every 15min,at the same time I want to stop workManger when I clicked on the button "StopThread" below is my Code:
val workManager = WorkManager.getInstance(applicationContext)
val workRequest = PeriodicWorkRequest.Builder(
RandomNumberGeneratorWorker::class.java,
15,
TimeUnit.MINUTES
).addTag("API_Worker")
.build()
binding.buttonThreadStarter.setOnClickListener {
workManager.enqueue(workRequest)
}
binding.buttonStopthread.setOnClickListener {
workManager.cancelAllWorkByTag("API_Worker")
}
And this is the RandomNumberGeneratorWorker
class RandomNumberGeneratorWorker(
context: Context,
params: WorkerParameters
) :
Worker(context, params) {
private val MIN = 0
private val MAX = 100
private var mRandomNumber = 0
override fun doWork(): Result {
Log.d("worker_info","Job Started")
startRandomNumberGenerator();
return Result.success();
}
override fun onStopped() {
super.onStopped()
Log.i("worker_info", "Worker has been cancelled")
}
private fun startRandomNumberGenerator() {
Log.d("worker_info","startRandomNumberGenerator triggered")
var i = 0
while (i < 100 && !isStopped) {
try {
Thread.sleep(1000)
mRandomNumber = (Math.random() * (MAX - MIN + 1)).toInt() + MIN
Log.i(
"worker_info",
"Thread id: " + Thread.currentThread().id + ", Random Number: " + mRandomNumber
)
i++
} catch (e: InterruptedException) {
Log.i("worker_info", "Thread Interrupted")
}
}
}
}
The issue that I'm facing is when I stopped the workManger it didn't work again when I clicked on buttonThreadStarter
I did a little research and I found that I can start-stop-start..etc workManger with the code below :
val workRequest = OneTimeWorkRequest.from(RandomNumberGeneratorWorker::class.java)
binding.buttonThreadStarter.setOnClickListener {
workManager.beginUniqueWork("WorkerName",ExistingWorkPolicy.REPLACE,workRequest)
}
binding.buttonStopthread.setOnClickListener {
workManager.cancelAllWork()
}
but as you can see it's working when I used OneTimeWorkRequest and with that, I can't repeat the work every 15 mins , Any suggestion in how to resolve this issue
WorkManager is not designed for periodic works with exact timing. In reality, the works "are not even periodic".
As you can see here from the logs:
https://developer.android.com/topic/libraries/architecture/workmanager/how-to/debugging#use-alb-shell0dumpsys-jobscheduler
WorkManager delegates to the JobScheduler. JS jobs work in a way that you have a number of explicit(you set them) and implicit(set by the system) constraints and after all of them are satisfied - the job starts.
When you have a period there is an extra constraint - TIMING_DELAY. So if your 15min pass - this doesn't mean in no way that the job will be executed. There might be, and be sure that there will be other constraints. That is the case because WM is designed for resource optimization and it will ensure that the work will finish at some point, even on device restart. But it is not designed to be exact. It is quite the opposite.
And after all the constraints are satisfied - it might take a day, the job is no longer needed and a new job is created with again your 15min constraint - TIMING_DELAY. And the process starts again.
Also - when you say "doesn't trigger" - please, check why. Try to check the debug output from the JS and see if there is work at all. If there is - check what constraints are not satisfied.
But long story short - "every 15min" is not something for WorkManager. Normally you should use AlaramManager for exact timing, but with such a short interval you should try to consider using a Service.
Also, it is dangerous to call: cancelAllWork(). You might break the code of some library in your app. You should better use tags and cancel by tag.

Gatling feeder/parameter issue - Exception in thread "main" java.lang.UnsupportedOperationException

I just involved the new project for API test for our service by using Gatling. At this point, I want to search query, below is the code:
def chnSendToRender(testData: FeederBuilderBase[String]): ChainBuilder = {
feed(testData)
exec(api.AdvanceSearch.searchAsset(s"{\"all\":[{\"all:aggregate:text\":{\"contains\":\"#{edlAssetId}_Rendered\"}}]}", "#{authToken}")
.check(status.is(200).saveAs("searchStatus"))
.check(jsonPath("$..asset:id").findAll.optional.saveAs("renderedAssetList"))
)
.doIf(session => session("searchStatus").as[Int] == 200) {
exec { session =>
printConsoleLog("Rendered Asset ID List: " + session("renderedAssetList").as[String], "INFO")
session
}
}
}
I declared the feeder already in the simulation scala file:
class GVRERenderEditor_new extends Simulation {
private val edlToRender = csv("data/render/edl_asset_ids.csv").queue
private val chnPostRender = components.notifications.notice.JobsPolling_new.chnSendToRender(edlToRender)
private val scnSendEDLForRender = scenario("Search Post Render")
.exitBlockOnFail(exec(preSimAuth))
.exec(chnPostRender)
setUp(
scnSendEDLForRender.inject(atOnceUsers(1)).protocols(httpProtocol)
)
.maxDuration(sessionDuration.seconds)
.assertions(global.successfulRequests.percent.is(100))
}
But Gatling test failed to run, showing this error: Exception in thread "main" java.lang.UnsupportedOperationException: There were no requests sent during the simulation, reports won't be generated
If I hardcode the #{edlAssetId} (put the real edlAssetId in that query), I will get result. I think I passed the parameter wrongly in this case. I've tried to print the output in console log but no luck. What's wrong with this code? I would appreciate your help. Thanks!
feed(testData)
exec(api.AdvanceSearch.searchAsset(s"{\"all\":[{\"all:aggregate:text\":{\"contains\":\"#{edlAssetId}_Rendered\"}}]}", "#{authToken}")
.check(status.is(200).saveAs("searchStatus"))
.check(jsonPath("$..asset:id").findAll.optional.saveAs("renderedAssetList"))
)
You're missing a . (dot) before the exec to attach it to the feed.
As a result, your method is returning the last instruction, ie the exec only.

How can I pass a value from a TeamCity failure Condition to an e-mail notification?

I want to show the status of the build in an e-mail with just a simple FAIL or PASS text appear in the body. There does not seem to be any kind of predefined "buildStatus" variable that I can access or setup in TeamCity. I guess I need to access the "failureConditions" function at bottom but not sure how, tried lots of things but nothing worked, this is my script:
package _Self.buildTypes
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.BuildFailureOnText
import jetbrains.buildServer.configs.kotlin.v2019_2.failureConditions.failOnText
import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.schedule
allowExternalStatus = true
params {
param("MinorVersion", "0")
param("RevisionVersion", "0")
}
powerShell {
name = "Email"
scriptMode = script {
content = """
function Send-ToEmail([string]${'$'}email, [string]${'$'}attachmentpath){
${'$'}message = new-object Net.Mail.MailMessage;
${'$'}message.From = "teamcity#blog.com";
${'$'}message.To.Add(${'$'}email);
${'$'}message.Subject = "%env.TEAMCITY_PROJECT_NAME% | %VersionNumber% ";
${'$'}message.Body = "The build: PASS or FAIL text here";
${'$'}smtp = new-object Net.Mail.SmtpClient("blog.local", "25");
${'$'}smtp.EnableSSL = ${'$'}true;
${'$'}smtp.send(${'$'}message);
write-host "Mail Sent" ;
}
Send-ToEmail -email "me#blog.com" -attachmentpath ${'$'}path;
""".trimIndent()
}
}
}
failureConditions {
failOnText {
conditionType = BuildFailureOnText.ConditionType.CONTAINS
pattern = "FAIL"
reverse = false
}
}
To solve this in T/C:
Create new custom env. var. e.g. MyBuildStatus
Create new build step e.g. Set build status (only executes if build successful)
Create a new Parameter name='env.MyBuildStatus.Status' value='SUCCESS'
Add variable to email subject: $message.Subject = "%env.MyBuildStatus.Status%
Why T/C has no build in "buildstatus" env. variable is interesting.

If then else implementation to chose baseURL in Karate DSL

I have a little tricky requirement in Karate. I have a set of baseURL's in my karate.config which are chosen based on the implementation. Here is the snippet of it:
if (env == 'qa') {
config.apiKey = apiKey;
config.tsp_api = 'https://api.qa.tceu.net';
config.svt_dcm = 'https://svt.qa.tceu.net';
config.acn_dcm = 'https://acn.qa.tceu.net';
config.sos_dcm = 'https://sos.qa.tceu.net';
config.cust_dcm = 'https://cust.qa.tceu.net';
Here tsp,svt,acn,sos,cust are some actions.
I have a feature file which passes the action as a parameter:
# Vehicle Initiates the action
When def Perform_Report_Notification = call read('./../common/performActionNotification.feature') { action: '#(action)' }
In the called performActionNotification.feature, I need to pick up the url from the karate.config file based on the action that is passed. For example if the action is sos, then the url should be sos_dcm. If the action is svt then the url should be svt_dcm
Here is the snippet from performActionNotification.feature and what I am currently doing for sos:
Given url sos_dcm
And path '/AU/v1.0/TSP/'+ action
And request RequestPayload
When method post
Then status 200
I want to implement something like an if then else similar to:
if (action == 'sos')
then myurl == 'sos_dcm'
else if (action == 'acn')
then myurl == 'acn_dcm'
else if (action == 'svt')
then myurl == 'svt_dcm'
Given url myurl
And...
And...
...
I tried a sort of a hack and it works but its not a clean way of doing it. Instead of reading the URL from karate.config I am hardcoding it this way:
Given url 'https://'+act+'.qa.tceu.net'
One more thing I tried was
* def myurl = action +'_dcm' #so if action is acn then the variable myurl would be acn_dcm
Given url myurl
...
....
But this hardcodes the url as 'acn_dcm' instead of picking the defined url up from karate.config.
Can someone kindly suggest the best way to implement this?
Here is a hint. JSON is actually a pretty useful data-structure (think hash-map or dictionary) and you can lookup a value without needing an if statement.
* def data =
"""
{
qa: {
sos: 'https://sos.qa.tceu.net',
acn: 'https://acn.qa.tceu.net'
}
}
"""
* def env = 'qa'
* def urls = data[env]
* def action = 'sos'
* def actionUrl = urls[action]
* match actionUrl == 'https://sos.qa.tceu.net'
This should get you on your way :)
EDIT - also see this: https://stackoverflow.com/a/67868935/143475
I've finally used Peter's most elegant solution and it works like a charm!
Here's what I've finally implemented that does not need hardcoding of the endpoint URL's and is driven by endpoints in the karate.config file.
* def data =
"""
{
qa: {
sos: '#(sos_dcm)', # sos_dcm endpoint defined in karate.config file
acn: '#(acn_dcm)',
svt: '#(svt_dcm)'
}
}
"""
* def env = karate.properties['env']; # Driven by maven commandline arg -Denv=qa as an example
* def urls = data[env]
* def action = act # act comes from the calling feature file and has values - sos/acn/svt
* def myUrl = urls[action]
Given url myUrl
...
...
I would suggest looking into using javascript for your conditional logic
So the javascript function takes a param of action and then the if and else statements returns the variable of the url that you need.
Perform the javascript function before you make the request call. and use the variable that is returned by js to determine the logic.
make that js file a common function that can be accessed by multiple feature files.
function determineUrl(action) {
var url = "${urDefaultUrl}";
if (action == "sos") url == "${full url}";
else if (action == "acn") url == "${full url}";
return url;
}
Then in your feature file
* def urlDecider = 'classpath to your js function'
* myUrl = urlDecider(action)
* url myUrl
* Given path ....
For the sake of the community learning, there is one other way I figured out on similar lines of Jawad's solution is using a Java function. They do exactly the same thing as Jawad's solution but just that its a java class doing it. If a project has java class files, then to maintain consistency this solution can be used too.
Here is the how the class file looks:
public class DCMUrlDecider {
static String dcmURL="";
public static String getDCMUrl(String action) {
if (action.matches("sos"))
{
dcmURL = "https://sos.qa.tceu.net";
}
else if (action.matches("acn"))
{
dcmURL = "https://acn.qa.tceu.net";
}
else if (action.matches("svt"))
{
dcmURL = "https://svt.qa.tceu.net";
}
return dcmURL;
}
}
And here is the associated code snippet from the feature file:
* def dcmURLDecider = Java.type('com.TCEU.KarateTests.DCMUrlDecider')
* def myUrl = dcmURLDecider.getDCMUrl(act)
Given url myUrl
....
.....
Once again this means we are still hardcoding URLs in the java class files. Need to learn a way of doing it via karate.config files if possible.

Define a variable and set it to a default value if something goes wrong during definition

I have the following code in my build.gradle
Contents in version.properties are:
buildVersion=1.2.3
Value of $v variable during the Gradle build is coming as: 1.2.3
Value of $artifactoryVersion variable in JENKINS build is coming as: 1.2.3.1, 1.2.3.2, 1.2.3.x ... and so on where the 4th digit is Jenkins BUILD_NUMBER available to gradle build script during Jenkins build.
BUT, when I'm running this build.gradle on my desktop where I dont have BUILD_NUMBER variable available or set in my ENVIRONMENT variables, I get an error saying trim() can't work on null. (as there's no BUILD_NUMBER for Desktop/local build).
I'm trying to find a way i.e.
What should I code in my script so that if BUILD_NUMBER is not available, then instead of gradle build processing failing for an error, it'd set jenkinsBuild = "0" (hard coded) otherwise, pick what it gets during Jenkins build.
For ex: in Bash, we set a variable var1=${BUILD_NUMBER:-"0"} which will set var1 to a valid Jenkins BUILD number if it's available and set to a value, otherwise if it's NULL, then var1 = "0".
I DON'T want to have each developer/user set this BUILD_NUMBER in some property file. All I want is, if this variable doesn't exist, then the code should put "0" in jenkinsBuilds variable and doesn't error out during desktop builds. I know during Jenkins build, it's working fine.
// Build Script
def fname = new File( 'version.properties' )
Properties props = new Properties()
props.load( new FileInputStream( fname ) )
def v = props.get( 'buildVersion' )
def env = System.getenv()
def jenkinsBuild = env['BUILD_NUMBER'].trim()
if( jenkinsBuild.length() > 0 ) {
artifactoryVersion = "$v.$jenkinsBuild"
}
All you need is some regular Java/Groovy code:
def jenkinsBuild = System.getenv("BUILD_NUMBER") ?: "0"
The code above uses Groovy's "elvis" operator, and is a shorthand for the following code, which uses Java's ternary operator:
def buildNumber = System.getenv("BUILD_NUMBER")
def jenkinsBuild = buildNumber != null ? buildNumber : "0"
Here's the answer to using a Java plain object (JDK8):
public class Sample {
private String region;
private String fruit;
public Sample() {
region = System.getenv().getOrDefault("REGION", null);
fruit = System.getenv().getOrDefault("FRUIT", "apple");
}
}
With the Env-Inject plugin you can get and set build parameters.
For example, under "Inject environment variables to the build process", add a Groovy script such as:
def paramsMap = [:]
def build = Thread.currentThread().executable
def my_var = build.getEnvVars()["MY_PARAM"]
if (!my_var) paramsMap.put("MY_PARAM", "default value")
// Return parameters map
out.println("Injecting parameters:\n" + paramsMap)
return paramsMap