End Gatling simulation when scenario fails BUT generate a report - gatling

I have code which currently will not run my scenario if it fails;
//Defined outside of the scenario scope
var simulationHealthy = true
//defined within the scenario
.exec((session: io.gatling.core.session.Session) => {
if (session.status == KO) {
simulationHealthy = false
}
session
})
However my simulation keeps running until the duration set for the simulation is over, though the scenario will not keep executing.
What I would like to do is to have a scenario fail under conditions I define (similar to assertions) and for the entire simulation to fail at that point as well, and also generate a report.
Thanks
Edit: I am running these tests within the IntelliJ IDE. Ending the simulation programmatically is required.

You might run the test itself without report and produce the report with a second call for just the report generation from the simulation.log
Run Simulation w/o report (-nr flag), i.e.
gatling.sh -nr -s YourSimulationClass
Generate Report (-ro flag):
gatling.sh -ro yoursimulation
(your simultation is the path underneath the results folder, which can be specified with -rf, which contains the simulation.log file)
In IntelliJ you can define another LaunchConfiguration to be executed before. So you define an action for executing Gatling Test (with -nr flag) and another configuration for report generation (with -ro flag), that executes the Gatling Test run action before.
Alternatively you could use the gatling-maven-plugin and define two executions (run, report) with the same flags.
Edit
According to this group thread you could execute your steps conditionally or mute them. The condition could be the presence of an error, but anything else as well. If the condition depends on global state i.e. a global variable, it would mute all users (unlike exitHereIfFailed)
For example:
val continue = new AtomicBoolean(true)
val scn = scenario("MyTest")
.exec(
doIf(session => continue.get) {
exec(http("request_0").get("/home").check(status.is(200)))
.exec((session: io.gatling.core.session.Session) => {
if (session.status == KO) {
continue.set(false)
}
session
})
})
As said, this only stops sending requests to the SUT. Seems there is no other option at the moment (apart from System.exit(0))

You can use exitHereIfFailed in ScenarioBuilder returned by exec().
.exec(http("login")
.post("/serviceapp/api/auth/login")
...
.check(status.is(200))))
.exitHereIfFailed
.pause(1)
.exec(http("getProfileDetails")
.get("/serviceapp/api/user/get_profile")
.headers(authHeader("${token}"))
.check(status.is(200)))

Thanks to #GeraldMücke 's suggestion of using system.exit I've come up with a work around. Still no where close to ideal but it does the job.
The problems are
Still have to manually generate the report from the log that is created when gatling is run
The user has to constantly manage how long the scenario lasts for both items as I don't know a way to have a scenario last the length of a simulation
This is obviously a "proof of concept" it has nothing in the code to define failure over thresholds etc like the asserts and checks available in Gatling itself
Here's the code. I've nested simulations within the setUp function because it fits the criteria of the work I am doing currently, allowing me to run multiple simulations within the main simulation.
FailOverSimulation and ScenarioFailOver are the classes that need to be added to the list; obviously this only adds value when you are running something that loops within the setUp.
import java.util.concurrent.atomic.AtomicBoolean
import io.gatling.commons.stats.KO
import io.gatling.core.Predef._
import io.gatling.core.scenario.Simulation
import io.gatling.http.Predef._
import scala.concurrent.duration._
object ScenarioTest {
val get = scenario("Test Scenario")
.exec(http("Test Scenario")
.get("https://.co.uk/")
)
.exec((session: io.gatling.core.session.Session) => {
if(session.status == KO) {
ScenarioFailOver.exitFlag.set(true)
}
session
})
}
object TestSimulation {
val fullScenario = List(
ScenarioTest.get.inject(constantUsersPerSec(1).during(10.seconds))
)
}
object ScenarioFailOver {
var exitFlag = new AtomicBoolean(false)
val get = scenario("Fail Over")
.doIf(session => exitFlag.get()) {
exec(s => {
java.lang.System.exit(0)
s
})
}
}
object FailOverSimulation {
val fullScenario = List(
ScenarioFailOver.get.inject(constantUsersPerSec(1).during(10.seconds))
)
}
class SimulateTestEnding extends Simulation {
setUp(
FailOverSimulation.fullScenario
::: TestSimulation.fullScenario
).protocols(
)
}

Related

SalesForce query returns results in Query Editor, but returns null from APEX code in Lightning component

I'm completely new to SalesForce and have inherited a report that's not working. Please excuse any incorrect terminology, since I'm learning about all this as I go. The report has three prompts: states, years, and members. All dropdowns are supposed to populate with data returned from functions in an APEX class. State, which populates from a picklist, and years, which is populated with a loop, work fine. Members, which populates from a SQL query, returns nothing. If I run the report without any prompts selected (which should return an unfiltered list of results from a SQL query), it also returns nothing. Both of the SQL queries return data when I execute them directly in the query editor in the developer console, but they return nothing when called from the APEX functions.
Here's the initialization code from the Lightning controller:
doInit: function (component, event, helper) {
var action = component.get('c.getTrcAccounts');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS' && component.isValid()) {
component.set('v.trcAccList', response.getReturnValue());
}
helper.getLocationState(component, event);
helper.getYear(component, event);
});
$A.enqueueAction(action);
},
Here are the two helper functions referenced in that code:
getLocationState: function (component, event) {
var action = component.get('c.getLocationState');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS') {
component.set('v.LocationStateList', response.getReturnValue());
}
});
$A.enqueueAction(action);
},
getYear: function (component, event) {
var action = component.get('c.yearsOptions');
action.setCallback(this, function (response) {
var state = response.getState();
if (state === 'SUCCESS') {
component.set('v.LocationYearList', response.getReturnValue());
}
});
$A.enqueueAction(action);
}
Here is the code from the APEX class that returns the data for those three prompts:
Global class DataTableLocations {
#AuraEnabled
Global static List<TRC_Account__c> getTrcAccounts(){
set<string> trcAccountSet = new set<string>();
List<TRC_Account__c> traccList = new List<TRC_Account__c>();
for(TRC_Account__c trcacc : [SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000]){
if(!trcAccountSet.contains(trcacc.Name)){
trcAccountSet.add(trcacc.Name);
traccList.add(trcacc);
}
}
if(traccList.size()>0){
return traccList;
}
else{
return null;
}
}
#AuraEnabled
Global static List<string> getLocationState(){
List<string> options = new List<string>();
//options.add(new SelectOption('SelectAll', 'Select All'));
for( Schema.PicklistEntry f : Location__c.Physical_Address_State__c.getDescribe().getPicklistValues()) {
options.add(f.getValue());
}
return options;
}
#AuraEnabled
Global static List<string> yearsOptions() {
List<string> options = new List<string>();
date OldDate= date.today().addYears(-18);
integer oldyear=OldDate.year();
for( integer i=0; i<19 ;i++) {
options.add(string.valueOf(oldyear));
oldyear++;
}
return options;
}
}
If I run SELECT Id, Name from TRC_Account__c WHERE TRC_Member__c = True order by Name limit 50000 directly in the query editor window in the developer console, I get 7 results. However, if I output the response.getReturnValue() for getTrcAccounts in the doInit function, it's null.
Any help is greatly appreciated, as we're in a bit of a time crunch in conjunction with a site redesign. I'm told these reports were working at one point, but no one knows when they stopped working, and we inherited this code from a different company that did the original development. Thank you!
UPDATE:
In case it helps, this is the code in the lightning app that I think is used on the public page:
<aura:application extends="ltng:outApp" access="GLOBAL" implements="ltng:allowGuestAccess">
<aura:dependency resource="c:SearchBinReceiptsByYear"/>
</aura:application>
Edit
Right, it's a public page, it's called "Salesforce Sites". It's exposed to whole world without having to log in. These have special security in place because most of the time you don't want to expose data like that. At best you'd display contact us form, maybe some documents to download, product catalog... It's all very locked down, default is to ban everything and then admin decides what's allowed. It's bit unusual to have a Visualforce page + Aura component but ok, it happens.
You (and any other internal user) can see the results if you'd access this page from within salesforce. Something like https://mydomain.my.salesforce.com/apex/SearchBinReceiptsByYear and for you the page will work fine, "just" not outside of salesforce.
When exposed like that on the web - there's no logged in user. There's special "[Site Name] Guest User", you can see them if you search "Sites" in Setup. It has a special profile, also with [Site Name] in it. And nasty thing is - it doesn't show on the list of Users or Profiles.
Your code broke when Salesforce (auto)activated a critical update. Probably this one: https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_networks_secure_perms_guests.htm There are some good resources on the net if you Google "Secure Object Permissions for Guest Users", for example https://katiekodes.com/salesforce-spring-20-guest-user/
Ask your system administrator colleague or read up a bit about sharing rules.
You'll have to go to Setup -> Sharing Rules. There's a checkbox that caused your stuff to break and you can't untick it.
Scroll down to your TRC Account object and hit "New". You'll need to create something like this, but with your criteria (TRC Member equals true)
Save, wait a bit (it might take a while to recalculate the sharing, you'll get an email) and try the page.
If it still doesn't work you'll have to check the Guest user's profile, it might need permissions to Read TRC Accounts and their Name field.
If it's Salesforce Sites - try this to find it: https://help.salesforce.com/articleView?id=000334554&type=1&mode=1
If it's a Customer Portal, Community, Digital Experience (they renamed the product few times) - try with https://help.salesforce.com/articleView?id=sf.rss_config_guest_user_profile.htm&type=5
Original answer
It looks like it's running OK because accounts (members?) are fetched first and in that fetch's callback (what to do when data comes back from server) you have helper.getLocationState, helper.getYear. And you wrote that these populate OK. It's not the best performance code but it should get the job done.
In no specific order...
Does the whole thing work OK for sysadmins? Or is it broken for everybody? If it works for sysadmins it might be something to do with sharing, your sysadmin should know (Setup -> Sharing settings is where you control who can see what. Maybe "mortals" are not allowed to see any data? Typically sysadmins bypass it. As a quick & dirty test you can modify the class definition to global without sharing class DataTableLocations but it's a really ugly hack.
What happens if you open DeveloperConsole (upper right corner) while running this component, do you see any errors in the logs? What happens if in the console you go Debug -> Open ExecuteAnonymous and run this piece of code:
System.debug(DataTableLocations.getTrcAccounts());
Does it return something? Throw error?
You can go to Setup -> Debug Mode, tick the checkbox next to your user and save. This slows the system down a bit but lets you debug the javascript better. You can then sprinkle some debugger; or console.log statements in the source code and view what happens in your browser's console (Ctrl+Shift+J in Chrome, Ctrl+Shift+I in firefox). For example
action.setCallback(this, function (response) {
var state = response.getState();
debugger;
console.log(state);
console.log(component.isValid());
console.table(response.getReturnValue());
if (state === 'SUCCESS' && component.isValid()) {
component.set('v.trcAccList', response.getReturnValue());
}
console.log(component.get('v.trcAccList'));
debugger;
helper.getLocationState(component, event);
helper.getYear(component, event);
});
How's the trcAccList variable actually used in the "cmp" file, in the HTML-like file? Maybe it's being set all right and contains 7 records but it's not displayed right?

scala - Gatling - I can't seem to use Session Variables stored from a request in a subsequent Request

The code:
package simulations
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class StarWarsBasicExample extends Simulation
{
// 1 Http Conf
val httpConf = http.baseUrl("https://swapi.dev/api/films/")
// 2 Scenario Definition
val scn = scenario("Star Wars API")
.exec(http("Get Number")
.get("4")
.check(jsonPath("$.episode_id")
.saveAs("episodeId"))
)
.exec(session => {
val movie = session("episodeId").as[String]
session.set("episode",movie)
}).pause(4)
.exec(http("$episode")
.get("$episode"))
// 3 Load Scenario
setUp(
scn.inject(atOnceUsers(1)))
.protocols(httpConf)
}
Trying to grab a variable from the first Get request, and inject that variable into a second request, but unable to do so despite using the documentation. There might be something I'm not understanding.
When I use breakpoints, and navigate through the process, it appears the session execution happens AFTER both of the other requests have been completed (by which time is too late). Can't seem to make that session execution happen between the two requests.
Already answered on Gatling's community mailing list.
"$episode" is not correct Gatling Expression Language syntax. "${episode}" is correct.

Akka.net - Streams with parallelism, backpressure and ActorRef

Tying to learn how use Akka.net Streams to process items in parallel from a Source.Queue, with the processing done in an Actor.
I've been able to get it to work with calling a function with Sink.ForEachParallel, and it works as expected.
Is it possible to process items in parallel with Sink.ActorRefWithAck (as I would prefer it utilize back-pressure)?
About to press Post, when tried to combine previous attempts and viola!
Previous attempts with ForEachParallel failed when I tried to create the actor within, but couldn't do so in an async function. If I use an single actor previous declared, then the Tell would work, but I couldn't get the parallelism I desired.
I got it to work with a router with roundrobin configuration.
var props = new RoundRobinPool(5).Props(Props.Create<MyActor>());
var actor = Context.ActorOf(props);
flow = Source.Queue<Element>(2000,OverflowStrategy.Backpressure)
.Select(x => {
return new Wrapper() { Element = x, Request = ++cnt };
})
.To(Sink.ForEachParallel<Wrapper>(5, (s) => { actor.Tell(s); }))
.Run(materializer);
The Request ++cnt is for console output to verify the requests are being processed as desired.
MyActor has a long delay on every 10th request to verify the backpressure was working.

drive realtime api model toJson not populating fields in custom type

I'm building a Drive Realtime project using custom types: https://developers.google.com/google-apps/realtime/custom-objects.
I'm having an issue where the fields in my custom objects are not exported in the model.toJson() output. I'm sure I'm just missing something, but I haven't been able to find any differences with how I'm constructing the custom object vs. the realtime playground or the realtime API documentation.
Sample repro case using the realtime playground is below.
1) go to realtime playground: https://realtimeplayground.appspot.com/
2) open developer console
3) Run the following code
test = function () {}
test.prototype = { init: function() { this.name = 'testName';}};
test.prototype.name = gapi.drive.realtime.custom.collaborativeField('name');
gapi.drive.realtime.custom.registerType(test, 'testType')
gapi.drive.realtime.custom.setInitializer(test, test.prototype.init);
var model = window.doc.getModel()
model.getRoot().set('myTest', model.create(test));
model.toJson()
observed output:
"{"id":"root","type":"Map","value":
{"demo_string":
{"id":"Tq50c9iybcXi","type":"EditableString","value":"Edit Me!"},
"demo_list":{"id":"ZmjclOeUbcXj","type":"List","value":
[{"json":"Cat"},{"json":"Dog"},{"json":"Sheep"},{"json":"Chicken"}]},
"demo_cursors":{"id":"6TJ6Zzd2bcXj","type":"Map","value":{}},
"demo_map":{"id":"ukRRMPHbbcXj","type":"Map","value":
{"key1":{"json":"value 1"},"key2":{"json":"value 2"},"key3":{"json":"value 3"}}},
"demo_custom":{"id":"44nsuMAPbcXk","type":"DemoMovie","value":
{"name":{"json":"Minority Report"},
"director":{"json":"Steven Spielberg"},
"notes":{"json":""},"rating":{"json":""}}},
"myTest":{"id":"Kq4hcV4UbcvW","type":"testType","value":{}}}}"
Expected:
"{"id":"root","type":"Map","value":
{"demo_string":
{"id":"Tq50c9iybcXi","type":"EditableString","value":"Edit Me!"},
"demo_list":{"id":"ZmjclOeUbcXj","type":"List","value":
[{"json":"Cat"},{"json":"Dog"},{"json":"Sheep"},{"json":"Chicken"}]},
"demo_cursors":{"id":"6TJ6Zzd2bcXj","type":"Map","value":{}},
"demo_map":{"id":"ukRRMPHbbcXj","type":"Map","value":
{"key1":{"json":"value 1"},"key2":{"json":"value 2"},"key3":{"json":"value 3"}}},
"demo_custom":{"id":"44nsuMAPbcXk","type":"DemoMovie","value":
{"name":{"json":"Minority Report"},
"director":{"json":"Steven Spielberg"},
"notes":{"json":""},"rating":{"json":""}}},
"myTest":{"id":"Kq4hcV4UbcvW","type":"testType","value":{"json":"testName"}}}}}"
Registering custom types can only occur during the "Pre-Load" phase of the document life cycle. Your code is correct, but is being executed on the document after the document has loaded. This causes the custom object to not be properly constructed, which is why it is lacking the JSON value that you have specified in the init function.
To see the correct flow in action, put a break point on line 88 of static/elements/playground-app.js in the Realtime Playground application. Refresh the page and when execution has paused, run this code from the console:
test = function () {}
test.prototype = { init: function() { this.name = 'testName';}};
test.prototype.name = gapi.drive.realtime.custom.collaborativeField('name');
gapi.drive.realtime.custom.registerType(test, 'testType')
gapi.drive.realtime.custom.setInitializer(test, test.prototype.init);
Resume execution. When the application has fully loaded, run the rest of your code:
var model = window.doc.getModel()
model.getRoot().set('myTest', model.create(test));
model.toJson()
You will see that the outputted JSON matches what you are expecting:
"myTest":{"id":"1OiQd2QoEqBs","type":"testType","value":{"name":{"json":"testName"}}}
This workflow is documented under the "Registering custom types and fields" title of the Custom Collaborative Objects guide.

Calling Service's method multiple times holds single data instance

I am using services for my AngularJS project and trying to call a service.method using a for loop, like this :
for( key in URLs) {
Service.fetchXML(key);
}
Service description:
[..]
fetchXML : function(resource) {
var prop = SomeVar[resource]; //SomeVar is declared within the service
$.get('xmlURL.xml?resource='+prop, function(data) {
//adds data to the IndexedDB after properly parsing it.
console.log(resource)
dbAdd();
})
Problem is when I try resource inside fetchXML() method; its set permanently, means if the loop runs for five times, only one instance of fetchXML() is created and console.log(resource) returns the same for all five iterations.
Please tell me what am I doing wrong here.
for( key in URLs) {
Service.fetchXML();
}
Should be passing parameter to function since it is used as resource to create prop.
for( key in URLs) {
Service.fetchXML(key);
}
This should have been fairly easy to troubleshoot. First it would be apparent in the request url inspected in browser console/dev tools.
Also using some simple degugger or console.log() statements in function would have helped. Or setting breakpoint on the function and stepping through it to see variable values

Resources